사실 지난 미션까지만해도 @Controller
, @Service
, @Repository
를 제외하고는 직접 빈으로 뭔가를 등록해준 적이 없었다. 하지만 이번 미션을 하면서 내가 만든 클래스를 빈으로 등록해서 사용하면 좋을 것 같은 일이 생겼다.
FarePolicyImpl
FareService
마침 학습테스트에서 빈 등록 하는 방법에 대해 테스트해 볼 기회도 있어 찾아봤다.
이 부분이 헷갈렸다. 실제로 '빈으로 등록했다', '컴포넌트로 등록했다' 등으로 많이 듣고 썼기 때문이다.
우선 명확히 해야하는 것은 스프링 IoC 컨테이너에서 관리하는 빈과 애노테이션 @Bean
은 다르다.
스프링 공식 문서에서 빈의 정의는Srping IoC 컨테이너에 의해서 관리되고 어플리케이션에서 중요한 역할을 하는 객체
다.
@Component
를 사용하거나 @Bean
을 사용하면 BeanDefinition
의 인스턴스로서 Spring IoC 컨테이너에서 관리 대상이 되는 것이다.
그렇다면 @Bean
과 @Component
는 어떻게 다를까?
@Component 주석
클래스 레벨에 붙여서 auto-detection
의 대상이 된다.
@Bean 주석
메서드에 붙여서 해당 메서드가 Spring container가 관리할 빈을 생성하는 것을 나타낸다.
만약 내가 작성한 코드가 아닌 외부 라이브러리를 사용한다고 생각해보자. 외부 라이브러리의 클래스에 내가 직접 @Component
를 붙이지 못할 수도 있다. 이럴 때는 해당 클래스를 생성해서 반환하는 메서드에 @Bean
을 붙여서 빈으로 등록할 수 있다.
객체 관리 대상과 의존성 설정을 할 수 있는 세 가지 방법이 있다.
UserRepository
UserService
/resources/application-config.xml
UserRepository
가 있고 UserService
는 UserRepository
에 의존하고 있을 때 의존성 설정을 xml 파일에서 직접해주고 있다.
간단한 프로그램일 경우 각 클래스들의 의존성 설정을 이렇게 직접 해줄 수 있겠지만 프로젝트의 규모가 커지면 어려울 것 같다는 생각이 든다. 또한 직접 문자열로 관리를 하기 때문에 오타에도 취약할 것이다.
각 메서드가 빈으로 등록할 객체를 생성해서 등록해주고 있다. authenticationPrincipalArgumentResolver()
에서는 의존성 주입을 authService()
메서드를 이용해서 하고 있다.
참고로
@Bean
이@Configuration
클래스가 아닌@Component
나 심지어 일반 클래스에서도 사용될수는 있다. 이럴 경우lite Bean mode
라고 하며 위처럼 다른 @Bean 메서드를 참조할 수 없다.
여기서 clientDao()
메서드가 clientService1()
에서도 호출되고 clientService2()
에서도 호출되고 있다. clientDao()
메서드가 새로 객체를 생성해서 주니 각각 호출된 객체가 다르다고 생각할 수 있다. 하지만 스프링 컨테이너는 기본적으로 빈을 싱글톤으로 관리한다. 캐싱해서 만약 인스턴스가 존재하면 그걸 돌려준다.
공식 문서에 따르면 Java-based Configuration이 100% xml 방식을 대체하라고 있는 것은 아니라고 한다. xml에서 namespace 관리 같은 것은 여전히 유용한 방식이라고 한다.
AuthService
AuthenticationPrincipalArgumentResolver
어떤 것을 빈으로 등록할지 말지가 사실 아직 명확하지 않다. 페어와 이야기를 나눴을 때 장단점에 대해 이야기 해보았다.
빈으로 가능한 등록하지 말자 의견: 도메인의 의존관계를 너무 스프링이 관리하게 하는 것은 좋지 않은 것 같다. 프레임워크 없어도 도메인은 돌아가야하지 않을까? 또 불필요하게 많이 빈으로 등록되어 관리되면 메모리 관리에도 좋지 않을 것 같다.
빈으로 등록해도 괜찮다 의견: 스프링이 해주지 않는다면 결국 조립기의 역할을 해주는 클래스를 작성해주어야 하고 그것도 개발자의 비용이다. 애초에 프레임워크를 사용하는 큰 이유 중 하나가 객체의 생성과 조립을 맡기는 것이 아닌가?