使用方法的返回值进行注入
本节要讲的第三个类是MethodInvokingFactoryObject。MethodInvokingFactoryObject允许使用任意方法的返回值进行注入。
MethodInvokingFactoryObject类可以处理实例方法和静态方法。此外,Spring.NET中另有一种处理对象初始化的机制(参考4.5.1.1,IInitializingObject接口和init-method属性)也可以用来进行(初始化)方法的调用,但是用这种机制调用方法的目的只是进行初始化工作,并且不允许向方法中传递任何参数,调用的时机也被限制在对象被容器创建时。MethodInvokingFactoryObject类则允许在任意时刻调用任意对象的任意方法(或者任意类的静态方法)。
在下面的例子中,使用MethodInvokingFactoryObject类强制在myService对象创建之前调用一个静态方法。
<object id="force-init"
type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="TargetMethod" value="Initialize">
<property name="TargetType" value="ExampleNamespace.ExampleInitializerClass, ExampleAssembly">
</object>
<object id="myService" type="MyService" depends-on="force-init"/>
(按:原文中上面的例子似有错误,此处已做修正;具体可参考英文文档)
注意对象定义myService的depends-on属性引用了一个名为force-init的MethodInvokingFactoryObject对象,该引用会使对象force-init在myService之前初始化(并且调用force-init的目标类型上的目标方法。注意,若要使这个配置正常工作,myService对象必须以singleton模式操作(按:否则myService对象只会在请求时创建,而此时force-init已经被容器预先创建了,也就失去了depends-on的意义)——这也是默认的对象布署模式,参见下一章)。
MethodInvokingFactoryObject类也可用于访问工厂方法(按:当在代码中通过名称"force-init"从容器中获取对象时,如果其目标方法有返回值,那么获得的对象就是目标方法的返回值;如果目标方法返回null,那么获得对象的类型则是System.Reflecton.Missing),一般情况下,工厂方法都是以singleton模式操作的。如果MethodInvokingFactoryObject对象为singleton模式,那么在它所有属性被设置后(按:应该是设置了足够的属性后,即TargetType和TargetMethod),目标方法会立即被调用并将返回值存入缓存以备用。随后,如果容器向此工厂请求对象,那么返回的将是缓存中的值。MethodInvokingFactoryObject对象的singleton属性可以设置为false,此时每次请求对象都会去调用目标方法(返回值不会被缓存)。
在MethodInvokingFactoryObject的对象定义中,用TargetMethod和TargetType属性指定目标静态方法名和方法所在的类型全名;而用TargetMethod和TargetObject属性来指定目标实例方法名和方法所在对象的引用。
目标方法的参数可以通过两种方式来设置(这两种方式可混合使用)。第一种是通过Arguments属性来指定参数列表。注意列表中项的顺序是很重要的——必须和方法签名中的参数列表在次序上完全一致,且类型兼容,如下例:
<object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetType" value="Whatever.MyClassFactory, MyAssembly"/> <property name="TargetMethod" value="GetInstance"/> <!-- the ordering of arguments is significant --> <property name="Arguments"> <list> <value>1st</value> <value>2nd</value> <value>and 3rd arguments</value> <!-- automatic Type-conversion will be performed prior to invoking the method --> </list> </property> </object>
第二种方式是通过NamedArguments属性配置一系列键/值对,其中键名对应参数名(文本类型),值对应参数值(可以是任意对象)。参数名是大小写不敏感的,并且顺序(当然)是不重要的(因为词典类型本身就没有顺序)。见下面的例子:
<object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject"> <object type="Whatever.MyClassFactory, MyAssembly"/> </property> <property name="TargetMethod" value="Execute"/> <!-- the ordering of named arguments is not significant --> <property name="NamedArguments"> <dictionary> <entry key="argumentName"><value>1st</value></entry> <entry key="finalArgumentName"><value>and 3rd arguments</value></entry> <entry key="anotherArgumentName"><value>2nd</value></entry> </dictionary> </property> </object>
下面使用MethodInvokingFactoryObject来调用一个实例方法:
<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject" ref="myMethodObject"/> <property name="TargetMethod" value="Execute"/> </object>
上例中的目标对象也可以是匿名的内联对象定义...如果对象的方法不会在工厂对象之外调用,那么最好通过内联对象定义将目标方法限制在工厂对象内部。
最后要注意,如果使用MethodInvokingFactoryObject来调用一个有变长参数列表的方法,必须使用list来按顺序定义要传递的参数值。请看下面的例子,其中CreateObject方法的参数arguments是用C#关键字params来定义的;随后是相应的XML配置:
[C#] public class MyClassFactory { public object CreateObject(Type objectType, params string[] arguments) { return ... // implementation elided for clarity... } }
<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> <object id="paramsMethodObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject" ref="myMethodObject"/> <property name="TargetMethod" value="CreateObject"/> <property name="Arguments"> <list> <value>System.String</value> <!-- here is the 'params string[] arguments' --> <list> <value>1st</value> <value>2nd</value> </list> </list> </object>