bean的配置定义
引入idref
<property name="targetName">
<idref bean="theTargetBean" />
</property>
在运行时,它完全等同于
<property name="targetName">
<value>theTargetBean</value>
</property>
使用idref标记允许容器在部署时 验证所被引用的bean是否存在,此外,如果被引用的bean在同一XML文件内,且bean名字就是bean id,那么可以使用local属性,此属性允许XML解析器在解析XML文件时来对引用的bean进行验证
<!-- a bean with an id of 'theTargetBean' must exist, else an XML exception will be thrown -->
<idref local="theTargetBean"/>
上面的例子与在ProxyFactoryBean bean定义中使用<idref/>元素指定AOP interceptor的相同之处在于:如果使用<idref/>元素指定拦截器名字,可以避免因一时疏忽导致的拦截器ID拼写错误。
集合合并
2.0开始,Spring IoC容器将支持集合的合并。这样我们可以定义parent-style和child-style的<list/>、<map/>、<set/>或<props/>元素,子集合的值从其父集合继承和覆盖而来;也就是说,父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集全中对应的值。
强类型集合
public class Foo {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
//在foobean的accounts属性被注入之前,通过反射,利用强类型Map<String, Float>的泛型信息,Spring的底层类型转换机制将会把各种value元素值转换为Float类型,因此字符串9.99、2.75及3.99就会被转换为实际的Float类型。
XML-based configuration metadata shortcuts
Spring提供了简化格式用于替代<value/>和<ref/>元素。<property/>、<constructor-arg/>及<entry/>元素都支持value属性(attribute),它可以用来替代内嵌的<value/>元素
<property name="myProperty">
<value>hello</value>
</property>
等同于:
<property name="myProperty" value="hello"/>
<property name="myProperty">
<ref bean="myBean">
</property>
等同于:
<property name="myProperty" ref="myBean"/>
<entry>
<key>
<ref bean="myKeyBean" />
</key>
<ref bean="myValueBean" />
</entry>
等同于:
<entry key-ref="myKeyBean" value-ref="myValueBean"/>
组合属性名称
当设置bean的组合属性时,除了最后一个属性外,只要其他属性值不为null,组合或嵌套属性名是完全合法的。例如,下面bean的定义:
<bean id="foo" class="foo.Bar">
//foo bean有个fred属性,此属性有个 bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foo的fred属性及fred 的bob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常。
<property name="fred.bob.sammy" value="123" />
</bean>
使用depends-on
depends-on属性可以用于当前bean初始化之前显式地强制一个或多个bean被初始化
//表示依赖于多个bean,manager,accountDao 它们之间用逗号分开
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
延迟初始化bean
在XML配置文件中,延迟初始化将通过<bean/>元素中的lazy-init属性来进行控制。一个延迟初始化bean将告诉IoC 容器是在启动时还是在第一次被用到时实例化。
例如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
<!-- various properties here... -->
</bean>
如果Ioc容器在启动的时候创建了那些设置为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化singleton bean里面。
自动装配(autowire)
autowire的方便之处在减少或者消除属性或构造器参数的设置,这样可以给我们的配置文件减减肥!也可以针对单个bean设置其是否为被自动装配对象。当采用XML格式配置bean时,<bean/>元素的 autowire-candidate属性可被设为false,这样容器在查找自动装配对象时将不考虑该bean。
方法注入
在大部分情况下,容器中的bean都是singleton类型的。如果一个singleton bean要引用另外一个singleton bean,或者一个非singleton bean要引用另外一个非singleton bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个非singleton(prototype)类型的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例。
上述问题的一个解决办法就是放弃控制反转。通过实现BeanFactoryAware接口(见这里)让bean A能够感知bean 容器,并且在需要的时候通过使用getBean("B")方式(见这里)向容器请求一个新的bean B实例。看下下面这个例子,其中故意使用了
这种方法(一):
public class CommandManager implements BeanFactoryAware {
private BeanFactory beanFactory;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// the Command returned here could be an implementation that executes asynchronously, or whatever
protected Command createCommand() {
return (Command) this.beanFactory.getBean("command"); // notice the Spring API dependency
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
方法(二)Lookup方法注入Lookup
方法注入利用了容器的覆盖受容器管理的bean方法的能力,从而返回指定名字的bean实例。在上述场景中,Lookup方法注入适用于原型bean(尽管它也适用于singleton bean,但在那种情况下直接注入一个实例就够了)。Lookup方法注入的内部机制是Spring利用了CGLIB库在运行时生成二进制代码功能,通过动态创建Lookup方法bean的子类而达到复写Lookup方法的目的。
如果你看下上个代码段中的代码(CommandManager类),Spring容器动态覆盖了createCommand()方法的实现。你的CommandManager类不会有一点对Spring的依赖,在下面这个例子中也是一样的:
public class CommandManager {
public Object process(Object command) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// mmm, but where is the implementation of this method?
protected abstract CommandHelper createHelper();
}
在包含被注入方法的客户类中(此处是CommandManager),此方法的定义必须按以下形式进行:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
如果方法是抽象的,动态生成的子类会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。让我们来看个例子:
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>
在上面的例子中,标识为commandManager的bean在需要一个新的command bean实例时,会调用createCommand方法。重要的一点是,必须将command部署为原型。当然也可以指定为singleton,如果是这样的话,那么每次将返回相同的command bean实例!
Lookup方法注入既可以结合构造器注入,也可以与setter注入相结合。
请注意,为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里。 另外,Spring容器要子类化的类不能是final的,要覆盖的方法也不能是final的。 同样的,要测试一个包含抽象方法的类也稍微有些不同,你需要自己编写它的子类提供该抽象方法的桩实现。 最后,作为方法注入目标的bean不能是序列化的(serialized)。
提示
有兴趣的读者也许已经发现ServiceLocatorFactoryBean (在org.springframework.beans.factory.config包里)的用法和ObjectFactoryCreatingFactoryBean的有些相似, 不同的是它允许你指定自己的lookup接口,不一定非要用Spring的lookup接口,比如ObjectFactory。 要详细了解这种方法请参考ServiceLocatorFactoryBean的Javadocs(它的确减少了对Spring的耦合)。
方法(三)自定义方法的替代方案
比起Lookup 方法注入来,还有一种很少用到的方法注入形式,该注入能使用bean的另一个方法实现去替换自定义的方法。除非你真的需要该功能,否则可以略过本节。
当使用基于XML配置元数据文件时,可以在bean定义中使用replaced-method元素来达到用另一个方法来取代已有方法的目的。考虑下面的类,我们将覆盖computeValue方法:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
实现org.springframework.beans.factory.support.MethodReplacer接口的类提供了新的方法定义。
/** meant to be used to override the existing computeValue
implementation in MyValueCalculator */
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
下面的bean定义中指定了将要复写的方法以及执行替换处理的bean定义:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
在<replaced-method/>元素内可包含一个或多个<arg-type/>元素,这些元素用来标明被复写的方法签名。只有被复写(override)的方法存在重载(overload)的情况(同名的多个方法变体)才会使用方法签名。为了方便,参数的类型字符串可以采用全限定类名的简写。例如,下面的字符串都表示参数类型为java.lang.String。
java.lang.String
String
Str
参数的个数通常足够用来区别每个可能的选择,这个捷径能减少很多键盘输入的工作,它允许你只输入最短的匹配参数类型的字符串。
bean的作用域
Singleton作用域
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>
Prototype作用域
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)
其他作用域
request、session以及global session仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架)。
自定义作用域
在Spring 2.0中,Spring的bean作用域机制是可以扩展的。这意味着,你不仅可以使用Spring提供的预定义bean作用域; 还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)。