스프링은 컨테이너에 Bean을 등록한다. 과거에는 xml에 bean을 작성해서 등록했는데 이젠 어노테이션으로 편리하게 등록할 수 있다.
스프링 MVC 패턴에서는 @Controller
@Service
@Repository
으로 손쉽게 스프링 컨테이너에 빈으로 저장할 수 있고, configuration 관련 객체들은 @Bean
@Component
로 저장할 수 있다.
오늘은 @Bean
과 @Component
이 두개를 알아보려 한당
@Component
는 싱글톤 클래스 빈을 생성하는 어노테이션이다. 근데 @Scope("Prototype")
어노테이션을 붙여주면 싱글톤이 아닌 빈도 생성됨.
@Service
, @Repository
같은 어노테이션이 여기에 포함된다.
@Component
어노테이션은 선언적(Declarative)인 어노테이션이다.
그냥 쉽게 말해서 "야야 여기 클래스 빈으로 등록해줘~~" 라는 뜻임.
스프링은 스캔할 패키지를 검색해서 @Component
어노테이션을 발견하면 등록한다.
@Bean
은 주로 @Configuration
어노테이션이 들어간 클래스 내의 메소드에 선언한다.
그냥 싹다 @Component
로 통일하면 안되나 싶어서 찾아본 이놈의 역할은 이 @Bean
을 통해서 유연하게 빈 등록이 가능하다는 것이다.
예전 스프링은 XML설정만 가능했다.(물논 지금도 XML설정을 지원하긴함)
이 XML 설정의 장점은 빈을 넣었다 뺐다 하면서 유연한 설정이 가능하다는 것이다. 필요없는 빈은 주석처리해버리고 앱 재시작하면 됨.
이렇게 스캔할 필요없이 바로 빈 등록이 가능한 부분이 @Bean
으로도 가능하다.
// prop 정의 안하면 빈 문자열로. null 받으면 switch 문에서 NullPointerException 발생함
@Value("${my.prop.animal:}")
private String animal;
@Bean
public Animal animal() {
switch(animal) {
case "dog": return new Dog();
case "cat": return new Cat();
case "cow": return new Cow();
default: throw new InvalidStateException("동물 정의하고 다시 실행할 것!");
}
}
이런식으로 프로퍼티 값을 읽어서 구현 클래스를 인터페이스 기반 빈에 등록해주는 유연한 처리가 가능하다.
또한, @Component
어노테이션 넣기 곤란한 타사 라이브러리 클래스를 빈에 등록할 때도 @Bean
어노테이션을 통한 메소드를 통해 등록할 수 있다.
이 둘의 첫번째 차이는 선언위치의 차이다.
@Component
는 class level에 선언되고 @Bean
은 method 레벨에 선언된다.
패스워드 암호화를 위해서 Bcrypt를 사용하는 모듈을 Component로 제공한다고 하면 아래와 같이 class level에 @Component
를 선언할 수 있다.
@Component
public class PasswordEncoder {
public String encode(String seed) {
return new BCryptPasswordEncoder().encode(seed);
}
public boolean matches(String seed, String password) {
return new BCryptPasswordEncoder().matches(seed, password);
}
}
@Component
위치에 @Bean
을 사용한다면 IDE가 바로 지적해줌
@Bean
은 메소드 레벨에 선언한다.
아래는 스프링 시큐리티를 사용했다면 익숙한 코드일 것이다.
Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Bean
을 붙이면 클래스 위에는 @Configuration
을 붙여줘야한다.
아래와 같이 @Configuration
없이 @Bean
만 사용했다면,
public class MyBean {
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
이 Bean을 다른 곳에서 의존성 주입하는데 활용할 수 없다.
@Configuration
을 사용하면 정상적으로 IoC 컨테이너에 등록되서 의존성 주입을 할 수 있다.
사실 Configuration
내부에 Component
어노테이션이 있다.
내부에 @Component가 붙어 있기 때문에, 메소드 레벨에 존재하는 @Bean
도 spring이 인식하여 설정 정보 자체도 스프링 빈으로 자동으로 등록된다.
@Bean
은 외부 라이브러리가 제공하는 객체를 사용할 때 활용하고, @Component
는 내부에서 직접 접근 가능한 클래스에 사용하면 된다.
@Component
public class PasswordEncoder {
public String encode(String seed) {
return new BCryptPasswordEncoder().encode(seed);
}
public boolean matches(String seed, String password) {
return new BCryptPasswordEncoder().matches(seed, password);
}
}
여기서 @Component
를 쓸 수 있는건, 이 PasswordEncoder 클래스를 내가 직접 만들었기 때문이다. 그래서 개발자가 직접 컨트롤이 가능한 클래스들의 경우에는 @Component
를 사용하면 된다. 이 코드가 라이브러리에 있는 코드였다면 @Component
를 쓸 수 없다.
라이브러리에 있는 객체는 @Bean
을 써서 활용할 수 있다.
@Bean
어노테이션은 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 직접 등록하고 싶은 경우에 사용 된다.
@Bean
과 @Component
의 차이는 선언 위치, 그리고 사용법의 차이에 있다.
@Component
는 자동으로 스프링 컨테이너에 스프링 빈을 등록하고, @Bean
은 수동으로 스프링 컨테이너에 등록할 스프링 빈을 직접 등록한다.
@Component
는 class level에 선언되고 @Bean
은 method 레벨에 선언된다.
@Bean
은 외부 라이브러리가 제공하는 객체를 사용할 때 활용하고, @Component
는 내부에서 직접 접근 가능한 클래스에 사용하면 된다.
일반적인 빈 등록은 간편하게 @Component
어노테이션으로, 유연한 빈 등록이 필요하다면 @Configuration
어노테이션이 들어간 클래스 내 @Bean
어노테이션 메소드 선언으로 사용한다.