FactoryBean 사용하기
- new 연산자로 생성할 수 없는(생성자가 private인) 클래스의 의존성 주입을 위해 FactoryBean 인터페이스를 제공
- 다른 빈을 생성하는 팩터리 역할을 담당하는 bean
- FactoryBean을 이용하여 의존성 요청이나 검색 요청을 하는 경우 스프링은 FactoryBean.getObject() 메서드를 호출하여 반환받은 결과를 반환
- 가장 큰 역할은 트랜잭션 프록시와 JNDI 컨텍스트에서 자동으로 리소스를 가져오는 것
- FactoryBean을 사용하면 기본으로 제공되는 것보다 더 많은 리소스를 IoC를 사용해 관리 가능(생성자가 private이거나 클래스 정보를 미리 알 수 없는 클래스 등)
FactoryBean(s) 예제: MessageDigestFactoryBean
- 일반적으로 암호화를 위해 Hash 값을 얻으려고 하는 경우
MessageDigest md5 = MessageDigest.getInstance("MD5");
- FactoryBean을 구현하여 사용하는 경우
package ...
import java.security.MessageDigest;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
public class MessageDigestFatoryBean implements
FactoryBean<MessageDigest>, InitializingBean {
private String algorithmName = "MD5";
private MessageDigest messageDigest = null;
public MessageDigest getObject() throws Exception {
return messageDigest;
}
public Class<MessageDigest> getObjectType() {
return MessageDigest.class;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() throws Exception {
messageDigest = MessageDigest.getInstance(algorithmName);
}
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
}
}
- 스프링은 FactoryBean.getObject() 메서드를 호출하여 FactoryBean이 생성항 객체를 얻는다.
- 위 코드에서 MessageDigestFatoryBean은 InitializingBean의 afterPropertiesSet() 메서드에서 생성한 MessageDigest 인스턴스를 getObject() 메서드를 통해 반환한다.
- FactoryBean.getObjectType() 메서드를 통해 지정된 타입을 반환할 수 있다면 스프링은 FactoryBean 인스턴스를 자동와이어링 할 수 있다(가급적 인터페이스 타입으로 반환해야 한다).
- FactoryBean.isSingleton() 메서드는 FactoryBean 자신이 아니라 FactoryBean이 관리하는 인스턴스를 싱글턴으로 사용할 것인지 알려준다.
...
public class MessageDigester {
private MessageDigest digest1;
private MessageDigest digest2;
public void setDigest1(MessageDigest digest1) {
this.digest1 = digest1;
}
public void setDigest1(MessageDigest digest2) {
this.digest1 = digest2;
}
public void digest(String msg) {
System.out.println("digest1 사용");
digest(msg, digest1);
System.out.println("digest2 사용");
digest(msg, digest2);
}
private void digest(String msg, MessageDigest digest) {
System.out.println("사용하는 알고리즘 : " + digest.getAlgorithm());
digest.reset();
byte[] bytes = msg.getBytes();
byte[] out = digest.digest(bytes);
System.out.println(out);
}
}
<beans...>
<bean id="shaDigest" class="...MessageDigestFactoryBean"
p:algorithmName="SHA1" />
<bean id="defaultDigest" class="...MessageDigestFactoryBean" />
<bean id="digester" class="...MessageDigester"
p:digest1-ref="shaDigest"
p:digest2-ref="defaultDigest" />
</beans>
...
public class MessageDigestDemo {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring/app-context-xml.xml");
ctx.refresh();
MessageDigester digester = ctx.getBean("digester", MessageDigest.class);
digester.digest("Hello World!");
ctx.close();
}
}
- 이와 같이 직접 MessageDigest 인스턴스를 생성하지 않고 스프링으로부터 의존성을 주입받아 사용할 수 있다.
자바 구성 클래스로 FactoryBean 구성
- 자바 구성 클래스를 이용할 경우 컴파일러가 적절한 타입으로 프로퍼티를 설정하도록 제한하기 때문에 getObject() 메서드를 명시적으로 호출해야 한다.
...
public class MessageDigesterConfigDemo {
@Configuration
static class MessageDigesterConfig {
@Bean
public MessageDigestFactoryBean shaDigest() {
MessageDigestFactoryBean factoryOne = new MessageDigestFactoryBean();
factoryOne.setAlgorithmName("SHA1");
return factoryOne;
}
@Bean
public MessageDigestFactoryBean defaultDigest() {
return new MessageDigestFactoryBean();
}
@Bean
MessageDigester digester() throws Exception {
MessageDigester messageDigester = new MessageDigester();
messageDigester.setDigest1(shaDigest().getObject());
messageDigester.setDigest1(defaultDigest().getObject());
return messageDigester;
}
}
public static void main(String... args) {
GenericApplicationContext ctx =
new AnnotationConfigApplicationContext(MessageDigesterConfig.class);
MessageDigester digester = (MessageDigester) ctx.getBean("digester");
digester.digest("Hello World!");
ctx.close();
}
}