[spring]從頭開始-FactoryBeans
開發工具:IntelliJ IDEA 13
開發環境:jdk1.6.0_45
Framework:spring 3
FactoryBean 一般都是運用在無法使用 spring 的 inject 來產生 instance 的狀況(無法使用 new 來產生實體的對象)。直接來看一下範例:
MessageDigestFactoryBean.java
package foo.bar;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import java.security.MessageDigest;
/**
* Created by Hsu on 2014/7/15.
*/
public class MessageDigestFactoryBean implements FactoryBean<MessageDigest>,InitializingBean {
private String algorithmName = "MD5";
// 於 java.security 關於安全性的編碼類別
private MessageDigest messageDigest = null;
@Override
public MessageDigest getObject() throws Exception {
return this.messageDigest;
}
@Override
public Class<MessageDigest> getObjectType() {
return MessageDigest.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
messageDigest = MessageDigest.getInstance(algorithmName);
}
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
}
}
可以看到上述的類別使用 InitializingBean來做初始化bean的動作,並且implement FactoryBean<T>這個類別,FactoryBean 簡單說就是一個用來幫我們產生實體用的工廠,其中需要複寫三個方法,getObject 返回<T>的實體(由FactoryBean產生,即為範例中的afterPropertiesSet的 MessageDigest.getInstance(algorithmName) 產生),getObjectType 返回 <T>的型別,isSingleton 告知spring 該物件是否為 Singleton 實例。
接著我們新建一個bean
MessageDigester.java
package foo.bar;
import java.security.MessageDigest;
/**
* Created by Hsu on 2014/7/15.
*/
public class MessageDigester {
private MessageDigest digest1 = null;
private MessageDigest digest2 = null;
public void setDigest1(MessageDigest digest1) {
this.digest1 = digest1;
}
public void setDigest2(MessageDigest digest2) {
this.digest2 = digest2;
}
public void digest(String msg) {
System.out.println("Using digest1");
digest(msg, digest1);
System.out.println("Using digest2");
digest(msg, digest2);
}
private void digest(String msg, MessageDigest digest) {
//取得編碼規則
System.out.println("Using alogrithm: " + digest.getAlgorithm());
//重置
digest.reset();
byte[] bytes = msg.getBytes();
//編碼
byte[] out = digest.digest(bytes);
System.out.println(out);
}
}
單純在專案中加入這個類別,應該還看不出個所以然,我們再把 spring configuration file 加上去:
spring-config.xml
..
..標頭的部分省略
<bean id="shaDigest" class="foo.bar.MessageDigestFactoryBean">
<property name="algorithmName">
<value>SHA1</value>
</property>
</bean>
<bean id="defaultDigest" class="foo.bar.MessageDigestFactoryBean"/>
<bean id="digester" class="foo.bar.MessageDigester">
<property name="digest1">
<ref local="shaDigest"/>
</property>
<property name="digest2">
<ref local="defaultDigest"/>
</property>
</bean>
..結尾省略
這邊先將 bean shaDigest Inject 編碼方式為 SHA1,另一個 digester 則使用系統預設 MD5,接下來我們建立一個演繹的 MessageDigestExample類別;
MessageDigestExample.java
package foo.bar;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* Created by Hsu on 2014/7/15.
*/
public class MessageDigestExample {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring-config.xml");
ctx.refresh();
MessageDigester digester = (MessageDigester) ctx.getBean("digester");
digester.digest("Hello World!");
}
}
結合起上面的程式,我們可以看到我們藉由 MessageDigestFactoryBean 來產生我們所要的編碼實體,運行之後所得到的結果為:
Using digest1
Using alogrithm: SHA1
[B@33682598
Using digest2
Using alogrithm: MD5
[B@7a7c3885
整個程式的流程可以解釋為,腦中先構想什麼類別(MessageDigest)要由 FactoryBean 來產生,依照什麼樣的條件(MessageDigester)產生什麼樣的實例,後續要怎麼樣使用(MessageDigestExample),比如說有哪些票卷(實例),如何產生(factory),票卷要怎麼用(商業邏輯)...這樣的思考流程。
ps.from Pro Spring 3
FactoryBeans are the perfect solution when you are working with classes that cannot be created
using the newoperator. If you work with objects that are created using a factory method and you want to
use these classes in a Spring application, then create a FactoryBeanto act as an adaptor, allowing your
classes to take full advantage of Spring’s IoC capabilities.
直接使用 FactoryBean?當然可以,就像買家具一樣,你當然也可以選擇直接去工廠買啊~
直接來看看範例:
AccessingFactoryBeans.java
package foo.bar;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.security.MessageDigest;
/**
* Created by Hsu on 2014/7/15.
*/
public class AccessingFactoryBeans {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring-config.xml");
ctx.refresh();
// 對照組
//MessageDigest digest = (MessageDigest) ctx.getBean("shaDigest");
// 要取得 factoryBean 實體,需在 beanName錢加上 & 運算子
// 否則將發生 Exception in thread "main" java.lang.ClassCastException:
// java.security.MessageDigest$Delegate cannot be cast to foo.bar.MessageDigestFactoryBean
MessageDigestFactoryBean factoryBean =
(MessageDigestFactoryBean) ctx.getBean("&shaDigest");
try {
MessageDigest shaDigest = factoryBean.getObject();
System.out.println(shaDigest.digest("Hello world".getBytes()));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
配置檔同上一個範例,這邊就不再贅述。
運行的結果為:
[B@7162e295
一樣成功得到我們們所想要的結果。
我們的第一個例子是我們知道如何產生bean實體的時候所用的方法,但如果是第三方的class,我們已經知道產生他的實體Method之後,我們有更快速的方式可以用spring來完成相同的效果,在<bean>中使用 factory-bean 及 factory-method,立馬來看看範例:
MessageDigestFactory.java
package foo.bar;
import java.security.MessageDigest;
/**
* Created by Hsu on 2014/7/15.
*/
public class MessageDigestFactory {
private String algorithmName = "MD5";
public MessageDigest createInstance() throws Exception {
return MessageDigest.getInstance(algorithmName);
}
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
}
}
再加上spring configuration file
factoryMethod.xml
..
..標頭的部分省略
<bean id="shaDigestFactory"
class="foo.bar.MessageDigestFactory">
<property name="algorithmName">
<value>SHA1</value>
</property>
</bean>
<bean id="defaultDigestFactory"
class="foo.bar.MessageDigestFactory"/>
<bean id="shaDigest"
factory-bean="shaDigestFactory"
factory-method="createInstance">
</bean>
<bean id="defaultDigest"
factory-bean="defaultDigestFactory"
factory-method="createInstance"/>
<bean id="digester"
class="foo.bar.MessageDigester">
<property name="digest1">
<ref local="shaDigest"/>
</property>
<property name="digest2">
<ref local="defaultDigest"/>
</property>
</bean>
..結尾省略
可以看到之先前不同的是,多了個指定 factry-bean / factory-method,直接指定產生實例的bean及Method,不需要再像 MessageDigestFactoryBean 做些多餘的繁瑣過程,接下來在來產生一個演繹類別 MessageDigestFactoryExample.java
package foo.bar;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* Created by Hsu on 2014/7/15.
*/
public class MessageDigestFactoryExample {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:factoryMethod.xml");
ctx.refresh();
MessageDigester digester = (MessageDigester) ctx.getBean("digester");
digester.digest("Hello World!");
}
}
上面運行的結果基本上與第一個範例相同:
Using digest1
Using alogrithm: SHA1
[B@290fd7f6
Using digest2
Using alogrithm: MD5
[B@4f2b6c89
要使用哪一種,就視情況而定囉!!!