스프링 프레임워크에서 팩토리 빈(FactoryBean<T>)은 객체 생성을 책임지고 해당 객체를 반환하는 특별한 종류의 빈(Bean)입니다. FactoryBean 은 다른 빈을 생성하는 용도로 사용될 수 있으며, 주로 복잡한 객체 생성 로직이 필요한 경우에 활용됩니다.
※ BeanFactory 인터페이스와 혼동하지 마세요
스프링 프레임워크에서 FactoryBean 인터페이스를 사용하는 궁극적인 이유는 복잡한 초기화 로직을 갖는 객체의 생성을 캡슐화하고, 스프링의 의존성 주입(DI) 컨테이너에 통합하기 위함입니다. FactoryBean을 사용하면 다음과 같은 이점들이 있습니다:
1. 복잡한 생성 과정 캡슐화: 객체 생성 과정이 복잡할 때, 이 로직을 FactoryBean 내부에 숨겨서 사용자가 간단히 빈을 요청할 수 있게 합니다. 이는 코드의 단순화와 재사용성 향상에 도움이 됩니다.
다이나믹 프록시(dynamic proxy) 생성은 복잡한 객체 생성 과정의 좋은 예입니다. 스프링에서 FactoryBean을 사용하여 다이나믹 프록시를 생성하는 경우는 다음과 같은 이유로 매우 흔합니다:
1. 인터페이스 기반 프록시 생성: 다이나믹 프록시는 특정 인터페이스를 구현하는 객체를 런타임에 생성합니다. 이를 통해 메소드 호출을 가로채거나 추가적인 로직을 실행할 수 있습니다.
2. AOP 통합: 다이나믹 프록시는 스프링 AOP(Aspect-Oriented Programming)의 핵심입니다. AOP는 트랜잭션 관리, 로깅, 보안 등과 같은 공통된 관심사(cross-cutting concerns)를 모듈화하는 데 사용됩니다.
3. 유연성: FactoryBean을 사용하면 다이나믹 프록시의 생성 로직을 캡슐화하고, 필요에 따라 다양한 프록시 객체를 유연하게 생성할 수 있습니다.
4. 스프링 컨테이너 통합: 다이나믹 프록시 객체들도 스프링의 라이프사이클 관리, 의존성 주입 등의 장점을 누릴 수 있습니다.
이러한 이유로, FactoryBean은 다이나믹 프록시와 같이 생성 과정이 복잡하고, 런타임에 다양한 처리가 필요한 객체를 생성하고 관리하는 데 매우 유용합니다.
2. 스프링 관리: FactoryBean으로 생성된 객체들은 스프링 컨테이너에 의해 관리됩니다. 이는 빈의 생명주기, 의존성 관리, AOP 통합 등 스프링의 다양한 기능을 활용할 수 있게 해줍니다.
3. 싱글톤/프로토타입 스코프 관리: FactoryBean을 통해 객체의 스코프(싱글톤, 프로토타입 등)를 관리할 수 있습니다. 이는 객체 생성과 관련된 메모리 관리나 성능 최적화에 유리합니다.
4. 지연 로딩: 필요할 때까지 객체의 생성을 지연시킬 수 있으므로, 애플리케이션의 시작 시간을 단축하고 리소스를 효율적으로 관리할 수 있습니다.
요약하면, FactoryBean은 복잡한 객체 생성 과정을 간소화하고, 스프링의 기능을 효과적으로 활용하여 애플리케이션의 구성과 유지보수를 용이하게 만드는 데 중요한 역할을 합니다.
FactoryBean 구체는 일반적으로 팩토리 메서드(Factory Method) 패턴을 구현한 클래스입니다. 이 팩토리 메서드는 특정한 객체를 생성하고 리턴하는 역할을 수행합니다. 스프링은 이러한 팩토리 메서드를 빈으로 등록하고, 필요한 경우에 해당 빈을 가져와서 사용할 수 있도록 지원합니다.
FactoryBean 을 사용하는 가장 일반적인 방법은 객체를 생성하기 위한 팩토리 메서드를 제공하는 클래스를 구현하는 것입니다. 팩토리 메서드는 실제로 객체를 생성하고 초기화하는 로직을 포함하며, FactoryBean은 이러한 팩토리 메서드를 호출하여 객체를 생성합니다. 팩토리 메서드는 보통 스태틱 메서드로 작성되며, FactoryBean은 해당 클래스를 참조하도록 설정됩니다.
스프링에서 FactoryBean 인터페이스를 구현한 클래스를 등록하기 위해서는 먼저 팩토리 메서드를 구현한 클래스를 정의해야 합니다. 이 클래스는 일반적으로 FactoryBean 인터페이스를 구현하거나, AbstractFactoryBean 클래스를 상속받아야 합니다. 이를 통해 스프링은 해당 클래스가 팩토리 빈으로 사용될 수 있도록 인식하게 됩니다.
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
팩토리 빈을 사용하면 생성된 빈의 라이프사이클을 컨트롤할 수 있습니다. 예를 들어, 팩토리 빈은 InitializingBean 인터페이스를 구현하여 초기화 로직을 정의할 수 있으며, DisposableBean 인터페이스를 구현하여 빈이 소멸될 때 정리 작업을 수행할 수도 있습니다. 또한, 팩토리 빈은 스코프를 지정하여 객체의 범위를 제어할 수도 있습니다.
팩토리 빈을 등록하기 위해 XML 구성 메타데이터 파일이나 자바 기반 구성 클래스에서 FactoryBean 인터페이스를 구현한 클래스를 빈으로 등록하면 됩니다.
스프링은 해당 클래스의 getObject() 메서드를 호출하여 팩토리 메서드를 실행하고, 그 결과로 리턴되는 객체를 빈으로 사용합니다. 또한, FactoryBean 인터페이스에는 getObjectType() 메서드가 정의되어 있어 스프링이 팩토리 빈이 생성하는 객체의 타입을 알 수 있습니다.
팩토리 빈은 빈의 스코프(scope)와 관련하여 중요한 역할을 수행할 수도 있습니다. 팩토리 빈은 기본적으로 싱글톤(Singleton) 스코프로 동작하지만, 필요에 따라 프로토타입(Prototype) 스코프와 같은 다른 스코프로 설정할 수도 있습니다.
팩토리 빈은 스프링의 강력한 기능 중 하나로, 동적인 객체 생성과 의존성 주입(Dependency Injection)을 조합하여 유연하고 확장 가능한 애플리케이션을 구성하는 데 도움을 줍니다.
스프링에서 팩토리 빈을 생성하려면 두 가지 방법을 사용할 수 있습니다.
1. 팩토리 메서드를 이용한 방법: 팩토리 빈을 생성하는 메서드를 정의한 후, 해당 메서드를 호출하여 빈을 생성합니다. 팩토리 메서드는 보통 팩토리 클래스에 위치하며, 정적(static) 또는 인스턴스 메서드로 정의될 수 있습니다. 이 방법은 스프링 빈 설정 파일(XML)에서 팩토리 빈을 정의할 때 주로 사용됩니다.
public class MyFactoryBean {
public static MyObject createInstance() {
// 객체 생성 및 초기화 로직
MyObject obj = new MyObject();
// ...
return obj;
}
}
팩토리 메서드를 사용한 팩토리 빈은 스프링 설정 파일에서 다음과 같이 정의할 수 있습니다.
<bean id="myFactoryBean" class="com.example.MyFactoryBean" factory-method="createInstance"/>
2. Spring의 FactoryBean 인터페이스를 구현한 클래스를 사용하는 방법: FactoryBean 인터페이스를 구현한 클래스를 생성하여 팩토리 빈으로 사용할 수 있습니다. FactoryBean 인터페이스는 getObject() 메서드를 통해 실제 빈 객체를 생성하고 반환합니다. 이 방법은 자바 구성 클래스(Java Config)에서 팩토리 빈을 정의할 때 주로 사용됩니다.
public class MyObject {
// MyObject 클래스의 구현 내용
}
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean<MyObject> {
@Override
public MyObject getObject() throws Exception {
// 객체 생성 및 초기화 로직
MyObject obj = new MyObject();
// ...
return obj;
}
@Override
public Class<?> getObjectType() {
return MyObject.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(name = "myFactoryBean")
public MyFactoryBean myFactoryBean() {
return new MyFactoryBean();
}
@Bean(name = "myObject")
public MyObject myObject() throws Exception {
return myFactoryBean().getObject();
}
}
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyObject myObject = context.getBean(MyObject.class);
context.close();
}
}
팩토리 빈을 사용하면 런타임 시에 다양한 구성 옵션에 따라 다른 타입의 빈 객체를 생성할 수 있습니다. 또한 팩토리 빈은 스프링의 다른 기능과 함께 사용될 수 있으며, 예를 들어 빈의 스코프(scope)를 제어하거나 AOP(Aspect-Oriented Programming)를 적용할 수 있습니다.
이렇게 팩토리 빈을 사용하면 객체 생성과 관리에 대한 유연성과 확장성을 제공하면서도 스프링의 IoC 컨테이너의 장점을 최대한 활용할 수 있습니다.
지연 로딩
지연 로딩(Lazy Loading)은 객체를 실제로 사용할 때까지 생성을 지연시키는 기법입니다. 스프링에서 지연 로딩을 구현하는 방법 중 하나는 @Lazy 애노테이션을 사용하는 것입니다. 이를 통해 빈(bean)이 필요할 때까지 초기화를 지연시킬 수 있습니다.
다음은 스프링에서 지연 로딩을 구현하는 간단한 예시 코드입니다:
1. 지연 로딩을 사용하는 빈 설정
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class AppConfig {
@Bean
@Lazy
public MyLazyBean myLazyBean() {
return new MyLazyBean();
}
}
2. 지연 로딩 빈의 클래스
public class MyLazyBean {
public MyLazyBean() {
System.out.println("MyLazyBean 생성자 호출");
}
public void doSomething() {
System.out.println("메소드 doSomething 실행");
}
}
3. 메인 애플리케이션
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("스프링 컨테이너 초기화 완료");
// 빈 요청
MyLazyBean bean = context.getBean(MyLazyBean.class);
bean.doSomething();
context.close();
}
}
이 코드에서 MyLazyBean 클래스는 지연 로딩 빈으로 설정되어 있습니다. 스프링 컨테이너가 초기화될 때 MyLazyBean의 인스턴스는 생성되지 않습니다. 대신, context.getBean(MyLazyBean.class)를 호출할 때 MyLazyBean의 인스턴스가 생성됩니다. 이 예제에서는 스프링 컨테이너 초기화 완료 메시지가 출력된 후에 MyLazyBean의 생성자가 호출됨을 확인할 수 있습니다.
이 방법은 리소스가 제한적인 환경에서 초기 부팅 시간을 줄이거나, 필요하지 않은 빈의 생성을 방지하기 위해 유용합니다.
팩토리빈(FactoryBean)을 사용하여 지연 로딩(Lazy Loading)
팩토리빈(FactoryBean)을 사용하여 지연 로딩(Lazy Loading)을 구현하는 것은 조금 더 복잡합니다. 이 경우, 팩토리빈은 실제 빈의 생성을 캡슐화하고, 해당 빈이 필요할 때까지 생성을 지연시킵니다. 다음은 팩토리빈을 사용하여 지연 로딩을 구현하는 예시 코드입니다:
1. 팩토리빈 인터페이스 구현
import org.springframework.beans.factory.FactoryBean;
public class LazyInitFactoryBean implements FactoryBean<MyLazyBean> {
private MyLazyBean myLazyBean;
@Override
public MyLazyBean getObject() throws Exception {
if (myLazyBean == null) {
myLazyBean = new MyLazyBean();
// 초기화 로직 실행
}
return myLazyBean;
}
@Override
public Class<?> getObjectType() {
return MyLazyBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
2. 지연 로딩을 사용할 빈 클래스
public class MyLazyBean {
public MyLazyBean() {
System.out.println("MyLazyBean 생성자 호출");
}
public void doSomething() {
System.out.println("메소드 doSomething 실행");
}
}
3. 스프링 설정 클래스
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public LazyInitFactoryBean myLazyBean() {
return new LazyInitFactoryBean();
}
}
4. 메인 애플리케이션
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("스프링 컨테이너 초기화 완료");
// 팩토리빈을 통한 빈 요청
MyLazyBean bean = context.getBean(MyLazyBean.class);
bean.doSomething();
context.close();
}
}
이 코드에서 LazyInitFactoryBean은 FactoryBean<MyLazyBean>을 구현합니다. getObject() 메소드는 MyLazyBean의 인스턴스를 지연 생성하며, 이 인스턴스는 getBean() 메소드를 통해 요청될 때 실제로 생성됩니다. 이 방식을 사용하면 스프링 컨테이너의 시작 시간을 단축하고, 필요하지 않은 리소스의 초기화를 방지할 수 있습니다.
'Spring Framework' 카테고리의 다른 글
AOP (0) | 2024.04.09 |
---|---|
Spring ProxyFactoryBean (0) | 2024.04.09 |
Java Dynamic Proxy와 Spring FactoryBean (0) | 2024.04.09 |
InvocationHandler (0) | 2024.04.09 |
서비스 추상화 (0) | 2024.04.09 |