olrlobt
[Spring] 스프링 컨테이너와 스프링 빈(Bean) 등록 본문
인프런의 김영한 강사님의 무료강의를 듣고 궁금한 내용을 찾아서 정리하였다.
스프링 빈 (Spring Bean)
Java Bean은 일반적으로는 getter와 setter를 갖고 있는 클래스(객체)를 의미하고, new를 통해 객체를 생성해 사용하곤 한다. 이러한 객체를 스프링 컨테이너에 등록해 주어, 개발자가 아닌 스프링 프레임워크가 객체를 관리하게 하는 것을 제어의 역전 (IoC)라고 하며, 이때 스프링 프레임워크가 관리하는 객체들을 스프링 빈 (Spring Bean)이라고 한다.
스프링 빈 등록
스프링 프레임워크에서는 Bean을 등록하는 대표적인 세 가지 방법이 존재한다.
XML을 이용한 Bean 등록
<beans>
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="message" value="Hello, Spring!" />
</bean>
</beans>
스프링의 초기 버전에서는 주로 XML 파일을 사용해 빈을 선언했다. <bean> 태그를 사용하여 빈을 정의하고, 이 안에서 <constructor-arg>나 <property> 태그를 사용하여 빈의 의존성을 주입해 주어야 했다.
스프링 부트가 출시된 지금은 잘 사용하지 않는 방법이다.
컴포넌트 스캔 : 어노테이션 기반 Bean 등록
@Service
public class ExampleService {
private final ExampleRepository exampleRepository;
@Autowired
public ExampleService(ExampleRepository exampleRepository) {
this.exampleRepository = exampleRepository;
}
// ...
}
스프링 2.5 이후 @Component, @Controller, @Service, @Repository 등의 어노테이션을 사용하여 클래스를 빈으로 선언할 수 있다. 또한, @Autowired나 @Inject를 사용하여 쉽게 의존성을 주입할 수 있다.
이 등록 방식이 컴포넌트 스캔이라고 불리는 이유는 @Component 어노테이션이 스프링 컴포넌트 스캔의 대상이며 스프링 빈으로 인스턴스화될 수 있음을 나타내는 어노테이션이기 때문이다.
@Component 이외의 @Controller, @Service, @Repository의 각 인터페이스에 들어가 보면 @Component를 메타-어노테이션(어노테이션에 붙는 어노테이션으로 상속과 비슷함)으로 갖고 있다는 것을 알 수 있다.
자바 설정 파일을 이용한 Bean 등록
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
스프링 3.0 이후, Java 설정 파일을 사용하는 방식을 사용할 수 있다. @Configuration은 해당 클래스가 Bean 정의를 포함하는 설정 클래스임을 나타낸다. @Bean 어노테이션을 사용하여 빈을 선언하고 의존성을 주입할 수 있다.
스프링 컨테이너
스프링 컨테이너는 스프링 프레임워크에서 제공하는 핵심 기능으로, 스프링에서의 객체(Bean) 생성과 라이프사이클 관리를 담당하며, 의존성 주입(D.I = Dependency Injection)을 통해 객체 간 관계를 설정하는 역할을 한다.
스프링 컨테이너는 애플리케이션 개발 시 객체 지향적인 설계와 유연성을 제공하여 개발자가 비즈니스 로직에 집중할 수 있도록 돕는다.
스프링 컨테이너는 크게 BeanFactory와 ApplicationContext의 두 가지 형태로 제공된다.
BeanFactory
BeanFactory는 스프링 컨테이너의 가장 기본적인 형태이며 최상위 인터페이스이다. 설정 파일에서 제공하는 메타데이터를 통해 빈을 생성하고, Bean 간의 의존성을 처리한다.
BeanFactory는 ApplicationContext와는 다르게, Bean을 필요한 시점에서 생성하여 인스턴스화하는 '지연 초기화(Lazy Initialization)' 방식을 사용한다. 이 방식은, 메모리 사용량을 최소화하고 성능을 향상할 수 있다.
ApplicationContext
ApplicationContext는 BeanFactory를 상속받아, BeanFactory의 모든 기능을 포함하고 있으며, 추가로 메시지 소스 처리를 통한 국제화 지원기능, 환경변수 구분 기능, 애플리케이션 레이어의 특정 컨텍스트 (ex WebApplicationContext)등 더 많은 기능을 제공한다.
ApplicationContext는 모든 Bean을 컨테이너 시작 시점에 생성하고 초기화하는 '즉시 초기화(Eager Initialization)'를 사용한다. 이를 통해 런타임 중에 발생할 수 있는 문제를 미리 방지할 수 있다.
만약, BeanFactory처럼 '지연 초기화(Lazy Initialization)' 를 사용하고 싶으면 @Lazy 어노테이션을 사용하여 Bean을 생성해 주면 지연 초기화가 가능하다.
위와 같은 이유들로 대부분의 경우, ApplicationContext가 BeanFactory보다 권장되며, 실제로 대부분의 스프링 기반 애플리케이션에서는 ApplicationContext를 사용한다.
싱글톤 컨테이너
스프링 컨테이너는 기본적으로 Bean의 인스턴스를 하나만 생성하여 관리하는 싱글톤 컨테이너이다.
싱글톤 컨테이너는 같은 Bean에 대한 모든 요청에 대해 하나의 인스턴스만을 재사용해서 반환하는데, 이는 메모리 사용을 최적화하여 성능 향상을 가져올 수 있다.
스프링 컨테이너가 진짜 싱글톤 컨테이너인지 확인하기 위해, ApplicationContext로 스프링 컨테이너를 가져와 getBean() 메서드를 이용해 빈을 꺼내 확인해 보았다.
@SpringBootApplication
public class StudySpringApplication {
public static void main(String[] args) {
SpringApplication.run(StudySpringApplication.class, args);
final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
final MemberService memberService1 = applicationContext.getBean("memberService", MemberService.class);
final MemberService memberService2 = applicationContext.getBean("memberService", MemberService.class);
System.out.println(memberService1);
System.out.println(memberService2);
System.out.println(applicationContext.isSingleton("memberService"));
}
}
ApplicationContext로 스프링 컨테이너를 가져와 getBean() 메서드를 통하여 등록되어 있는 "memberService" 스프링 빈을 가져왔다. 각각 memberService1과 memberService2로 두 번의 요청에 거쳐 가져왔고, 이를 출력해 주어 주솟값을 확인해 보았다.
Spring Container에 등록된 Bean은 기본적으로 Singleton을 스코프로 사용하기 때문에, 같은 인스턴스를 반환한다.
따라서 같은 주솟값이 출력된 것을 확인할 수 있었다.
또한, ApplicationContext에서 Singleton을 확인하기 위한 메서드로 isSingleton()을 사용할 수 있고, 확인 결과 true를 반환하여 싱글톤 컨테이너임을 확인하였다.
스코프 설정하기
만약, Singleton이 아닌 스코프를 사용하고 싶다면 Bean을 등록할 때 스코프를 같이 설정해 주면 된다.
Bean의 스코프는 singleton, prototype, request, session, global session 등 다양한 값으로 설정할 수 있고, @Scope를 통하여 등록 설정해 주면 된다.
- singleton: 스프링 컨테이너당 하나의 인스턴스만 생성
- prototype: 요청할 때마다 새로운 인스턴스를 생성
- request: HTTP 요청당 하나의 인스턴스를 생성. 주로 웹 환경에서 사용.
- session: HTTP 세션당 하나의 인스턴스를 생성. 주로 웹 환경에서 사용.
- global session: 전역 HTTP 세션당 하나의 인스턴스를 생성. 주로 포털 환경에서 사용.
테스트를 위해 다음과 같이 prototype으로 스코프를 설정한 후, 위 예제를 똑같이 수행해 보았다.
@Configuration
public class SpringConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
같은 memberService를 요청했음에도 싱글톤과는 다르게 각각 다른 주솟값이 나오는 것을 확인할 수 있다.
'Spring > Spring' 카테고리의 다른 글
[Spring boot] @Value 어노테이션으로 application.properties의 값 가져오기 (0) | 2023.03.27 |
---|