스프링에는 빈이라는 요소가 존재한다. 이 빈은 스프링 컨테이너에 의해 관리되는 자바 객체로 ApplicationContext.getBean()
으로 얻어질 수 있는 객체를 의미한다.
컴포넌트 스캔은 @Componnent
를 명시하여 빈을 추가하는 방식으로 클래스 위에 @Component
를 붙이면 스프링이 알아서 스프링 컨테이너에 빈을 등록한다.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
위 코드 상에서 @SpringBootApplication
내부를 보면 @ComponentScan
이라는 어노테이션이 존재한다. 이 어노테이션은 @Component
로 등록된 인스턴스의 경로를 알려주는 역할을 하고 @Component
는 실제로 찾아서 빈으로 등록할 클래스를 의미한다. 이러한 요소들은 Spring 상에서 IoC 컨테이너로 인해 빈으로 등록된다.
그렇다면 어떤 요소를
@Component
가 등록하려고 할까??
자바 코드로 빈을 등록하는 과정에는 2개의 어노테이션만 사용하면 된다. 하나는 @Configuration, 다른 하나는 @Bean 이다.
@Configuration
public class AppConfig {
@Bean
public Repository repository() {
return new Repository();
}
@Bean
public Service service() {
return new ServiceImpl(repository());
}
}
여기서 @Bean
은 클래스가 아니라 메소드에 붙여야 한다.
앞서 Bean을 등록하는 방법을 알게 되었다. 구체적으로 Bean에는 어떤 속성들이 있고 어떤 범위가 있는지 알아보자.
속성에 설정된 정보를 토대로 객체가 생성이 된다.
Bean 같은 경우는 메모리 절약을 위하여 기본적으로 싱글톤 객체로 생성이 된다. 따라서 한번 생성이 되면 모든 객체가 동일한 객체를 참조하는 방식을 사용한다. 이외에 다른 범위를 살펴보자.
이렇게 많은 종류의 Bean이 존재하는데 우리는 Bean을 사용함에 있어 적절한 선택을 하는 것이 중요하다.
상태가 없는 공유 객체 : 상태를 가지고 있지 않으면 동기화 비용이 없기에 매번 이 객체를 참조하는 곳에서는 새로운 객체를 생성할 이유가 없다.
읽기용으로만 상태를 가진 공유 객체 : 1번과 유사하게 상태를 가지고 있으나 읽기 전용이므로 여전히 동기화 비용이 들지 않는다.
공유가 필요한 상태를 지닌 공유 객체 : 객체 간의 반드시 공유해야할 상태를 지닌 객체가 하나 있다면, 이 경우에는 해당 상태의 쓰기를 가능한 동기화 할 경우 싱글톤도 적합하다.
쓰기가 가능한 상태를 지니면서도 사용빈도가 매우 높은 객체 : 애플리케이션 안에서 정말로 사용 빈도가 높다면, 쓰기 접근에 대한 동기화 비용을 감안하고서라도 싱글톤을 고려할만하다.
1) 장시간에 걸쳐 많은 매우 많은 객체가 생성될 때
2) 해당 객체가 매우 작은 양의 쓰기 상태를 가지고 있을 때
3) 객체 생성비용이 매우 클 때
추가적으로 Bean에서 싱글톤을 사용하지만 우리는 동기화도 같이 처리해주는구나 생각할 때가 있다. 그러나 스프링은 싱글톤 레지스트리를 통해 private, static 변수 등의 코드 없이 비즈니스 로직에 집중하고 테스트 코드에 용이한 싱글톤 객체를 제공해주는 것 뿐으로 동기화 문제는 개발자가 처리해야 한다. 만약에 싱글톤 빈이 상태를 가지게 되고 아무런 동기화 처리를 하지 않는다면 멀티스레드 환경에서 부작용이 발생할 수 있다.