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之间的耦合降低到代码层次,不利于高层次解耦。





以上内容仅供大家学习参考,写得不好,请见谅,如有错误,请指出,谢谢!



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。