Spring Bean
빈은 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트이다.
Spring IoC 컨테이너가 관리하는 자바 객체를 빈(Bean) 이라고 한다.
스프링 컨테이너가 관리하는 자바 객체를 뜻하며, 하나 이상의 빈을 관리한다.
빈은 인스턴스화 된 객체를 의미하고, 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
@Bean 어노테이션을 통해 메서드로부터 반환된 객체를 스프링 컨테이너에 등록한다.
빈은 클래스의 등록 정보, Getter/Setter 메서드를 포함하며, 컨테이너에 사용되는 설정 메타데이터로 생성된다.
설정 메타데이터 : XML or Java Annotation, 자바 코드로 표현하며 컨테이너의 명광과 인스턴스화, 설정, 조립할 객체 등을 정의한다.
IoC 의 특징
일반적으로 처음 배우는 자바 프로그램에서는 각 객체들이 프로그램의 흐름을 결정하고 각 객체를 직접 생성하고 조작하는 작업(객체를 직접 생성하여 메서드 호출)을 했다. 즉 모든 작업을 사용자가 제어하는 구조였다.
ex) A 객체에서 B 객체에 있는 메서드를 사용하고 싶다면, B 객체를 직접 A 객체 내에서 생성하고 메서드를 호출
IoC 가 적용된 경우, 객체의 생성을 특별한 관리 위임 주체에게 맡긴다. 이 경우 사용자는 객체를 직접 생성하지 않고, 객체의 생명주기를 컨트롤하는 주체는 다른 주체가 된다.
사용자의 제어권을 다른 주체에게 넘기는 것을 IoC(제어의 역전)라고 한다.
Spring 에서는 직접 new 를 이용하여 생성한 객체가 아니라, Spring 에 의해 관리당하는 자바 객체를 사용한다.
이렇게 Spring에 의해 생성되고 관리되는 자바 객체를 Bean 이라고 한다.
Bean 을 왜 사용할까?
스프링 간 객체의 의존관계를 관리하도록 하는 것에 가장 큰 목적이 있다.
객체가 의존관계를 등록할 때 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만든다.
Spring Bean의 생명주기
객체 생성 -> 의존 설정 -> 초기화 -> 사용 -> 소멸 과정 의 생명주기를 가지고 있다.
빈은 스프링 컨테이너에 의해 생명주기를 관리한다.
스프링 컨테이너가 초기화될 때 먼저 빈 객체를 설정에 맞추어 생성하며 의존 관계를 설정한 뒤 해당 프로세스가 완료되면 빈 객체가 지정한 메서드를 호출해서 초기화를 진행한다.
객체를 사용해 컨테이너가 종료될 때 빈이 지정한 메서드를 호출해 소멸 단계를 거친다.
스프링은 InitializingBean 인터페이스와 DisposableBean 을 제공하며 빈 객체의 클래스가 InitializingBean Interface 또는 DisposableBean 을 구현하고 있으며 해당 인터페이스에서 정의된 메서드를 호출해 빈 객체의 초기화 또는 종료를 수행한다.
어노테이션을 이용한 빈 초기화 방법에는 @PostConstruct 와 빈 소멸에서는 @PreDestroy 를 사용한다.
Spring Bean 의 Scope
빈 스코프는 기본적으로 빈이 존재하는 범위를 뜻한다.
빈의 객체는 기본적으로 싱글톤의 범위를 가지며 싱글톤은 스프링 컨테이너의 시작과 종료까지 단 하나의 객체만을 사용하는 방식이다.
request, session, global session 의 스코프는 일반 spring 어플리케이션이 아니라 Spring MVC Web Application 에만 사용된다.
스코프 | 설명 |
singleton | 하나의 빈 정의에 대해 Spring IoC Container 에서 단 하나의 객체만 존재한다. |
prototype | 하나의 빈 정의에 대해 다수의 객체가 존재할 수 있다. |
request | 하나의 빈 정의에 대해 하나의 HTTP request 의 생명주기 안에 단 하나의 객체만 존재한다. 각각의 HTTP request는 자신만의 객체를 가지며 Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다. |
session | 하나의 빈 정의에 대해 하나의 HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다. Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다. |
global session | 하나의 빈 정의에 대해 하나의 global HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다. 일반적으로는 portlet context 안에서만 유효하며, Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다. |
빈의 객체 범위를 prototype 으로 설정하면 객체를 매번 새롭게 생성한다는 특징이 있으며, 프로토타입으로 설정하면 @Scope 어노테이션을 @Bean 어노테이션과 함께 사용해야 한다.
1. Singleton
- singleton bean 은 스프링 컨테이너 안에서 딱 한 번 생성되며, 컨테이너가 사라질 때는 bean도 같이 사라진다.
- 생성된 하나의 인스턴스는 single bean cache 에 저장되며 해당 bean 에 대한 요청과 참조가 있을 때마다 캐시된 객체를 반환한다.
- 기본적으로 모든 bean 은 scope 가 명시적이지 않으면 singleton scope 를 가진다.
- 상태가 없는 공유객체, 읽기 전용으로만 상태를 가진 객체, 쓰기가 가능한 상태를 지니면서도 사용 빈도가 높은 객체가 싱글톤 타입에 적합하다.
// xml setting
<bean id="..." class="..." scope="singleton"></bean>
// annotation setting
@Scope("singleton")
2. Prototype
- prototype bean 은 모든 요청에서 새로운 객체로 생성하는 것이며, 의존성 관계의 bean 이 주입될 때마다 새로운 객체가 생성되어 주입된다.
- GC에 의해 bean 이 자동으로 제거된다.
- 사용할 때마다 상태가 달라져야 하는 객체와 쓰기가 가능한 상태가 있는 객체가 프로토 타입으로 적합하다.
// xml setting
<bean id="...' class="..." scope="prototype"></bean>
// annotation setting
@Scope("prototype")
Spring Bean 을 Spring IoC Container 에 등록하는 방법
자바 어노테이션을 사용한다.
자바에서는 Annotation 이라는 기능이 있는데 이는 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종이다.
자바에서는 @Override, @Deprecated 와 같은 기본적인 Annotation 을 제공한다.
public class Parant {
public void doSomething() {
System.out.println("Parent");
}
}
Public class Son extends Parant {
@Override
public void doSomething() {
System.out.println("Son");
}
}
스프링에서는 여러 어노테이션을 사용하지만 빈을 등록하기 위해서는 @Component 어노테이션을 사용한다.
@Component Annotation 이 등록되어 있는 경우에는 스프링이 어노테이션을 확인하고 자체적으로 빈으로 등록한다.
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "This is data");
return "hello";
}
}
@Controller 어노테이션 내부를 확인해보면 아래와 같은 코드를 확인 할 수 있다.
@Controller 어노테이션에는 @Component 어노테이션이 있는 것을 확인 할 수 있다. 이로 인해 스프링은 해당 Controller 를 빈으로 등록한다.
// ...
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
@Component 어노테이션이 있으면 스프링 빈으로 자동 등록된다.
@Component 를 포함하는 @Controller, @Service, @Repository 어노테이션도 스프링 빈으로 자동 등록된다.
- @Controller : 스프링 MVC 컨트롤러로 인식된다.
- @Repository : 스프링 데이터 접근 계층으로 인식하고 해당 계층에서 발생하는 예외는 모두 DataAccessException 으로 변환한다.
- @Service : 특별한 처리는 하지 않으나, 개발자들이 핵심 비즈니스 계층을 인식하는데 도움을 준다.
- @Configuration : 스프링 설정 정보로 인식하고 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다. (물론 스프링 빈 스코프가 싱클톤이 아니라면 추가 처리를 하지 않음)
Bean Configuration File 에 직접 Bean 등록
@Configuration 과 @Bean 어노테이션을 이용해 빈을 등록할 수 있다.
아래처럼 사용하면 스프링 프로젝트에서의 Configuration 역할을 하는 class 를 지정할 수 있다.
해당 File 하위에 빈으로 등록하고자 하는 class 에 @Bean 어노테이션을 사용해주면 등록할 수 있다.
@Configuration
public class HelloConfiguration {
@Bean
public HelloController sampleController() {
return new SampleController;
}
}
컴포넌트 스캔과 자동 의존관계 설정
컴포넌트 스캔은 @Component 를 명시하여 빈을 추가하는 방법이다.
클래스 위에 @Component 를 붙이면 스프링이 알아서 스프링 컨테이너에 빈을 등록한다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
@Component 는 ElementType.TYPE 설정이 있어 Class 혹은 Interface 에만 붙일 수 있다.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.
이렇게 객체 의존관계를 외부에서 넣어주는 것을 의존성 주입(DI)이라고 한다.
@Bean vs @Component
@Bean
개발자가 컨트롤리 불가능한 외부 라이브러리들을 Bean 으로 등록하고 싶은 경우에 사용
메서드 또는 어노테이션 단위에 붙일 수 있다.
@Component
개발자가 직접 컨트롤이 가능한 클래스들의 경우에 사용된다.
클래스 또는 인터페이스 단위에 붙일 수 있다.
참고링크
https://ittrue.tistory.com/221
https://melonicedlatte.com/2021/07/11/232800.html
https://dev-wnstjd.tistory.com/440
https://developer-ellen.tistory.com/198
https://steady-coding.tistory.com/594