본문 바로가기

IT

스프링 팩토리 빈(Factory Bean)

728x90

스프링 빈은 기본적으로 클래스 이름과 프로퍼티로 정의된다. 스프링은 지정된 클래스 이름을 가지고 리플렉션을 이용해서 해당 클래스의 오브젝트를 만든다. 클래스의 이름을 갖고 있다면 다음과 같은 방법으로 새로운 오브젝트를 생성할 수 있다. Class의 newInstance() 메소드는 해당 클래스의 파라미터가 없는 생성자를 호출하고 그 결과 생성되는 오브젝트를 돌려주는 리플렉션 API 이다. 

Date now = (Date) Class.forName("java.util.Date").newInstance();

 

스프링은 내부적으로 리플렉션 API를 이용해서 빈 정의에 나오는 클래스 이름을 가지고 빈 오브젝트를 생성한다. 클래스 정보를 가지고 디폴트 생성자를 통해 오브젝트를 만드는 방법 외에도 빈을 만들 수 있는 여러가지 방법을 제공한다. 대표적으로 팩토리 빈(factory bean)을 이용한 빈 생성 방법이다.

 

팩토리 빈(Factory Bean)이란 스프링을 대신해서 오브젝트의 생성로직을 담당하도록 만들어진 특별한 빈이다. 

팩토리 빈을 만드는 방법에는 여러 가지가 있는데, 가장 간단한 방법은 스프링의 FactoryBean이라는 인터페이스를 구현하는 것이다.

public interface FactoryBean<T>{
	T getObject() throws Excetpion; -> 빈 오브젝트를 생성해서 돌려준다.
	Class<? extend T> getObjectType(); -> 생성되는 오브젝트의 타입을 알려준다.
	boolean isSingleton(); -> getObject()가 돌려주는 오브젝트가 항상 같은 싱글톤 오브젝트인지 알려준다.
}

FactoryBean 인터페이스를 구현한 클래스를 스프링의 빈으로 등록하면 팩토리 빈으로 동작한다. 

 

다음과 같은 Message 클래스를 빈 오브젝트로 만들어보자.

public class TextEx{
	String text;
    
    //생성자를 private으로 선언해서 외부에서 생성자를 통해 객체를 만들지 못하도록 설정
    private TextEx(String text){
    	this.text = text;
    }
    
    public String getText(){
    	return text;
    }
    
    //생성자 대신 스태틱 팩토리 메소드를 제공
    public static TextEx newTextEx(String text){
    	return new TextEx(text);
    }
}

Message 클래스의 객체를 만드려면 newMessage()라는 스태틱 메소드를 사용해야 하기에 이 클래스를 직접 스프링 빈으로 등록해서 사용할 수 없다. <bean id = "message" class="package.example.Message">  private 생성자를 가진 클래스의 직접 사용 금지

 

사실 스프링은 private 생성자를 가진 클래스를 빈으로 등록해주면 리플렉션을 이용해 객체를 만들어준다. 리플렉션은 private으로 선언된 접근 규약을 위반할 수 있는 강력한 기능이 있다. 하지만 생성자를 private으로 만들었다는 것은 스태틱 메소드를 통해 객체가 만들어져야 한다는 중요한 이유가 있기 무시하고 강제로 생성하면 위험하기에 조심해야한다.

 

팩토리 빈을 이용해서 생성하는 예제 코드를 보자.

public class TextExFactoryBean implements FactoryBean<TextEx>{
	String text;
    
    // 오브젝트를 생성할 때 필요한 정보를 팩토리 빈의 프로퍼티로 설정해서 대신 DI 받을 수 있게 한다. 
    // 주입된 정보는 오브젝트 생성 중에 사용된다.
    public void setText(String text){
    	this.text = text;
    }
    
    // 실제 빈으로 사용될 오브젝트를 직접 생성한다.
    public TextEx getObject() throws Exception{
    	return TextEx.newTextEx(this.text);
    }
    
    public Class<? extends TextEx> getObjectType(){
    	return TextEx.class;	
    }
    
    // getObject()가 메소드가 돌려주는 오브젝트가 싱글톤인지 알려준다. 이 팩토리 빈은 매번 요청할 때마다 새로운 오브젝트를 만들기 때문에 false로 설정했다. 
    // 이것은 팩토리 빈의 동작방식에 관한 설정이고 만들어진 빈 오브젝트는 싱글톤으로 스프링이 관리해줄 수 있다.
    public boolean isSingleton(){
    	return false;
    }
}

팩토리 빈은 팩토리 메소드를 가진 오브젝트일 뿐이다. 스프링은 FactoryBean 인터페이스를 구현한 클래스가 빈의 클래스로 지정되면 팩토리 빈 클래스의 getObject() 메소드를 이용해 오브젝트를 가져오고 이를 빈 오브젝트로 사용한다.

 

팩토리 빈의 설정 방법은 다음과 같다.

<bean id = "TextEx" class = "package.example.TextExFactoryBean">
	<property name="text" value = "Factory Bean" />
</bean>

TextEx 빈 오브젝트 타입이 class 애트리뷰틍 정의된 TextExFactoryBean이 아닌 getObectType() 메소드가 돌려주는 타입으로 결정된다. 또한 getObject() 메소드가 생성해주는 오브젝트가 빈의 객체로 등록된다.