열심히 끝까지

디바이스 융합 자바(Java) day91(2) - SpringBoot 의존 주입 본문

디바이스 융합 자바(Java)기반 풀스택 개발자 양성과정(수업내용)

디바이스 융합 자바(Java) day91(2) - SpringBoot 의존 주입

노유림 2022. 10. 24. 15:37

[의존 주입]
> 어떤 객체 A가 뭔가 메서드를 수행할 때, 
    b.hello() c.hello() 만들어야 함
    
  1) new를 직접 수행해서 바로 사용(== 개발자가 신경써야 함)
       >> "강한 결합"이 되어 있다 라고 표현
  2) 미리 만들어진 객체를 할당받아서 사용 가능 => DI
      : 객체를 생성해주고 라이프사이클(scope)를 관리하고
        필요로 하는 곳에 의존을 주입해주는 친구를 == 컨테이너
             : IoC(Inversion Of Container)
      : IoC를 제공하는 것이 Spring 
       >> "약한 결합"이 되어 있다 라고 표현

>>> 유지보수가 용이하려면?
     == 낮은 결합도, 높은 응집도
  

>> 예시

package test;

class Member{
	String name;
	public Member() { // 코드 변화를 주자 문제가 발생하는 코드가 있고 아닌 코드 존재
		// private로 바꾸면 강한 결합에서 코드 존재
		
	}
}

public class Test {
	
	public static void createMember1() {
		// 강한 결합
		Member member1 = new Member();
	}
	public static void createMember2(Member member) {
		// 약한 결합
		Member member2 = member;
	}
	
	public static void main(String[] args) {
		
	}	
}


>> class Member에 public을 private로 바꾸면
      강한 결합인 createMember1에서 에러 발생

[의존 주입 방법]
1) .xml << boot에서는 사용 잘 안함
2) 자바 코드 작성
3) @
    > 2, 3 사용해볼 것


>>> 자바 코드 작성
>> application 역할을 해줄 친구 생성

@Configuration 작성해줄 것!!

package com.ryo.springboot.test;

import org.springframework.context.annotation.Configuration;

@Configuration // 스프링 컨테이너의 설정 파일 역할
public class Config {
	// 설정 파일
	
}



>> 이 페이지 상황에 @Autowired가 작성되면 빨간줄 날 가능성 존재
    >> 그러니 미리 이름 연결해 놓으면 좋겠다...
    : 이름 지어놓으면 커스터마이징 가능

package com.ryo.springboot.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 스프링 컨테이너의 설정 파일 역할
public class Config {
	// 설정 파일
	
	@Bean // 객체화 공식(공식 용어가 아님.. 강사님이 제작)
	public Leader leaderA() {
		// Setter 주입
		Leader leader1 = new Leader();
		leader1.setName("티모");
		leader1.setMember(new MemberA());
		return leader1;
	}
	
	@Bean(name="ryo") // 이름 생성 가능
	public Leader leaderB() {
		return new Leader("아리", new MemberA());
	}
}




전에 boot 프로젝트 만든 채로 새로 만들면
> 최근(기존의 것) 설정 및 최근 추가한 Dependencies 도 띄움

예시

만든 패키지가 아닌 기존패키지에 TestApplication.java가 존재
>> 이대로 한번 돌려보기

package com.ryo.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication
public class TestApplication {

	public static void main(String[] args) {
		//SpringApplication.run(TestApplication.class, args);
	}

}


-----------------

package com.ryo.springboot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.ryo.springboot.test.Config;
import com.ryo.springboot.test.Leader;
import com.ryo.springboot.test.Member;

// import org.springframework.boot.SpringApplication;
// import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication
public class TestApplication {

	public static void main(String[] args) {
		//SpringApplication.run(TestApplication.class, args);
		
		// 1. IoC 제공해 줄 스프링 컨테이너 생성하기
		ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
		// Config가 대신하고 있기 때문에 Config.class 보라고 지칭
		// 동작 확인한 후 싱글톤으로 객체 생성 및 관리됨
		// config.class의 객체를 객체화 진행
		
		// 2. @Bean 등록한 객체 사용해보기
		Leader leader1 = (Leader)ac.getBean("leaderA"); // 형변환 가능
		leader1.hello();
		// leader1 생성 -> config에 있는 bean이 leaderA()인 것을 ac에 넣어 leader1에 저장
		// leader의 생성자 hello 가져오기
		
		Leader leader2 = (Leader)ac.getBean("ryo");
		leader2.hello();
		// 
		
		Member member = ac.getBean("memberB", Member.class); // output 타입 지정해서 사용 가능
		member.hello("main에서 작성한 안녕하세요");
		
		// 3. 결과 확인
		if(leader1 == leader2) {
			System.out.println("동일한 객체입니다.");
		}else {
			System.out.println("동일하지 않은 객체입니다.");
		}
	}

}

자바 코드 작성 전체 코드-------

Leader.java----------------------

package com.ryo.springboot.test;

public class Leader { 
	// 외부에서 다이렉트 접근을 막기 위해 private 작성
	private String name;
	private Member member;
	
	public Leader() {
		
	}
	public Leader(String name, Member member) {
		this.name = name;
		this.member = member;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Member getMember() {
		return member;
	}
	public void setMember(Member member) {
		this.member = member;
	}
	public void hello() {
		member.hello(name + ": 안녕하세요!");
	}
}

Member.java--------------------

package com.ryo.springboot.test;

public interface Member {
	void hello(String msg); // public 앱스트랙 생략
}

MemberA.java------------------

package com.ryo.springboot.test;

public class MemberA implements Member{

	@Override
	public void hello(String msg) {
		System.out.println("A멤버 : " + msg);
		
	}
	
}

MemberB.java-----------------

package com.ryo.springboot.test;

public class MemberB implements Member{

	@Override
	public void hello(String msg) {
		System.out.println("B멤버 : " + msg);
		
	}

}

Config.java----------------------

package com.ryo.springboot.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 스프링 컨테이너의 설정 파일 역할
public class Config {
	// 설정 파일
	
	@Bean // 객체화 공식(공식 용어가 아님.. 강사님이 제작)
	public Leader leaderA() {
		// Setter 주입
		Leader leader1 = new Leader();
		leader1.setName("티모");
		leader1.setMember(new MemberA());
		return leader1;
	}
	
	@Bean(name="ryo")
	public Leader leaderB() {
		return new Leader("아리", new MemberA());
	}
	
	@Bean
	public MemberA memberA() {
		return new MemberA();
	}
	
	@Bean
	public MemberB memberB() {
		return new MemberB();
	}
}

TestApplication-----------------

package com.ryo.springboot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.ryo.springboot.test.Config;
import com.ryo.springboot.test.Leader;
import com.ryo.springboot.test.Member;

// import org.springframework.boot.SpringApplication;
// import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication
public class TestApplication {

	public static void main(String[] args) {
		//SpringApplication.run(TestApplication.class, args);
		
		// 1. IoC 제공해 줄 스프링 컨테이너 생성하기
		ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
		// Config가 대신하고 있기 때문에 Config.class 보라고 지칭
		// 동작 확인한 후 싱글톤으로 객체 생성 및 관리됨
		// config.class의 객체를 객체화 진행
		
		// 2. @Bean 등록한 객체 사용해보기
		Leader leader1 = (Leader)ac.getBean("leaderA"); // 형변환 가능
		leader1.hello();
		// leader1 생성 -> config에 있는 bean이 leaderA()인 것을 ac에 넣어 leader1에 저장
		// leader의 생성자 hello 가져오기
		
		Leader leader2 = (Leader)ac.getBean("ryo");
		leader2.hello();
		// 
		
		Member member = ac.getBean("memberB", Member.class); // output 타입 지정해서 사용 가능
		member.hello("main에서 작성한 안녕하세요");
		
		// 3. 결과 확인
		if(leader1 == leader2) {
			System.out.println("동일한 객체입니다.");
		}else {
			System.out.println("동일하지 않은 객체입니다.");
		}
	}

}


---------------------------------------------
>>> @
   : 위 프로젝트에서 Config 삭제 한 후
     @Component와 @Value, @Autowired, @Qualifier 추가
@SpringBootApplication
      : Bean(객체)들을 생성할 때, 싱글톤을 유지할 수 있도록 설정 및 관리
      : 각각의 객체들이 생성되면 용이한 순간을 추측하여 메모리를 관리
        . jar 파일들도 생성 및 설정 관리
      : @ 지정된 클래스를 스캔해서 Beam 등록 관리
        >> 모든 클래스 파일에 @을 검색해서 Bean 검색

 

Test02Application.class 에서 SpringBootApplication 실행
1. main 메서드 실행
2. @SpringBootApplication의 영향으로 @ 스캔 및 등록
3. 10번 라인에 의해 내장 톰캣 실행
4. ApplicationContext 생성 == 스프링 컨테이너 실행
       -> URL로 요청 시 RequestMapping에 의해 작성한 메서드가 호출되는 방식

 

@(어노테이션) 전체코드-----------

Leader.java----------------------

package com.ryo.springboot.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component // Leader 객체화
public class Leader { 
	// 외부에서 다이렉트 접근을 막기 위해 private 작성
	@Value("티모") // setter의 역할을 수행
	private String name;
	
	@Autowired
	@Qualifier("memberA") //이름으로 볼 수 있도록 설정
	private Member member;
	
	public Leader() {
		
	}
	public Leader(String name, Member member) {
		this.name = name;
		this.member = member;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Member getMember() {
		return member;
	}
	public void setMember(Member member) {
		this.member = member;
	}
	public void hello() {
		member.hello(name + ": 안녕하세요!");
	}
}

 

MemberA.java-------------------

package com.ryo.springboot.test;

import org.springframework.stereotype.Component;

@Component("memberA")
public class MemberA implements Member{

	@Override
	public void hello(String msg) {
		System.out.println("A멤버 : " + msg);
		
	}
	
}

MemberB.java-------------------

package com.ryo.springboot.test;

import org.springframework.stereotype.Component;

@Component("memberB")
public class MemberB implements Member{

	@Override
	public void hello(String msg) {
		System.out.println("B멤버 : " + msg);
		
	}

}

TestController.java--------------

package com.ryo.springboot.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
	@Autowired
	Leader leader1;
	@Autowired
	Leader leader2;
	@Autowired
	@Qualifier("memberB")
	Member member;
	
	@RequestMapping("/")
	public @ResponseBody String root() { // 이 글자가 @ResponseBody를 통해 화면에 출력
		leader1.hello();
		leader1.setMember(member);
		leader1.hello();
		
		if(leader1 == leader2) {
			System.out.println("동일한 객체입니다.");
		}else {
			System.out.println("동일하지 않은 객체입니다.");
		}
		
		return "루트(표지) 페이지";
	}
}

Test02Application.java---------

package com.ryo.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Test02Application {

	public static void main(String[] args) {
		SpringApplication.run(Test02Application.class, args); // 내장된 Tomcat 실행
		// Test02Application.class 에서 SpringBootApplication 실행
		// 1. main 메서드 실행
		// 2. @SpringBootApplication의 영향으로 @ 스캔 및 등록
		// 3. 10번 라인에 의해 내장 톰캣 실행
		// 4. ApplicationContext 생성 == 스프링 컨테이너 실행
		//        -> URL로 요청 시 RequestMapping에 의해 작성한 메서드가 호출되는 방식
	}

}

 


----------------------------------------------
[Tip 구간]

Tip1   @Override는..?
             Override는 컴파일러에게 정보를 제공
            @Bean에서 이름 비교 시 필요한 것
             Autowired는 
             Anotation는  
             Qualifier는 
      > @Override를 작성하지 않으면 상위 클래스 바로 접근은 힘들겠지만
          그럼에도 오버라이드 된 상태
          : 개발 용이성을 표현해주는 것(오버라이드 된 것이라는 언급이 없으면 유지보수 힘듬)
      > 회사에서는 @도 작성해서 쓸 가능성 존재

Tip2   ApplicationContext ac = new AnnotaionConfigApplicationContext(?);
                    => 디자인 패턴 중 하나의 예시