스프링 기본편을 내가 글을 적으면서, 어? 근데 이거 왜쓰지 싶은 것이 있었다.
이렇게 왜?라는 질문을 던지니까? 그냥 단순히 이렇게 쓰는가보다 하고 넘어가는걸, 글을 쓰면서 다시 확인해보니까 좋은거 같다.
각설하고,우선, Component,Configuration 이거 왜쓰는걸까?
정확히 설명하기 힘들 수 도 있다.
싱글톤으로 객체를 생성해서 스프링 컨테이너에 등록하고, 또 유연성과 확장성을 높이고, 또 의존성을 관리하기 위해서 등등등... 너무 여러가지 가 있지만, 다 맞는말이긴한데
결국 나의 생각은, 싱글톤이 제일 중요한것 같다.
하나를 만들어서 공유하는것, 이것이 제일 중요한것 같다.
여기 멤버 서비스를 보자, 여기서 DI가 사용되지 않았는데, 유지보수, 유연성과 확장성, 의존성 이런거 다 제처 두고, 이렇게 하면 제일 문제가 뭘까?
나는 이렇게 생각한다.
멤버서비스 뿐만 아니라 OrderService를 새로만들고, 여기서 다시 new memoryMemberRepository를 한다 치자.
그러면 멤버서비스의 로직과 orderService의 로직이 정상적으로 돌아 갈까?
=>정답은 아니다.
왜냐하면 멤버 서비스에서 memoryMemberRepository에다가 고동현을 넣고, OderService에서 고동현을 찾으려고 보면, 죽었다 꺠어나도 찾을수 없다.
왜? =>두 서비스의 저장소가 서로 다르니까.
하나의 저장소를 공유해야하는데 서로 다르게 된다.
이래서 스프링 입문편과 핵심편의 글에서 내가 DI와 DI컨테이너에 대해서 쭉 설명을 한것 이다.
그런데 내가 궁금한게,
Component 스캔을 하면 Spring Bean에 등록이되는것도 알겠고, Configuration을 하면 Spring Bean에 등록되는것도 알겠는데, 어? 그러면 이거 두개 왜 쓰는거지? 이런 궁금증이 들었다.
Configuration에노테이션 안에 Component에노테이션이 있다. Service,Repository 에노테이션도 마찬가지다.
여기서 궁금증 첫번쨰가? 어? Bean에노테이션이 등록하는건데 이거 Configuration도 굳이 에노테이션도 붙여야하나? 라는 궁금증이었다.
정답은 약간 문제가 있다. 이다.=> Configuration없이도 스프링 bean에 등록이 되는건 사실이다.
그러나, 문제가있는데. 우리가 메소드 호출을 통해서 객체를 생성하지 않았는가?
MemoryMemberRepository라던지 근데 이게 객체를 생성할때 Configuration을 안붙이면 싱글톤을 보장하지 않는다.
그러므로, MemoryMemberRepository 하나의 저장소로 Service던 다른데서 이용해야 김철수가 들어가있는 저장소를 사용하는데,
이러면 다른곳에서는 김철수가 없는 새로운 MemoryMember Repository를 사용하는 경우가 있다는말이다.
고로 싱글톤을 보장하기 위해서 Configuration을 붙이는게 맞다.
두번째, 궁금증은 그러면 Component랑 Configuration이랑 뭐가 다르냐? 어차피 컨테이너에 등록하는건 똑같은데,
미묘하지만 신기한 다른점이 있다.
@Configuration
public static class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
@Component
public static class Compo {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
자 여기서 Configuration과 Component에노테이션은 둘다 클래스를 빈에 등록시키고, 이 내부에 Bean에노테이션이 붙은 메서드의 반환된 객체를 key value 로 컨텍스트에 등록한다.
그런데 신기하게 이게 둘다 서로 결과가 다르다.
Configuration에서는 두번째 메서드에서 생성자의 인자로 simpleBean()을 호출하는데 여기서는 스프링 컨텍스트에 등록되어있는 SimpleBean을 반환한다.
그러나 Component에서는 마치 순수 메서드를 호출한거 마냥, 스프링 컨텍스트에서 만들어 놓은 SimpleBean을 반환하는게 아니고 새로 생성된 빈이 반환되는것이다.
그래서 만약 새로 생성되는게 아니고 Component스캔사용시 스프링 컨텍스트에서 만들어 놓은 것을 가져오려면 Autowired를 사용하여야한다.
@Component
public static class Config {
@Autowired
SimpleBean simpleBean;
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean);
}
}
이차이가 바로 CGLIB=>Code Generation LIBrary가 두 애노테이션마다 다르게 동작학 떄문인데, Bean메서드는 우선적으로 무조건 한번 최초로 호출되어서 생성된 객체가 스프링 컨텍스트에 저장이된다.
Configuration애노테이션이 사용된 클래스 내부에서는 Bean메소드의 내부에서 호출한 메소드가 Bean메소드인경우 컨텍스트에 등록된 빈을 반환하도록 되어있다.
그러나 Component애노테이션의 경우는 컨텍스트에 등록된 빈을 반환하는것이아닌 실제 메서드를 호출하는 형식으로 되어있다.
그래서 수정된 Component를 보면 simpBean객체가 먼저 Bean애노테이션이 달려있어서 스프링 컨텍스트에 등록이 되고 Autowired를 사용하여서 injection해준다음 이 simpleBean을 그냥 인자에 넣어주는 형태인 것이다.