三、使用depends-on属性
bean标签的depends-on属性,表示一个bean需要依赖另外的bean,但是他们之间没有很直接的从属关系(如一个bean是另一个bean的属性),使用了这个属性的bean在初始化本身之前,必须先初始化它depends-on的bean,如:
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> <property name="manager" ref="manager" /> </bean> <bean id="manager" class="ManagerBean" /> <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
四、bean的延迟初始化
默认情况下,Spring在初始化Ioc容器(ApplicationContext)的时候会初始化所有的单例bean。通过设置bean标签的lazy-init属性,可以设置一个bean不在Ioc容器初始化的时候也被初始化,而是在这个bean第一次使用的时候才进行初始化。如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="notlazy" class="com.foo.AnotherBean"/>
我们可以在容器级别来设置所有单例bean都使用延迟初始化,方法是通过使用beans根标签的default-lazy-init属性,如:
<beans default-lazy-init="true"> <!-- no beans will be pre-instantiated... --> </beans>
五、依赖的自动装配(Autowiring collaborators)
Spring可以自动在Ioc容器中检查bean之间的关系,进而自动地建立bean之间的依赖关系。自动装配bean有优点也有其不足之处,由用户自己决定是否使用这个功能。
自动装配的xml配置,是通过bean标签的autowire属性实现的。有五种autowire模式:
no --默认的,不自动装配,依赖通过常规的ref标签来配置
byName --通过属性名字自动装配,比如一个bean设置了通过byName方式自动装配,那么Spring会在Ioc容器中查找以这个bean的属性命名的其他bean,并作为这个bean的依赖注入。
byType --和byName类似,但是是通过属性的类型来查找依赖的。这必须要求在容器中最多只存在唯一一个这种类型的bean,否则会抛出异常。如果不存在,那么没有依赖被注入。
constructor --和byType类似,不过是根据构造函数的参数类型来查找依赖的,同样,一种类型的bean最多只能存在一个,否则有致命错误发生。
使用byType和constructor模式自动装配,你可以自动装配一个数组或者强类型的集合类,这种情况下,所有类型匹配的bean会作为依赖放到集合里去。如果自动装配一个强类型的Map,并且key的类型为String,那么容器中所有bean类型匹配Map的值类型的bean就会注入到这个Map,键值为对应的bean的名字。
设置bean标签的autowire-candidate属性为false,可以让一个bean不能作为候选的依赖自动装配到其他的bean中。
设置bean标签的primary属性为true,可以配置一个bean作为主要的候选依赖可以自动装配到其他bean中。
在beans根标签上设置default-autowire-candidate属性,可以指定容器中bean的命名符合某些模式的bean才参与自动装配,比如把该属性设置为*Repository,那么就只有bean的名字以Repository结尾的bean才可能被自动装配到别的bean中去。
六、方法注入 (Method Injection)
前面讲的都是bean与bean之间的依赖,即我们可以把一个bean注入到另一个bean中。除此之外,Spring还可以把一个bean的方法注入到另一个bean中去实现或者覆盖这个bean中方法。
(1)Lookup Method Injection: Spring可以利用CGLIB库的字节码生成能力来自动生成一个类的子类,并且通过注入一个方法来重写父类的抽象方法。
这种注入方式,必须要求父类及其需要重写的方法不能是final方法。Spring从3.2开始已经重写对CGLIB库进行了打包(在spring-core jar包里),不在需要在类路径下添加CGLIB的库。
举例如下:
package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // 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(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
通过以下xml配置,CommandManager类中的createCommand方法被AsyncCommand类的方法实现了,就是方法注入。Spring是通过动态生成了CommandManager的子类来实现的。
<!-- 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>
这种方法注入解决的现实问题就是,一个单例(singleton)bean与原型(prototype)bean之间的依赖。使得单例中每次需要原型bean的时候,容器都是重新初始化一个新的bean给单例bean使用。
(2)Arbitrary Method replacement: Spring还提供了一种不怎么常用的方法注入方式,那就是任意方法替换,用一个bean中方法,替换另一个bean中的方法。实现方法是如下:
假设下面这个类中方法需要替换:
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(String) * 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 ...; } }
最后在xml中配置如下:
<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"/>
可以定义多个<arg-type>标签元素来指定需要替换的方法的参数类型。为了方便,这个标签的值可以使用类型的简写形式,比如String, Str, java.lang.String都可以用来表示字符串类型参数。