1、构造方法注入时,经常使用构造器参数的索引进行注入
<bean id="fundAcco" class="com.errol.dao.FundAcco">
<constructor-arg index="0" value="212123"/>
<constructor-arg index="1" value="1"/>
<constructor-arg index="2" ref="tradeAcco"/>
</bean>
使用这种方式的好处是不需要关注参数的类型是什么,只需要知道参数的下标(从0开始),容器就能自动地去匹配构造方法中的参数,使之实例化。
这种注入的方式是IOC的首选。
2、idref元素
使用这个元素可以将容器内的其他bean的id传给或者元素,并且提供错误验证功能。验证功能指的的是,容器在部署时,会验证所引用的bean是否存在。
<bean id="fundAcco" class="com.errol.dao.FundAcco">
<constructor-arg name="vc_fundacco" value="212123"/>
<constructor-arg name="i_state" value="1"/>
<constructor-arg name="vc_fundacco">
<idref bean="tradeAcco"/>
</constructor-arg>
</bean>
注意到idref标签内使用的是bean属性,如果目标bean与这个bean在同一个xml文件中,idref标签可以改成:
<idref local="tradeAcco"/>
两者的效果一致
这里我测试发现,上面的构造方法参数索引注入和idref标签不能一起用
3、depends-on
描述一个bean依赖另一个bean最直接的方法是ref标签,或者上面的idref,但有时候,两个bean之间的依赖关系并不是那么直接,例如,当类中的静态块的初始化时,如数据库驱动的注册,这个时候depends-on属性可以用于当前Bean初始化之前强制性地制造一个或者多个被引用的bean。
<bean id="custInfo" class="com.errol.dao.Custinfo" depends-on="tradeAcco"></bean>
4、自动装配
一共有5中形式
no 不使用自动装配。必须通过ref元素指定依赖,这是默认设置。由于显式指定协作者可以使配置更灵活、更清晰,因此对于较大的部署配置,推荐采用该设置。而且在某种程度上,它也是系统架构的一种文档形式。
byName 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。例如,在bean定义中将autowire设置为by name,而该bean包含master属性(同时提供setMaster(..)方法),Spring就会查找名为master的bean定义,并用它来装配给master属性。
byType 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果你不希望这样,那么可以通过设置dependency-check=”objects”让Spring抛出异常。
constructor 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
autodetect 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
6、依赖检查dependency-check
Spring能够对bean定义中的属性值进行检查,属性dependency-check默认为none,也就是不检查,当有需要检查的场景时,在bean标签中加上dependency-check属性可以实现不同的检查方法:
none 没有依赖检查,如果bean的属性没有值的话可以不用设置。
simple 对于原始类型及集合(除协作者外的一切东西)执行依赖检查
object 仅对协作者执行依赖检查
all 对协作者,原始类型及集合执行依赖检查
7、方法注入
这里比较重要
在大部分情况下,容器中的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实例。看下下面这个例子,其中故意使用了这种方法:
首先定义了两个类。Tainfo , Agency
public class TaInfo {
}
其中,Agency类中有一个方法用于产生Tainfo,也即是说,Agency是singleton,而Tainfo是prototype,那么,代码是这样的:
public class Agency implements BeanFactoryAware{
private BeanFactory beanFactory;
public BeanFactory getBeanFactory() {
return beanFactory;
}
public void getTA(){
TaInfo ta = new TaInfo();
System.out.println(ta.toString());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
实现BeanFactoryAware接口时,默认要重写setBeanFactory(BeanFactory beanFactory)方法,也就是说,我们需要将factory传到我们的Agency类中,于是定义了一个成员变量BeanFactory beanFactory,方法中将beanFactory注入。
下面是关键点,xml文件如何配置?
<bean id="tainfo" class="com.errol.dao.TaInfo" scope="prototype"></bean>
<bean id="agency" class="com.errol.dao.Agency" scope="singleton">
<!-- <property name="beanFactory" ref="factory"></property> -->
</bean>
就这么简单,注意我注释的地方,刚开始以为,Agency类中的成员变量BeanFactory 也需要依赖注入,后来经过不断的尝试和测试发现,继承了接口,BeanFactory 将会自动注入,前提是bean是单例的(singleton)并且不延迟加载(lazy-init=false);
测试类:
@Test
public void testSingleton2Prototype(){
BeanFactory conFactory = new ClassPathXmlApplicationContext(configLocation);
//BeanFactory af = (BeanFactory) conFactory.getBean("agencyFactory");
Agency agency = (Agency)conFactory.getBean("agency");
agency.getTA();
agency.getTA();
}
得到输出值
com.errol.dao.TaInfo@2be2befa
com.errol.dao.TaInfo@33682598
发现两次获取的Tainfo不相同,也就是说我们的singleton类型的Agency类成功获取到了prototype类型的Tainfo对象。
但是,这样做的弊端是代码于Spring耦合了,不符合我们的初衷,所以,Spring就提供了更简单的方式达到上面这种情况的目的,也就是Lookup方法注入
Lookup方法注入利用了容器的覆盖受容器管理的bean方法的能力,从而返回指定名字的bean实例。在上述场景中,Lookup方法注入适用于原型bean(尽管它也适用于singleton bean,但在那种情况下直接注入一个实例就够了)。Lookup方法注入的内部机制是Spring利用了CGLIB库在运行时生成二进制代码功能,通过动态创建Lookup方法bean的子类而达到复写Lookup方法的目的。
还是上面的两个类(Agency类不再需要实现BeanFactoryAware接口)
public abstract class Agency /*implements BeanFactoryAware*/{
private TaInfo ta;
public TaInfo getTa() {
return ta;
}
public void setTa(TaInfo ta) {
this.ta = ta;
}
public abstract TaInfo createta();;
}
这个类的重点是,抽象类,和抽象方法,如果不是抽象的,不行。
配置
<bean id="tainfo" class="com.errol.dao.TaInfo" scope="prototype"></bean>
<bean id="agency" class="com.errol.dao.Agency" scope="singleton">
<lookup-method name="createta" bean="tainfo"/>
<property name="ta" ref="tainfo"></property>
</bean>
来看测试,先不用方法注入的形式获取tainfo
Agency agency = (Agency)conFactory.getBean("agency");
//System.out.println(agency);
TaInfo t1= agency.getTa();
System.out.println(t1);
TaInfo t2= agency.getTa();
System.out.println(t2);
System.out.println(t1==t2);
结果不出所料,取的tainfo是同一个
com.errol.dao.TaInfo@5e4be2cc
com.errol.dao.TaInfo@5e4be2cc
true
再看用lookup-method方法注入
Agency agency = (Agency)conFactory.getBean("agency");
//System.out.println(agency);
TaInfo t1= agency.createta();
System.out.println(t1);
TaInfo t2= agency.createta();
System.out.println(t2);
System.out.println(t1==t2);
每次获取出来的Tainfo对象都不一样,这样就达到了我们的目的
com.errol.dao.TaInfo@b554d32
com.errol.dao.TaInfo@d3f136e
false
这里尤其要注意的是,如果需要用lookup-method标签进行方法注入,需要导入一个CGLIB的Jar包
cglib-nodep-2.1_3.jar
否则执行将会报错。