스프링 빈의 등록
자동 등록 - @Component
빈의 자동등록은 컴포넌트 스캔을 거쳐서 이루어진다.
클래스 상단에 @Component 어노테이션
이 있으면 컴포넌트 스캔을 거쳐서 스프링 빈으로 자동등록 된다.
이 밖에도 @Component를 포함하는 @Controller, @Service, @Repository
도 자동등록된다. 생성자 위에 @Autowired 어노테이션을 붙여서 의존관계 주입이 가능하고, 해당 클래스의 생성자가 하나 뿐이라면 @Autowired는 생략 가능하다.
예를 들어 @RestController는 @RequestMapping에 @Responsebody를 추가한 기능을 한다. 그럼에도 @RequestMapping + @Responsebody 와 @RestController 사이에는 차이가 존재한다. 바로 빈의 자동등록이 되는가와 되지 않는가의 차이이다.
@Controller, @Service, @Repository 등의 어노테이션을 붙여서 컨트롤러, 서비스, 리포지토리 등을 자동등록하는 이유는 싱글톤 패턴
에 있다.
빈으로 등록하면, 싱글톤으로 관리가 되어서 객체가 여러번 생성되는 것을 막아서 메모리 공간의 낭비를 줄일 수 있기 때문이다.
빈의 자동등록에서는 빈 이름을 해당 클래스의 첫글자만 소문자로 바꾼 것으로 한다.
ex) JellyFish -> jellyFish
빈의 중복된 자동 등록을 하게 되는 경우 ConflictingBeanDefinitionException
예외가 발생한다.
수동 등록 - @Bean
어떤 인터페이스의 구현체를 지정하는 등의 이유로 수동 등록을 사용할 수 있다. 수동 등록은 수동 등록 메서드를 통해서 이루어진다. 또한 수동 등록을 위한 Configuration 클래스를 따로 생성할 수도 있다.
@Bean
수동 등록을 하는 빈들은 @Component 대신 @Bean
을 붙인다.
수동 등록 시에는 메서드 명으로 빈 이름을 지정하고, 빈 이름은 겹치지 않도록 만들어야 한다. 자동 등록된 빈에 대하여 수동 등록을 하게 되는 경우, 예외는 발생하지 않는다. 다만 수동 등록된 빈이 우선권을 가지게 된다.
public class FooBarConfig {
@Bean
public FooBar fooBar(){
return new FooBarBaz();
}
}
// 빈 이름은 메서드 명을 따라서 fooBar가 됨
@Bean은 보통 단독으로만 사용되지 않고, @Configuration
과 함께 사용된다.
공식 문서에서는 @Bean을 단독으로 사용하는 경우를 lite mode
라고 부르고, @Configuration이 붙은 클래스 내에서 @Bean을 사용하는 경우를 full mode
라고 부른다.
@Configuration
클래스 상단에 @Configuration 어노테이션을 붙여서 Configuration 파일로 지정할 수 있다. @Configuration 어노테이션에는 @Component 어노테이션이 포함되어 컴포넌트 스캔의 대상이 된다.
@Configuration
public class FooBarConfig {
@Bean
public FooBar fooBar(){
return new FooBarBaz();
}
}
// FooBar is an interface
// FooBarBaz implements FooBar
왜 @Configuration을 사용하는가? Lite mode vs. Full mode
앞에서 @Bean만 사용하는 경우를 lite mode, @Configuration을 함께 사용하는 경우를 full mode라고 한다고 했다. 그런데 왜 @Configuration을 사용하는 것이 권장될까?
lite mode에서는 빈 간의 종속성 선언을 할 수 없다.
예를 들어 서비스와 리포지토리를 각각 빈으로 등록할 때, 서비스가 리포지토리에 종속되어 있는 경우를 종속성이라고 할 수 있다.
서비스 빈 메서드에서는 매개변수로 리포지토리를 필요로 하기 때문에 리포지토리 빈 메서드를 실행하게 된다.
public class MyConfig {
@Bean
public MyController myController() {
return new MyControllerImpl(myService());
}
@Bean
public MyService myService() {
return new MyServiceImpl(myRepository());
}
@Bean
public MyRepository myRepository() {
return new MyRepositoryImpl();
}
}
// controller -> service -> repository 종속
// 빈 간의 종속성 선언을 위해서 매개변수로 다른 빈 메서드를 호출하고 있다.
위와 같은 상황에서 full mode는 CGLIB를 통한 프록시 객체
를 생성한다. 그리고 캐싱을 통해서 이미 생성된 빈이 있는지 확인하는 과정을 거친다.
그런데 lite mode는 다른 빈의 메서드를 호출하게 될 때 프록시를 사용하지 않고 표준 자바 호출
을 사용하게 된다. 즉, 싱글톤을 보장하지 않는 것이다.
그러므로 스프링 컨테이너를 통한 자원 관리를 위해서 full mode를 사용하는 것이 권장된다.
출처 https://docs.spring.io/spring-framework/reference/core/beans/java/basic-concepts.html
@Import
의도적으로 Configuration 클래스를 컴포넌트 스캔 대상 패키지 밖
으로 빼어 컴포넌트 스캔에서 제외시킬 수 있다.
빈으로 여러 개의 Configuration 클래스가 모두 Bean으로 등록되는 것을 원치 않는 경우 이런 방법을 사용한다.
이 때 특정 Configuration 파일만 Bean으로 등록하는 방법이 바로 @Import를 사용하는 것이다.
application 클래스에서 @Import 어노테이션으로 Configuration 클래스를 명시한다.
// Application Class
/**
* scanBasePackage를 지정하면, 그 이하의 패키지만 컴포넌트 스캔
* FooBarConfig.class가 hello.spring.app 밖의 패키지에 존재하는 경우
**/
@SpringBootApplication(scanBasePackages = "hello.spring.app")
@Import(FooBarConfig.class)
// 여러 Config 클래스를 동시에 import 하는 경우 파라미터로 배열
// -> @Import({FooConfig.class, BarConfig.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
'Spring Boot, JPA' 카테고리의 다른 글
Spring - 외부 설정 (0) | 2024.11.13 |
---|---|
Spring - WAR, JAR, Executable JAR (0) | 2024.11.13 |
Spring - 중복 제거를 위한 디자인 패턴 (0) | 2024.11.12 |
Spring - Thread Local로 동시성 문제 해결 (0) | 2024.11.12 |
Spring - 컨테이너와 빈(Bean) (2) | 2024.11.12 |