Note:
- DI 이란 Dependcy Injection 의 약자로 직역하면 의존성 주입 이라고 한다.
- IOC 의 한 패턴으로 사용이된다.
- 각 Class 사이에 필요로 하는 의존관계가 있다면 이를 컨테이너가 자동적으로 연결시켜 주는 것으로 각 Class 사이의 의존관계를 Bean 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해 주는 것이다.
public class Store {
private Item item;
public Store(Item item) {
this.item = item;
}
}
설정할 의존성을 나타내는 인수를 각각 생성자를 호출한다.
스프링에서는 기본적으로 유형에 따라 각 인수를 해결하고 그 뒤에 속성 이름과 명확성을 색인이 온다.
@Configuration
public class ExConfiguration {
@Bean
public Item getItem() {
return new Item("사과 나무");
}
@Bean
public Store getStore() {
return new Store(getItem());
}
}
생성자 기반의 의존성 테스트를 위하여 아래와 같이 테스트 케이스를 작성하였다.
ExConfiguration class 를 Autowired 로 주입해주고 케이스를 만들었다.
@SpringBootTest
class CoreApplicationTests {
@Autowired
ExConfiguration exConfiguration;
@Test
@DisplayName("DI Example1")
void loadByExConfiguration() {
Store store = exConfiguration.getStore();
String itemTitle = store.getItem().getTitle();
Assertions.assertEquals(itemTitle, "사과나무");
return;
}
}
테스트 케이스가 성공했다. 이렇게 보면 우리가 주입 시킨 아이템이라는 객체에 사과나무라는 변수 title 이 정상적으로 같은 객체를 보고 있다고 할 수 있다.
Setter 기반 DI의 경우 컨테이너는 인수가 없는 생성자 또는 인수가 없는 정적 팩터리 메소드를 호출하여 빈을 인스턴스화한 후 클랙스의 setter 메소드를 호출하여서 사용한다.
@Bean
public Store getStore1() {
Store store = new Store();
store.setItem(getItem());
return store;
}
필드 기반의 종속성 주입의 경우 @Autowired
주석으로 종속성 주입이 가능하며
@RequiredArgsConstructor
요즘 들어서 이어노테이션을 많이 사용을 한다.
@RequiredArgsConstructor
의 경우 final
키워드를 붙여주어야 한다.
@Service
@RequiredArgsConstructor
public class UserService {
@Autowired
ExConfiguration exConfiguration;
private final UserRepository repository;
}
Bean 주입 방식 중 어노테이션 주입 방식으로 spring 에서 configration class 를 만들어서 테스트를 해보려고 한다.
Bean 이 싱글톤 생성이 맞는지 그리고 싱글톤 생성이 맞다면 진짜로 맞는지 체크를 하기위해 예제 코드를 만들어 보았다.
예제를 이해하기 위해서는 application context 라는 것이 무엇인지 알아야 한다.
Application Context:
- Bean Factory 를 주입 받은 spring 에서 제공하는 bean factory라고 할 수 있다.
- 별도의 설정 정보를 참고 하여 IOC 를 적용하고 빈의 생성, 관계설정 등의 제어 작업을 총괄한다.
@Configuration
public class CoreConfiguration {
@Bean
public MemberVO memberVO() {
MemberVO memberVO = new MemberVO();
memberVO.setName("릴보이");
memberVO.setNickName("작은거인");
return memberVO;
}
@Bean(name = "hello")
public MemberVO memberVO1() {
return new MemberVO("페노메코", "페노메코1");
}
@Bean
public PrintA printA() {
return new PrintA();
}
@Bean
public PrintB printB() {
return new PrintB();
}
}
configuration class 에서 memberVO 라는 Bean 그리고 hello라는 bean 과 함께 print 인터 페이스에서 적용하기 위한 printA, printB를 주입 해주었다.
이렇게 주입된 bean들은 application context에서 가지고 올 수 있으며 또한 @Autowired
어노테이션을 사용하여서 사용이 가능하다. (이론적으로)
// print interface
public interface Print {
String sout();
}
// print을 상속한 printA class
public class PrintA implements Print {
@Override
public String sout() {
return "I am Print A";
}
}
// print을 상속한 printB class
public class PrintB implements Print {
@Override
public String sout() {
return "I am Print B";
}
}
// 테스트를 위한 MeberVO
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MemberVO {
@Setter
private String name;
@Setter
private String nickName;
@Autowired
@Qualifier("printA")
private Print print;
public String getPrint() {
return print.sout();
}
public MemberVO(String name, String nickName) {
this.name = name;
this.nickName = nickName;
}
}
위의 코드처럼 데이터 class를 만들 었다.
MeberVO에서 @Qualifier
어노테이션을 사용을 하면 빈을 강제로 주입이 가능하다
이렇게 데이터 class 를 구성을 하였다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/start")
public class StartResource {
private final ApplicationContext context;
private final MemberVO hello;
private final MemberVO memberVO;
@GetMapping("")
public String showByWebStart() {
return "SUCCESS";
}
@GetMapping("/test1")
public MemberVO showByMember() {
return context.getBean("memberVO", MemberVO.class);
}
@GetMapping("/test2")
public MemberVO showByPrint() {
return hello;
}
@GetMapping("/test3")
public boolean checkByBean() {
MemberVO beanMemberVO = context.getBean("memberVO", MemberVO.class);
return beanMemberVO.equals(memberVO);
}
}
컨트롤러의 경우 위처럼 작성을 했는데 필드에서 @Autowired
를 사용을 안하는 이유는 스프링 부트와 AWS로 혼자 구현하는 웹 서비스
라는 책에서 @RequiredArgsConstructor
를 선호해서 사용하라는 말을해서 습관처럼 사용을 한다.
필드에서 ApplicationContext
를 선언을하면 Bean을 가지고 올 수 있다.
그리고 Bean Name 으로 각각의 MemberVO 을 선언 해준다. java configuartion naming을 선언이 가능하는데 Bean 어노테이션에서 설정을 안해주면 메소드 명으로 bean naming으로 가지고 온다.
비교하는 api를 통해서 보았을 때 같은 객체이고 싱글톤 패턴을 사용을 한다는 것을 알 수 있다.
객체를 디버깅 하여서 보았는데 객체의 참조 주소가 같아서 같은 객체이다 라고 할 수 있다.