1. 程式人生 > >Spring通過工廠建立例項的注意事項

Spring通過工廠建立例項的注意事項

如果第三方(or別的team)提供一個工廠類(此類是不可以修改的,往往以jar包形式提供的),需要供給我們專案來使用。

但是我們自己的專案使用了spring來配置,所以我們當然希望能夠通過spring的aop來配置這個工廠類來,來建立例項以進行引用。

但是這個工廠類的原始碼比較特殊。如下:

package x.y;

import java.util.Properties;

import x.y.client.CmdClient;
import x.y.client.InternalCmdClient;

public class ClientFactory {
	private static volatile CmdClient			cmdclient;
	private static volatile InternalCmdClient	cmdinternalclient;
	private static Properties					props	= new Properties();

	public static void setEnv(String env){
		props.put( "env", env );
	}

	

	public static CmdClient getCmdClient() throws Exception{
		if ( cmdclient == null ) {
			synchronized ( CmdClientImpl.class ) {
				if ( cmdclient == null ) {
					cmdclient = new CmdClientImpl( props );
				}
			}
		}
		return cmdclient;
	}

	public static InternalCmdClient getIntenalCmdClient() throws Exception{
		if ( cmdinternalclient == null ) {
			synchronized ( InternalCmdClientImpl.class ) {
				if ( cmdinternalclient == null ) {
					cmdinternalclient = new InternalCmdClientImpl( props );
				}
			}
		}
		return cmdinternalclient;
	}

	public static void destoryClient(){
		if ( cmdclient != null ) {
			cmdclient.destroy();
			cmdclient = null;
		}

		if ( cmdinternalclient != null ) {
			cmdinternalclient.destroy();
			cmdinternalclient = null;
		}
	}

}

介紹如上工廠類的特殊性:

  1. 所有的方法屬性都是靜態的。
  2. setEnv() 是靜態方法:內部是往靜態屬性props設值。
  3. 兩個工廠方法,是靜態空參方法(這是符合工廠方法定義的)。內部的實現是根據已經設定好的props來例項化物件,如果引數(env)沒有設值,那麼例項化物件就會失敗。所以我們就需要先setEnv,在通過工廠方法例項化物件。
一:有人會說,這個很簡單,根據spring的官方文件,用“靜態工廠方法”進行例項化。但是此方法不可取,如下所述:
	<bean id="internalClient" class="x.y.ClientFactory"
		factory-method="getIntenalCmdClient" destroy-method="destroy">
		<property name="env" value="${env}" />
	</bean>

如上配置,例項化internalClient會失敗!原因:
  1. spring雖然會呼叫setEnv()方法。但是並不是呼叫x.y.ClientFactory類裡面的此方法,而是呼叫internalClient例項裡面的setEnv()方法。所以此<property>的配置是無效的。
  2. 另外工廠方法getIntenalCmdClient會被首先呼叫(可以debug斷點),然後spring再呼叫setEnv方法。因此在呼叫getIntenalCmdClient時候沒有任何env引數被傳入。
  3. 以上兩條都會導致例項化失敗!

二:會有人說既然“靜態工廠化方法”不可取,那麼spring官方文件裡面還提供了“例項工廠方法

”。但是此方法也是不可取的,如下所述:

	<bean id="clientFactory"
		class="x.y.ClientFactory">
		<property name="env" value="${env}" />
	</bean>
	<bean id="internalClient" factory-bean="clientFactory"
		factory-method="getIntenalCmdClient" destroy-method="destroy">
	</bean>
如上配置,例項化internalClient會失敗!原因:
  1. 例項化clientFactory,其目的是為了讓spring呼叫setEnv方法,往x.y.ClientFactory類裡面設值(靜態屬性env設值),因為設定了factory-bean屬性,所以clientFactory的例項化順序會先於internalClient。到目前沒有什麼問題,但是問題出現在第二點
  2. 工廠方法gentIntenalCmdClient是靜態方法,設定了factory-bean屬性的含義就是clientFactory.getIntenalCmdClient如此呼叫,在spring裡面不支援物件引用靜態方法!因此是報錯的。
  3. spring報錯log:“No matching factory method found: factory bean 'clientFactory'; factory method 'getIntenalCmdClient()'. Check that a method with the specified name exists and that it is non-static.
綜上兩種情況,我想出瞭解決方法。
  1. 例項化clientFactory bean還是需要的,因為必須要保證env可以最先set值(靜態方法setEnv()可以最先被呼叫)。
  2. 例項化internalClient bean使用"靜態工廠方法",如此就可以成功的呼叫靜態工廠方法(ClientFactory.getIntenalCmdClient())
  3. 為保證先setEnv,再呼叫工廠方法(ClientFactory.getIntenalCmdClient()),可以使用depends-on屬性
  4. destroy-method所設值的方法,是internalClient例項所能引用到的方法。
配置如下:
<bean id="internalClient" class="x.y.AhClientFactory"
		factory-method="getIntenalCmdClient" destroy-method="destroy"
		depends-on="clientFactory">
	</bean>
	<bean id="clientFactory" class="x.y.ClientFactory">
		<property name="env" value="${env}" />
	</bean>

如有不對的or更好的解決方案,望大哥賜教!

BTW:

   例項工廠和靜態工廠一樣的相同限制:
  1. 靜態工廠方法上不能有引數,也不能在Spring種定義靜態工廠方法的引數。
  2. 靜態工廠方法只能是public的,不能是private或protected的。
  3. 靜態工廠方法不能和建構函式注入一起使用。
    例項工廠和靜態工廠不同的點:
  • 例項工廠方法不能是靜態的,而靜態工廠方法必須是靜態的。