@Configuration
& @Bean
& @Component
& @IoC Container
@Configuration
: 스프링 IOC Container
에게 해당 클래스를 Bean
구성 Class
임을 알려주는 어노태이션.@IoC Container
: 스프링 애플리케이션에서는 오브젝트의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다. 이를 컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있다고 해서 IoC라고 부른다. 그래서 스프링 컨테이너를 IoC 컨테이너라고도 한다. - 토비의 스프링3.0 767p@Component
& @Bean
: @Bean
어노테이션과 @Component
어노테이션 둘다 Spring(IOC) Container
에 Bean
을 등록하도록 하는 메타데이터(실제 데이터가 아닌 데이터를 위한 데이터)를 기입하는 어노테이션이다. 그렇다면 왜 두개나 만들어 놓았을까? 정답은 둘의 용도가 다르기 때문이다.Bean | Component |
---|---|
메소드에 사용 | 클래스에 사용 |
개발자가 컨트롤이 불가능한외부 라이브러리 사용시 사용 | 개발자가 직접 컨트롤이 가능한내부 클래스에 사용 |
@Bean
: 메소드 레벨에서 선언하며, 반환되는 객체(인스턴스)를 개발자가 수동으로 빈으로 등록하는 애노테이션. 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean
으로 등록하고 싶은 경우에 사용된다.
우리가 new
연산자로 어떤 객체를 생성했을 때 그 객체는 Bean
아니다.ApplicationContext.getBean()
으로 얻어질 수 있는 객체는 Bean
이다.
@Configuration
public class ApplicationConfig {
@Bean
public ArrayList<String> array(){
return new ArrayList<String>();
}
}
위는 @Bean
어노테이션을 이용하여 Bean을 생성한 예제이다. 위와 같이 ArrayList
같은 라이브러리등을 Bean으로 등록하기 위해서는 별도로 해당 라이브러리 객체를 반환하는 Method
를 만들고 @Bean
어노테이션을 붙혀주면 된다. 위의 경우 Bean어노테이션에 아무런 값을 지정하지 않았으므로 Method 이름을 CamelCase
로 변경한 것이 Bean id
로 등록된다. (ex. 메소드 이름이 arrayList()인 경우 arrayList
가 Bean id)
@Configuration
public class ApplicationConfig {
@Bean
public ArrayList<Integer> array(){
return new ArrayList<Integer>();
}
@Bean
public Student student() {
return new Student(array());
}
}
의존관계가 필요할 때에는 어떻게 해결할 수 있을까? Student
객체의 경우 생성자에서 ArrayList를 주입 받도록 코드를 짜놓았다 이럴때에는 Bean으로 선언된 array()메소드를 호출함으로써 의존성을 주입할 수 있다.
@Bean
을 사용하는가? 메소드가 아닌 그냥 객체를 만들면 되지 않는가?결론적으로 말하면, 하나의 오브젝트만을 만들어 서버의 과부하를 막기 위함이다.
스프링의 싱글톤 레지스트리 덕분에 싱글톤 방식으로 사용될 애플리케이션 클래스라도 public 생성자를 가질 수 있다. 싱글톤으로 사용돼야 하는 환경이 아니라면 간단히 오브젝트를 생성해서 사용할 수 있다. 따라서 테스트 환경에서 자유롭게 오브젝트를 만들 수 있고, 테스트를 위한 목 오브젝트로 대체하는 것도 간단하다.
가장 중요한 것은 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴(싱글톤패턴은제외) 등을 적용하는 데 아무런 제약이 없다는 점이다. 스프링은 IoC 컨테이너일 뿐만 아니라, 고전적인 싱글톤 패턴을 대신해서 싱글톤을 만들고 관리해주는 싱글톤 레지스트리라는 점을 기억해두자. 스프링이 빈을 싱글톤으로 만드는 것은 결국 오브젝트의 생성 방법을 제어하는 IoC 컨테이너로서의 역할이다.
-토비의 스트링3.0 107~110p
@Component
: 클래스 레벨에서 선언함으로써 스프링이 런타임시에 컴포넌트스캔을 하여 자동으로 빈을 찾고 등록하는 애노테이션이다. 개발자가 직접 컨트롤이 가능한 Class들의 경우엔 @Component를 사용한다.
@Component
public class Utility {
// ...
}
https://galid1.tistory.com/494
https://youngjinmo.github.io/2021/06/bean-component/
Spring Framwork의 IoC
에서 관리하고 있는 Bean
들 중에서 필요한 것을 객체에 주입하는 것
Bean에
의존성 주입(Recommended)@Service
public class OrderService {
ProductRepository productRepository;
@Autowired
public OrderService(ProductRepository productRepository) {
// 생성자가 하나뿐이면 @Autowired 생략 가능
this.productRepository = productRepository;
}
}
Spring Framework Reference에서 권장하는 방법은 생성자를 통해 Bean
의 의존성을 주입하는 방법이다. 왜냐하면 필수적으로 사용해야하는 의존성 없이는 인스턴스를 만들지 못하도록 강제할 수 있기 때문이다.
💡 생성자 방식에서 생성자가 단 하나뿐이면 `@Autowired`가 생략 가능하다고 했는데, 이를 [lombok](https://projectlombok.org/)과 함께 사용해서 편하게 주입하는 방법도 있다. lombok에서 `@RequiredArgsConstructor`Annotation을 붙이면 필수 member(final로 선언된)를 받는 생성자가 자동으로 만들어지고, 이 클래스가 Component scan의 대상인 클래스라면 단 하나의 생성자만 있기 때문에 `@Autowired`가 생략 가능해진다.
Bean
에 의존성 주입(NOT recommended)@Service
public class OrderService {
ProductRepository productRepository;
@Autowired
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}
단점 : set method
를 public
으로 열어둬야 하니 변경 가능한 위험Bean
에 의존성 주입(NOT recommended)@Service
public class OrderService {
@Autowired
ProductRepository productRepository;
}
Spring framework 없이는 productRepository
가 초기화 되지 않는 문제점이 있다. 이 때문에 Test
프레임워크 등에서는 NullpointException
뜨는 등의 문제가 발생한다. 깔끔해 보이지만 IntelliJ도 워닝을 띄워준다.@Service
public class OrderService {
@Autowired
ProductRepository productRepository;
}