Spring框架之自动装配
Spring的IoC容器通过Java反射机制了解了容器中所存在Bean的配置信息,这包括构造方法的结构,属性的信息,而正是由于这个原因,Spring容器才能通过某种规则来对Bean进行自动装配,而无须通过显式的方法进行配置。
一.自动装配类型:Spring IoC容器可以自动装配相互协作Bean之间的关联关系。因此,可以自动使Spring通过检查BeanFactory中的内容,来指定Bean协作(其它被依赖的Bean),,下面来介绍这4种类型:
1.byName类型:根据属性名自动装配。此类型将检查容器并根据名字查找与属性完全一样的bean,并将其与属性自动装配。
注:使用byName自动装配类型时,对于设置的属性名字必须提供set()方法,否则在启动Spring时,将会报出异常。
下面附上一个例子:
(1).第一步,新建一个Java项目,项目名为spring_byName,然后配置好spring环境即可。
(2).第二步,新建一个People类,放在com.bean包下,声明三个属性,分别为name,age,sex,并生成其setXxx()和getXxx()方法,在写一个方法get(),用于输出People各个属性值,具体看代码,如下:
<pre class="java" name="code">package com.bean; public class People { private String name; private int age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public void get() { System.out.print("姓名为:" + name); System.out.print(",年龄为:" + age); System.out.print(",性别为:" + sex); } }
(3).第三步,新建一个Student类,也放在com.bean包下,声明course课程这个属性以及上面People类的对象属性,并生成属性的setXxx()和getXxx()方法,在写一个get()方法,输出course这个属性值和调用People类中的get()方法,具体代码如下:
package com.bean; public class Student { private String course; private People people; public String getCourse() { return course; } public void setCourse(String course) { this.course = course; } public People getPeople() { return people; } public void setPeople(People people) { this.people = people; } public void get() { System.out.println("学生的课程为:" + course); people.get(); } }
(4).第四步,打开配置文件applicationContext.xml文件,只需要在<bean>标签中通过autowire属性设置为byName来启动自动装配,如下代码所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="people" class="com.bean.People"> <property name="name" value="张三"></property> <property name="age" value="23"></property> <property name="sex" value="男"></property> </bean> <bean id="student" class="com.bean.Student" autowire="byName"> <property name="course" value="Java程序设计"></property> </bean> </beans>
其中使用<property>标签中的所指定的name要与类中的属性名一一对应起来,然后在student这个bean里使用了byName自动装配类型,那么在peoplebean里的id属性值必须与Student类中所声明的People对象属性名一致!
(5).第五步,编写一个测试类Test,也放在com.bean包下,其中获取加载配置文件,然后通过ApplicationContext对象获取Student这个Bean,再调用这个Bean对象的get()方法,具体代码如下:
package com.bean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args){ ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); Student stu=(Student) ac.getBean("student"); stu.get(); } }
(6).第六步,运行效果如下:
这样便把People这个Bean的三个属性值自动装配进去了Student这个Bean中去了,所以调用get()方法也把People类的属性值也输出来了!
(7).如果我们把配置文件中的People这个Bean改成下面所示,即把id属性改成people1,如下:
<bean id="people1" class="com.bean.People"> <property name="name" value="张三"></property> <property name="age" value="23"></property> <property name="sex" value="男"></property> </bean>
其它代码不变,运行测试类Test之后,如下图所示:
这样就报空指针异常了,获取不到People类的属性名,所以使用byName自动装配类型的话,一个Bean中的id要与另一个Bean的对象属性名一一对应!
注:如果上面这个例子把自动装配类型改为byType的话,也是可以的,就算你People类在配置文件里配置bean的id属性任意都行,因为此时就是按照类型装配了!
2.byType类型:如果容器存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型的bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没找到相匹配的bean,则什么事都不发生。
(1).这里就不附上例子了,我们通过上面这个例子把autowire属性设置为byType,也可以运行成功,因为是按照类型匹配的,我们可以把配置文件改成如下所示,其它都不改:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="p" class="com.bean.People"> <property name="name" value="张三"></property> <property name="age" value="23"></property> <property name="sex" value="男"></property> </bean> <bean id="student" class="com.bean.Student" autowire="byType"> <property name="course" value="Java程序设计"></property> </bean> </beans>
运行之后效果与byName一样。
(2).如果我们把配置文件改成如下所示,我们配置两个People类,如下图所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="p" class="com.bean.People"> <property name="name" value="张三"></property> <property name="age" value="23"></property> <property name="sex" value="男"></property> </bean> <bean id="p1" class="com.bean.People"> <property name="name" value="李红"></property> <property name="age" value="18"></property> <property name="sex" value="女"></property> </bean> <bean id="student" class="com.bean.Student" autowire="byType"> <property name="course" value="Java程序设计"></property> </bean> </beans>
则会报一个错误,因为我们定义了两个类型都为People的bean,所以不能匹配到是哪一个bean,报错信息如下图所示:
即如果Bean采用byType进行自动装配,当IoC容器中存在多个类型匹配的Bean时,就无法判断究竟该选择哪个Bean作为自动装配的目标,所以就抛出上面的异常信息了!
(3).这里简单讲述一下Spring如何进行匹配入参,如果A类和B类,两者满足以下三种情况中的任何一种,可以称之A按类型匹配于B:
~A和B是相同的类型。 ~A是B的子类。 ~A实现了B的接口。
3.constructor类型:与byType类型相似,不同在于constructor类型应用于构造器参数。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常。
(1).其实使用constructor自动装配时,只不过是通过构造方法而进行自动装配的。
(2).下面附上一个例子:
第一步,首先创建一个Java项目,项目名称为spring_constructor,配置好Spring环境。
第二步,新建一个Man类,具体代码如下,就不做分析了,很简单:
package com.bean; public class Man { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void out(){ System.out.println(name); System.out.println(sex); System.out.println(age); } }
第三步,新建一个Building类,代码如下:
package com.bean; public class Building { private String name; private int floors; private Man m; public Building(String name,int floors,Man m){ this.name=name; this.floors=floors; this.m=m; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getFloors() { return floors; } public void setFloors(int floors) { this.floors = floors; } public Man getPerson() { return m; } public void setPerson(Man man) { this.m = man; } public void out(){ System.out.println(name); System.out.println(floors); m.out(); } }
Building类里有一个带三个参数的构造方法。
第四步,打开配置文件进行配置,配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="man" class="com.bean.Man"> <property name="name" value="A-Lc"></property> <property name="sex" value="男"></property> <property name="age" value="23"></property> </bean> <bean id="building" class="com.bean.Building" autowire="constructor"> <constructor-arg index="0" value="金大福"></constructor-arg> <constructor-arg index="1" value="88"></constructor-arg> </bean> </beans>
在配置文件中,把autowire属性设置为constructor来启动自动装配,然后再用<constructor-arg>标签把带的参数传进去,这里只传了两个,其中最后一个Man的对象属性参数通过匹配有Man类型的bean传进去。
第五步,新建Test测试类,代码如下:
package com.bean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.bean.Building; public class Test { public static void main(String[] args){ ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); Building b=(Building) ac.getBean("building"); b.out(); } }
第六步,运行后效果如下:
二.自动装配控制
1.在一个Spring应用中,Bean的数量很多,因此在使用自动装配时,如果容器中多个匹配项,Spring会抛出异常,不能正常工作。针对这种问题,可以对那些不需要匹配的Bean进行设置,设定这个Bean是否为被自动装配对象。当采用XML格式配置Bean时,可将<bean>元素的autowire-candidate属性设置为false,这样容器在查找自动装配对象时将不考虑该Bean,也就是这个Bean将不会被作为自动装配对象。
2.如果我们把上面constructor类型的自动装配例子的配置文件修改为下面所示,其它代码不改:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="man" class="com.bean.Man"> <property name="name" value="A-Lc"></property> <property name="sex" value="男"></property> <property name="age" value="23"></property> </bean> <bean id="man1" class="com.bean.Man"> <property name="name" value="A-Xg"></property> <property name="sex" value="男"></property> <property name="age" value="23"></property> </bean> <bean id="building" class="com.bean.Building" autowire="constructor"> <constructor-arg index="0" value="金大福"></constructor-arg> <constructor-arg index="1" value="88"></constructor-arg> </bean> </beans>
定义两个类型相同的bean,此时IoC容器不知道匹配哪一个 ,所以会报下图的错:
3.我们可以将上面的代码的一个不需要匹配的bean的autowire-candidate属性设置为false,如下代码所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="man" class="com.bean.Man" autowire-candidate="false"> <property name="name" value="A-Lc"></property> <property name="sex" value="男"></property> <property name="age" value="23"></property> </bean> <bean id="man1" class="com.bean.Man"> <property name="name" value="A-Xg"></property> <property name="sex" value="男"></property> <property name="age" value="23"></property> </bean> <bean id="building" class="com.bean.Building" autowire="constructor"> <constructor-arg index="0" value="金大福"></constructor-arg> <constructor-arg index="1" value="88"></constructor-arg> </bean> </beans>
4.此时运行效果就输出姓名为A-Xg的信息了,如下图所示:
三.使用自动装配的前提:使用自动装配,主要是为了方便,提高工作效率,但是要正确并合理地使用自动装配,必须先理解自动装配的优缺点,才能正确判断何时需要使用自动装配。
1.自动装配的优点如下:
(1).自动装配能显著减少装配的数量,因此在配置数量相当多时采用自动装配,可以减少工作量。
(2).自动装配可以使配置与Java代码同步更新。例如:如果需要给一个Java类增加一个依赖,那么该依赖将自动实现而不需要修改配置。因此强烈在开发过程中采用自动装配,而在系统趋于稳定的时候改为显式装配的方式。
2.虽然自动装配具有上面这些优点,但不是说什么时候都可以使用它,因为它还有如下一些缺点:
(1).尽管自动装配比显式装配更神奇,但是,Spring会尽量避免在装配不明确时进行猜测,因为装配不明确可能出现难以预料的结果,而Spring所管理的对象之间的关联关系也不再能清晰地进行文档化。
(2).对于那些根据Spring配置文件生成文档的工具来说,自动装配将会使这些工具无法生成依赖信息。
3.决定是否使用自动装配方式时,没有绝对的对错。考虑项目的实际是最好的方法,例如对于大型的应用,不建议使用自动装配。虽然自动装配可以减少配置文件的工作量,但是大大降低了依赖关系的清晰度和透明度。由于依赖关系的配置基于源文件的属性名,这就导致Bean与Bean之间的耦合降低到代码层次,不利于高层次解耦。
以上内容仅供大家学习参考,写得不好,请见谅,如有错误,请指出,谢谢!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。