스프링 빈
스프링 빈(Spring Bean)은 스프링 컨테이너가 관리하는 객체로, 스프링 애플리케이션에서 구성 요소로 동작하며,
의존성 주입과 같은 스프링의 핵심 기능을 활용할 수 있도록 관리된다.
즉, 빈은 인스턴스화된 객체를 의미하며, 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
쉽게 이해하자면 new 키워드 대신 사용한다고 보면된다.
스프링 빈의 특징
1. 스프링 컨테이너에서 관리된다
- 개발자가 직접 생성하고 관리하지 않고, 스프링 컨테이너(ApplicationContext)가 객체의 생명 주기를 관리한다.
- 즉, 스프링 프레임워크가 객체(빈)의 생성부터 소멸까지의 생명 주기를 책임지고 관리한다는 뜻이다.
- 스프링 컨테이너(ApplicationContext)는 스프링 빈(Bean)을 생성하고 관리하는 "공장" 같은 역할을 한다.
2. 싱글톤으로 관리된다.
- 기본적으로 스프링 빈은 싱글톤(Singleton) 스코프로 생성되며, 컨테이너당 하나의 인스턴스만 생성된다.
- 하나의 스프링 컨테이너가 같은 스프링 빈 클래스를 요청할 때, 항상 같은 객체(인스턴스)를 반환한다는 의미이다.
- 즉, 스프링 컨테이너는 빈을 처음 한 번만 생성하고, 이후에는 계속 동일한 객체를 재사용한다.
3. 의존성 주입이 가능하다.
- 스프링 컨테이너가 제공하는 핵심 기능 중 하나가 의존성 주입(DI, Dependency Injection)이다.
- @Autowired어노테이션을 통해 스프링 컨테이너에게 의존성 주입을 요청
- 의존성 주입은 스프링 컨테이너가 스프링 빈의 의존성을 자동으로 주입하는 과정을 의미.
- 즉, 객체가 필요로 하는 다른 객체(의존성)를 스프링이 대신 주입해준다는 뜻이다.
- 스프링 컨테이너는 빈을 생성하면서 필요한 의존성을 자동으로 주입한다.
- 의존성은 주로 세 가지 방식으로 주입된다.
스프링 빈 등록 방법
1. 어노테이션 기반 등록
@Component
public class MyComponent {
public void doSomething() {
System.out.println("MyComponent is working!");
}
}
- @Component, @Service, @Repository, @Controller 등의 어노테이션을 사용하여 빈으로 등록한다.
- 스프링의 @ComponentScan 기능이 이를 스캔하여 컨테이너에 등록한다.
2. Java Config 클래스에서 등록
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
- @Configuration 클래스 내부에 @Bean을 사용하여 직접 빈을 정의하고 등록할 수 있다.
3. XML 설정 파일에서 등록
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="myService" class="com.example.MyService"/>
</beans>
- XML 파일에 <bean> 태그를 사용하여 빈을 정의한다.
스프링 빈의 스코프(Scope)
스코프는 빈이 생성되고 유지되는 범위를 정의한다. 빈의 스코프는 객체의 생명주기와 범위를 결정한다.
일반적으로 사용되는 스코프에는 싱글톤(Singleton), 프로토타입(Prototype), 그리고 웹 관련 스코프가 있다.
1. 싱글톤(Singleton): 컨테이너당 하나의 인스턴스만 생성되며, 애플리케이션 전역에서 공유된다.
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton") // 기본값이기 때문에 생략해도 됩니다.
public class SingletonBean {
public SingletonBean() {
System.out.println("SingletonBean 객체 생성");
}
public void printMessage() {
System.out.println("싱글톤 빈: " + this);
}
}
@Component
public class MainApp {
@Autowired
private SingletonBean singletonBean1;
@Autowired
private SingletonBean singletonBean2;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApp.class, args);
MainApp app = context.getBean(MainApp.class);
app.testSingletonScope();
}
public void testSingletonScope() {
System.out.println("==== 싱글톤 빈 테스트 ====");
singletonBean1.printMessage();
singletonBean2.printMessage();
}
}
SingletonBean 객체 생성
싱글톤 빈: SingletonBean@1a2b3c4d // 동일한 인스턴스를 참조
싱글톤 빈: SingletonBean@1a2b3c4d // 동일한 인스턴스를 참조
- 하나의 인스턴스만 존재: 애플리케이션 전체에서 동일한 객체가 사용된다.
- 전역적으로 공유: 모든 요청에서 동일한 객체가 사용되기 때문에 상태를 공유해야 하는 객체에 유용하다.
- 메모리 절약: 객체를 한 번만 생성하므로 메모리 사용량을 줄일 수 있다.
- 스프링의 기본 스코프: @Scope("singleton")로 명시하지 않아도 기본적으로 싱글톤이 된다.
2. 프로토타입(Prototype): 빈 요청 시마다 새로운 인스턴스를 생성한다.
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype") // 프로토타입 스코프
public class PrototypeBean {
public PrototypeBean() {
System.out.println("PrototypeBean 객체 생성");
}
public void printMessage() {
System.out.println("프로토타입 빈: " + this);
}
}
@Component
public class MainApp {
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApp.class, args);
MainApp app = context.getBean(MainApp.class);
app.testPrototypeScope();
}
public void testPrototypeScope() {
System.out.println("==== 프로토타입 빈 테스트 ====");
// 프로토타입 빈을 매번 새로 요청하여 출력
PrototypeBean prototypeBean1 = context.getBean(PrototypeBean.class);
prototypeBean1.printMessage();
PrototypeBean prototypeBean2 = context.getBean(PrototypeBean.class);
prototypeBean2.printMessage();
}
}
PrototypeBean 객체 생성
프로토타입 빈: PrototypeBean@1a2b3c4d // 서로 다른 인스턴스를 참조
PrototypeBean 객체 생성
프로토타입 빈: PrototypeBean@5e6f7g8h // 서로 다른 인스턴스를 참조
- 매번 새로운 인스턴스가 생성된다.
- 객체가 필요할 때마다 새로 생성되므로 상태를 공유하지 않으며, 독립적인 객체가 필요할 때 유용하다.
- 메모리 사용이 더 많을 수 있습니다. 요청할 때마다 객체를 생성하기 때문이다.
- 스프링 컨테이너가 해당 객체의 생명주기를 관리하지 않기 때문에 명시적으로 객체를 관리해야 할 필요가 있다.
3. 웹 관련 스코프: HTTP 요청, 세션, 서블릿 컨텍스트와 같은 웹 관련 객체에 맞춰 빈의 생명주기를 관리하는 방법이다.
- request: HTTP 요청마다 새로운 빈 인스턴스를 생성한다.
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope // request 스코프
public class RequestScopedBean {
public RequestScopedBean() {
System.out.println("RequestScopedBean 객체 생성");
}
public void printMessage() {
System.out.println("Request 스코프 빈: " + this);
}
}
@Component
public class MainApp {
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApp.class, args);
MainApp app = context.getBean(MainApp.class);
app.testRequestScope();
}
public void testRequestScope() {
System.out.println("==== Request 스코프 빈 테스트 ====");
// 요청마다 새로운 인스턴스를 요청
RequestScopedBean requestScopedBean1 = context.getBean(RequestScopedBean.class);
requestScopedBean1.printMessage();
RequestScopedBean requestScopedBean2 = context.getBean(RequestScopedBean.class);
requestScopedBean2.printMessage();
}
}
RequestScopedBean 객체 생성
Request 스코프 빈: RequestScopedBean@1a2b3c4d
RequestScopedBean 객체 생성
Request 스코프 빈: RequestScopedBean@5e6f7g8h
- HTTP 요청마다 새로운 빈 인스턴스가 생성됨.
- 해당 요청이 끝나면 빈은 더 이상 존재하지 않음.
- 여러 요청을 처리하는 경우, 각 요청은 독립적인 객체를 가짐.
- session: HTTP 세션마다 새로운 빈 인스턴스를 생성한다.
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;
@Component
@SessionScope // session 스코프
public class SessionScopedBean {
public SessionScopedBean() {
System.out.println("SessionScopedBean 객체 생성");
}
public void printMessage() {
System.out.println("Session 스코프 빈: " + this);
}
}
@Component
public class MainApp {
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApp.class, args);
MainApp app = context.getBean(MainApp.class);
app.testSessionScope();
}
public void testSessionScope() {
System.out.println("==== Session 스코프 빈 테스트 ====");
// 세션마다 새로운 인스턴스를 요청
SessionScopedBean sessionScopedBean1 = context.getBean(SessionScopedBean.class);
sessionScopedBean1.printMessage();
SessionScopedBean sessionScopedBean2 = context.getBean(SessionScopedBean.class);
sessionScopedBean2.printMessage();
}
}
SessionScopedBean 객체 생성
Session 스코프 빈: SessionScopedBean@1a2b3c4d
Session 스코프 빈: SessionScopedBean@1a2b3c4d
- 세션마다 하나의 빈 인스턴스가 생성됨.
- 세션이 종료될 때 빈은 사라짐.
- 로그인한 사용자에게만 해당되는 데이터 저장이나 세션 상태 관리에 사용.
- application: 서블릿 컨텍스트당 하나의 빈 인스턴스를 생성한다.
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;
@Component
@ApplicationScope // application 스코프
public class ApplicationScopedBean {
public ApplicationScopedBean() {
System.out.println("ApplicationScopedBean 객체 생성");
}
public void printMessage() {
System.out.println("Application 스코프 빈: " + this);
}
}
@Component
public class MainApp {
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApp.class, args);
MainApp app = context.getBean(MainApp.class);
app.testApplicationScope();
}
public void testApplicationScope() {
System.out.println("==== Application 스코프 빈 테스트 ====");
// 서블릿 컨텍스트마다 동일한 인스턴스를 요청
ApplicationScopedBean applicationScopedBean1 = context.getBean(ApplicationScopedBean.class);
applicationScopedBean1.printMessage();
ApplicationScopedBean applicationScopedBean2 = context.getBean(ApplicationScopedBean.class);
applicationScopedBean2.printMessage();
}
}
ApplicationScopedBean 객체 생성
Application 스코프 빈: ApplicationScopedBean@1a2b3c4d
Application 스코프 빈: ApplicationScopedBean@1a2b3c4d
- 서블릿 컨텍스트마다 하나의 빈 인스턴스가 생성됨.
- 애플리케이션이 종료될 때 빈이 사라짐.
- 애플리케이션 전반에 걸쳐 공유되어야 하는 데이터를 관리하는 데 유용하다.
스프링 빈의 생명주기
1. 생성: 스프링 컨테이너가 빈의 인스턴스를 생성한다.
- 스프링 컨테이너가 애플리케이션 시작 시점에 설정 파일(예: XML, Java Config)을 참고하여 빈 정의를 읽고 인스턴스를 생성
- 빈 생성은 @Component, @Bean, XML <bean> 등으로 등록된 빈을 기반으로 이루어진다.
- 이 단계에서 객체가 메모리에 로드되지만, 의존성은 아직 주입되지 않는다.
2. 의존성 주입: 생성된 빈에 필요한 의존성을 주입한다.
- 생성된 빈에 필요한 의존성을 컨테이너가 자동으로 주입한다.
- 아래는 주입 방법이다.
- 생성자 주입: 빈 생성과 동시에 의존성을 생성자 파라미터로 전달.
@Component
public class ServiceA {
public void serve() {
System.out.println("ServiceA is serving...");
}
}
@Component
public class Client {
private final ServiceA serviceA;
// 생성자 주입
@Autowired
public Client(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void execute() {
serviceA.serve();
}
}
- 가장 권장되는 방법으로, 주입받을 객체를 생성자를 통해 전달받음.
- 의존성이 final로 선언 가능하여 불변성을 보장할 수 있음.
- 세터 주입: 빈 생성 후, 세터 메서드를 통해 주입.
@Component
public class ServiceA {
public void serve() {
System.out.println("ServiceA is serving...");
}
}
@Component
public class Client {
private ServiceA serviceA;
// 세터 주입
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void execute() {
serviceA.serve();
}
}
- 의존성 주입을 위한 세터 메서드를 통해 객체를 전달받음.
- 선택적 의존성이 필요한 경우 적합.
- 그러나, 객체가 주입되지 않은 상태에서 메서드를 호출할 가능성이 있어 런타임 에러 위험이 있음.
- 필드 주입: 빈 생성 후, 필드에 직접 주입.
@Component
public class ServiceA {
public void serve() {
System.out.println("ServiceA is serving...");
}
}
@Component
public class Client {
@Autowired
private ServiceA serviceA;
public void execute() {
serviceA.serve();
}
}
- 필드에 직접 @Autowired를 붙여 주입.
- 코드가 간결하지만, 의존성 확인 및 변경이 어려움.
- 테스트나 유지보수 관점에서 권장되지 않음.
3. 초기화: 빈이 의존성을 모두 주입받은 후, 초기화 작업을 수행한다.
@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("MyBean initialized.");
}
}
- 초기화는 빈의 설정이 완료된 후 추가 작업을 정의할 수 있는 단계이다.
- 아래는 초기화 설정 방법이다.
초기화 설정 방법
- InitializingBean 인터페이스: afterPropertiesSet() 메서드 구현.
- XML <bean> 속성: <bean init-method="initMethodName" />.
- Java Config: @Bean(initMethod="initMethodName").
- @PostConstruct: 빈의 초기화 작업을 지정하는 가장 권장되는 방식.
4. 사용: 빈이 애플리케이션의 요청을 처리하거나 작업을 수행한다.
- 초기화가 완료된 빈은 애플리케이션이 실행되는 동안 컨테이너에서 관리되며 요청에 따라 사용된다.
- 스프링 컨테이너는 요청 시 빈을 반환하거나 새로운 빈(스코프에 따라)을 생성한다.
- 빈의 사용 단계에서는 비즈니스 로직을 처리한다.
5. 소멸: 애플리케이션 종료 시 @PreDestroy나 destroy-method를 통해 정리 작업을 수행한다.
@Component
public class MyBean {
@PreDestroy
public void destroy() {
System.out.println("MyBean destroyed.");
}
}
- 소멸 단계는 자원 정리, 연결 종료 등의 작업을 수행하는 데 사용된다.
소멸 설정 방법
- DisposableBean 인터페이스: destroy() 메서드 구현.
- XML <bean> 속성: <bean destroy-method="destroyMethodName" />.
- Java Config: @Bean(destroyMethod="destroyMethodName").
- @PreDestroy: 빈 소멸 작업을 지정하는 가장 권장되는 방식.
생명주기 콜백
생명주기 콜백은 빈의 생명주기 각 단계에서 호출되는 특정 메서드들을 의미
스프링은 빈을 관리하는 동안 특정 시점에 자동으로 호출될 메서드를 지정할 수 있도록 제공하며, 이를 통해 빈의 생명주기를 제어하거나 특정 작업을 수행할 수 있다. 생명주기 콜백은 보통 빈이 생성되고 초기화되는 시점 또는 소멸되는 시점에 호출된다.
생명주기 콜백 종류
@Component
public class MyBean implements InitializingBean, DisposableBean {
public MyBean() {
System.out.println("MyBean 생성");
}
// 초기화 콜백 (InitializingBean의 afterPropertiesSet() 메서드)
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MyBean 초기화 (InitializingBean)");
}
// 소멸 콜백 (DisposableBean의 destroy() 메서드)
@Override
public void destroy() throws Exception {
System.out.println("MyBean 소멸 (DisposableBean)");
}
}
- 초기화 콜백 (Initialization Callbacks): 빈이 생성된 후, 의존성 주입이 완료된 후에 호출된다.
- @PostConstruct: 빈이 생성되고 의존성 주입이 완료된 후 초기화 작업을 할 때 사용한다.
- InitializingBean 인터페이스의 afterPropertiesSet(): @PostConstruct와 비슷한 역할을 하지만 InitializingBean 인터페이스를 구현해야 한다.
- 소멸 콜백 (Destruction Callbacks): 빈이 더 이상 사용되지 않거나 애플리케이션이 종료될 때 호출된다.
- @PreDestroy: 빈이 소멸되기 전에 호출됩니다.
- DisposableBean 인터페이스의 destroy(): @PreDestroy와 비슷한 역할을 하지만 DisposableBean 인터페이스를 구현해야 한다.
빈의 생명주기와 생명주기 콜백의 차이
- 빈의 생명주기: 스프링 컨테이너에서 빈이 생성되고, 초기화되며, 소멸되는 전체적인 과정을 의미한다. 빈이 언제 생성되고, 언제 초기화되며, 언제 소멸되는지를 다룬다.
- 생명주기 콜백: 빈의 생명주기 중 특정 단계에서 호출되는 메서드를 의미한다. 이 메서드는 초기화나 소멸 등 빈의 특정 단계에서 자동으로 호출되어 추가 작업을 수행할 수 있게 한다.
따라서, 빈의 생명주기는 빈이 살아있는 동안의 전체 흐름을 말하고, 생명주기 콜백은 그 흐름 속에서 특정 시점에 호출되는 메서드들이다.
예제 코드: 빈 생명주기 전체 흐름
@Component
public class MyService {
public MyService() {
System.out.println("1. MyService constructor: 빈 생성");
}
@Autowired
public void setDependency(MyDependency dependency) {
System.out.println("2. setDependency: 의존성 주입");
}
@PostConstruct
public void init() {
System.out.println("3. @PostConstruct: 초기화 작업");
}
public void doWork() {
System.out.println("4. 사용 중: 비즈니스 로직 실행");
}
@PreDestroy
public void destroy() {
System.out.println("5. @PreDestroy: 빈 소멸 전 작업");
}
}
@Component
public class MyDependency {
public MyDependency() {
System.out.println("MyDependency 빈 생성");
}
}
@Configuration
@ComponentScan("com.example")
public class AppConfig {}
테스트 코드
public class BeanLifecycleTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = context.getBean(MyService.class);
myService.doWork();
context.close(); // 애플리케이션 종료 -> 빈 소멸
}
}
실행 결과
MyDependency 빈 생성
1. MyService constructor: 빈 생성
2. setDependency: 의존성 주입
3. @PostConstruct: 초기화 작업
4. 사용 중: 비즈니스 로직 실행
5. @PreDestroy: 빈 소멸 전 작업
- @PostConstruct는 빈이 초기화된 후에 호출되는 메서드를 지정하는 어노테이션이다.
- MyService클래스에서 @PostConstruct를 사용하면, Spring이 MyService의 인스턴스를 생성하고, 의존성 주입을 완료한 후에 init() 메서드를 자동으로 호출한다.
스프링 컨테이너
스프링 컨테이너(Spring Container)는 스프링 프레임워크의 핵심 구성 요소로, 빈(Bean)의 생성, 관리 및 조정을 담당한다.
스프링 컨테이너의 주요 역할
- 빈 생성 및 관리: 스프링 컨테이너는 애플리케이션에서 사용하는 객체(빈)를 생성하고, 초기화하고, 필요한 의존성을 주입한다. 의존성 주입을 통해 개발자는 의존성 관리에 대한 부담을 줄이고, 객체 간의 결합도를 낮춘다.
- 의존성 주입: 스프링은 의존성 주입(DI) 방식으로 객체를 연결한다. 스프링 컨테이너는 @Autowired가 붙은 필드나 메소드에 자동으로 의존성을 주입한다. 왜냐하면 @Autowired 어노테이션을 통해 스프링 컨테이너에게 의존성 주입을 요청하기 때문이다. 즉, 객체가 필요한 의존 객체를 스프링 컨테이너에서 주입받는다. 이를 통해 객체들 간의 의존성을 최소화하고, 유연한 코드 작성이 가능합니다.
- 빈 생명주기 관리: 스프링 컨테이너는 빈의 생성, 초기화, 소멸 등을 자동으로 관리한다. 예를 들어, 객체가 생성된 후 의존성 주입이 이루어지고, 초기화 메서드가 호출된 후 사용되며, 소멸될 때는 자원 해제 작업 등을 실행한다.
- 스코프 관리: 스프링은 빈의 스코프(빈이 생성되고 유지되는 범위)를 관리한다. 예를 들어, singleton, prototype, request, session 등 다양한 스코프를 제공하여 빈이 어떻게 생성되고 관리될지를 정의할 수 있다.
스프링 컨테이너의 주요 역할 정리
1 .객체 생성:
- 개발자가 직접 객체를 생성하지 않아도, 컨테이너가 설정에 따라 필요한 객체를 자동으로 생성한다.
2. 의존성 관리(Dependency Injection)
- 객체 간의 의존 관계를 스프링 컨테이너가 알아서 연결해준다.
- 즉, 필요한 객체를 주입해 준다.
3. 라이프사이클 관리
- 객체의 생성부터 소멸까지의 라이프사이클을 컨테이너가 책임진다.
- 개발자는 객체의 생성과 소멸 시점에 대해 고민할 필요가 없다.
스프링 컨테이너의 종류
1. BeanFactory: 빈을 생성하고 의존관계를 설정하는 기능을 담당하는 가장 기본적인 IoC 컨테이너이자 클래스를 말한다.
- 스프링 빈 컨테이너에 접근하기 위한 최상위 인터페이스.
- 스프링 빈을 관리하고 조회하는 역할을 담당.
- getBean()을 제공.
- Lazy-loading 방식을 사용.( 빈을 필요할 때만 생성하고 초기화하는 방식 )
- 필요할 때만 로딩하기 때문에, 가벼운 경량 컨테이너이기 때문에 메모리 효율이 뛰어나다.
2. ApplicationContext: BeanFactory의 기능을 포함하면서, 더 많은 기능을 제공, BeanFactory의 확장된 버전
- Eager-loading 방식을 사용한다. (런타임 실행시 모든 빈을 미리 로딩시키는 방식)
- MessageSource 를 이용한 국제화 기능
- EnvironmentCapable 환경 변수를 이용한 로컬, 개발, 운영 구분
- ApplicationEventPublisher 애플리케이션 이벤트를 이용하여, 이벤트를 발행하고 구독하는 모델을 편리하게 지원
- ResourceLoader 를 이용하여 편리하게 파일, 클래스패스 등의 리소스를 조회
- AOP 지원
'Spring' 카테고리의 다른 글
[Spring] 예외 처리 방식 (0) | 2024.12.11 |
---|---|
[Spring] Security JWT (0) | 2024.11.10 |
[Spring] 세션, 토큰, 쿠키 (0) | 2024.11.08 |
[Spring] @Profile (0) | 2024.11.04 |
[Spring] 스프링 AOP - 실무 주의사항 (1) | 2024.09.25 |