您的位置:首页 > 编程语言 > Java开发

Spring学习04--注入、依赖、方法注入详解

2016-12-21 22:34 423 查看
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

否则执行将会报错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring