set注入和构造注入有时在做配置时比较麻烦。所以框架为了提高开发效率,提供自动装配功能,简化配置。spring框架式默认不支持自动装配的,要想使用自动装配需要修改spring配置文件中<bean>标签的autowire属性。
所谓自动装配,就是将一个Bean注入到其他Bean的Property中,类似于以下:
<bean id="customer" class="com.lei.common.Customer" autowire="byName" />
1. Auto-Wiring ‘no’
1、no
默认情况下,不自动装配,通过“ref”attribute手动设定。默认情况下,需要通过'ref’来装配bean,如下:
<bean id="customer" class="com.lei.common.Customer">
<property name="person" ref="person" />
</bean>
<bean id="person" class="com.lei.common.Person" />
2. Auto-Wiring ‘byName’
2、byName
从Spring环境中获取目标对象时,目标对象中的属性会根据名称在整个Spring环境中查找<bean>标签的id属性值。如果有相同的,那么获取这个对象,实现关联。
整个Spring环境:表示所有的spring配置文件中查找,那么id不能有重复的。
根据属性Property的名字装配bean,这种情况,Customer设置了autowire="byName",Spring会自动寻找与属性名字“person”相同的bean,找到后,通过调用setPerson(Person person)将其注入属性。
<bean id="customer" class="com.lei.common.Customer" autowire="byName" />
<bean id="person" class="com.lei.common.Person" />
如果根据 Property name找不到对应的bean配置,如下
<bean id="customer" class="com.lei.common.Customer" autowire="byName" />
<bean id="person_another" class="com.lei.common.Person" />
Customer中Property名字是person,但是配置文件中找不到person,只有person_another,这时就会装配失败,运行后,Customer中person=null。
3. Auto-Wiring ‘byType
3、byType
从Spring环境中获取目标对象时,目标对象中的属性会根据类型在整个spring环境中查找<bean>标签的class属性值。如果有相同的,那么获取这个对象,实现关联。
缺点:如果存在多个相同类型的bean对象,会出错。
如果属性为单一类型的数据,那么查找到多个关联对象会发生错误。
如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。
根据属性Property的数据类型自动装配,这种情况,Customer设置了autowire="byType",Spring会总动寻找与属性类型相同的bean,找到后,通过调用setPerson(Person person)将其注入。
<bean id="customer" class="com.lei.common.Customer" autowire="byType" />
<bean id="person" class="com.lei.common.Person" />
如果配置文件中有两个类型相同的bean会怎样呢?如下:
<bean id="customer" class="com.lei.common.Customer" autowire="byType" />
<bean id="person" class="com.lei.common.Person" />
<bean id="person_another" class="com.lei.common.Person" />
一旦配置如上,有两种相同数据类型的bean被配置,将抛出UnsatisfiedDependencyException异常,见以下:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException:
所以,一旦选择了’byType’类型的自动装配,请确认你的配置文件中每个数据类型定义一个唯一的bean。
4. Auto-Wiring ‘constructor’
4、constructor(3.x以上已不能用)
使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。
如果没找到则抛出异常
这种情况下,Spring会寻找与参数数据类型相同的bean,通过构造函数public Customer(Person person)将其注入。
<bean id="customer" class="com.lei.common.Customer" autowire="constructor" />
<bean id="person" class="com.lei.common.Person" />
5、autodetect
自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
这种情况下,Spring会先寻找Customer中是否有默认的构造函数,如果有相当于上边的’constructor’这种情况,用构造函数注入,否则,用’byType’这种方式注入,所以,此例中通过调用public Customer(Person person)将其注入。
<bean id="customer" class="com.lei.common.Customer" autowire="autodetect" />
<bean id="person" class="com.lei.common.Person" />
注意:
项目中autowire结合dependency-check一起使用是一种很好的方法,这样能够确保属性总是可以成功注入。
<bean id="customer" class="com.lei.common.Customer"
autowire="autodetect" dependency-check="objects" />
<bean id="person" class="com.lei.common.Person" />
<bean>标签的 autowire 属性,它负责自动装配<bean>标签定义 JavaBean 的属性。这样做可以省去很多配置 JavaBean 属性的标签代码,使代码整洁、美观。但是它也有负面影响,即使用自动装配之后,无法从配置文件中读懂 JavaBean 需要什么属性。自动装配存在很多不正确的装配问题,例如错误装载属性、“byType”属性和“constructor”属性对相同类型参数无法判断等。当然,将自动装配和手动装配混合使用也能解决此问题。下面通过一个实例来分析如何使用自动装配。
首先创建创建一个学生类 Student,定义学号、姓名、性别、年龄等属性,并添加对应的 set()与 get()方法。程序代码如下。
- package com.autobean;
- public class Student {
- private String ID;
- private String name;
- private int age;
- private String sex;
- public String getID() {
- return ID;
- }
- public void setID(String iD) {
- ID = iD;
- }
- 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;
- }
- }
package com.autobean;
public class Student {
private String ID;
private String name;
private int age;
private String sex;
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
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;
}
}
同样创建一个教师类 Teacher,定义姓名、性别和年龄等属性,并添加对应的 set()与 get()方法。程序代码如下。- package com.autobean;
- public class Teacher {
- 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;
- }
- }
package com.autobean;
public class Teacher {
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;
}
}
创建教学档案类 TeachFile,定义 Teacher 和 Student 两个属性,并添加 print()方法。用于输出教师与学生的信息。程序代码如下。
- package com.autobean;
- public class TeachFile {
- private Teacher teacher;
- private Student student;
- public TeachFile() {
- }
- public TeachFile(Teacher teacher, Student student) {
- this.teacher = teacher;
- this.student = student;
- }
- public Student getStudent() {
- return this.student;
- }
- public void setStudent(Student student1) {
- this.student = student1;
- }
- public Teacher getTeacher() {
- return teacher;
- }
- public void setTeacher(Teacher teacher) {
- this.teacher = teacher;
- }
- public void print() {
- System.out.println("------教师信息------");
- System.out.println("姓名:" + teacher.getName());
- System.out.println("年龄:" + teacher.getAge());
- System.out.println("性别:" + teacher.getSex());
- System.out.println();
- System.out.println("------学生信息------");
- System.out.println("学号:" + student.getID());
- System.out.println("姓名:" + student.getName());
- System.out.println("年龄:" + student.getAge());
- System.out.println("性别:" + student.getSex());
- }
- }
- <?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.2.xsd">
- <bean id="student" class="com.autobean.Student">
- <property name="ID" value="80" />
- <property name="name" value="阿王" />
- <property name="age" value="23" />
- <property name="sex" value="男" />
- </bean>
- <bean id="teacher" class="com.autobean.Teacher">
- <property name="name" value="何老师" />
- <property name="age" value="43" />
- <property name="sex" value="女" />
- </bean>
- <!-- 默认情况下,通过'ref’来装配bean -->
- <bean id="teachFile1" class="com.autobean.TeachFile" >
- <property name="teacher" ref="teacher" />
- <property name="student" ref="student" />
- </bean>
- <!--根据byName自动装配bean -->
- <bean id="teachFile2" autowire="byName" class="com.autobean.TeachFile" />
- <!--根据byType自动装配bean -->
- <bean id="teachFile3" autowire="byType" class="com.autobean.TeachFile" />
- <!--根据constructor自动装配bean -->
- <bean id="teachFile4" autowire="constructor" class="com.autobean.TeachFile"/>
- </beans>
[java] view plain copy print?
- package com.autobean;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.xml.XmlBeanFactory;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.core.io.Resource;
- public class PrintInfo {
- public static void main(String[] args) {
- Resource res = new ClassPathResource("applicationContext.xml");
- BeanFactory bf = new XmlBeanFactory(res);
- TeachFile tf1 = (TeachFile) bf.getBean("teachFile1");
- TeachFile tf2 = (TeachFile) bf.getBean("teachFile2");
- TeachFile tf3 = (TeachFile) bf.getBean("teachFile3");
- TeachFile tf4 = (TeachFile) bf.getBean("teachFile4");
- System.out.println("默认情况下,通过'ref’来装配bean");
- tf1.print();
- System.out.println("根据byName自动装配bean");
- tf2.print();
- System.out.println("根据byType自动装配bean");
- tf3.print();
- System.out.println("根据constructor自动装配bean");
- tf4.print();
- }
- }
在上面实例的配置文件 appContext.xml 中,TeachFile 类采用4种装配,将 Teacher
类和 Student 类注入到对应的属性中。语法格式如下。
- <bean autowire="byName" id="teachFile" class="TeachFile" />
<bean autowire="byName" id="teachFile" class="TeachFile" />
在 autowire 属性中指定类型为“byName”。autowire 属性共支持 5 种装配类型,下面分别介绍每种装配类型的用法。
(1)no:autowire 采用的默认值,采用自动装配。必须使用 ref 直接引用其他 Bean,这样可以增加代码的可读性,并且不易出错。
(2)byName:以属性名区分自动装配。在容器中寻找与 JavaBean 的属性名相同的JavaBean,并将其自动装配到 JavaBean 中。如果用上面的实例来解释,TeachFile 类的实例对象 teachFile 包含的两个属性分别是 Teacher 类和 Student 类的实例对象,而配置文件中已经定义了这两个类的实例。在定义 teachFile 实例时指定了自动装配类型为“byName”,容器会自动寻找 teachFile 实例需要的属性(即 teacher 和 student 两个 JavaBean),并注入到 teachFile 实例中。此类自动装配类型存在错误装配 JavaBean 的可能,如果配置文件中定义了与需要自动装配的 JavaBean 属性相同而类型不同的 JavaBean,那么它会错误地注入不同类型的JavaBean。读者可以将上面实例中的配置文件修改一下,将 student 和 teacher 两个JavaBean 的类型保持不变,将名字调换一下,便会出现此问题。这时自动装配无法解决此问题,只能通过混合使用手动装配来指定装配哪个 JavaBean。
(3)byType:以属性类型区分自动装配。容器会自动寻找与 JavaBean 的属性类型相同的 JavaBean 的定义,并将其注入到需要自动装配的 JavaBean 中。如果将上面配置JavaBean 自动装配的类型修改为 byType,也可以实现相同的结果。这种自动装配类型也会出现无法自动装配的情况。例如在配置文件中再次添加一个Student 类或 Teacher 类的实现对象,byType 自动装配类型会因为无法自动识别装配哪一个 JavaBean 而抛出org.springframework.beans.factory.UnsatisfiedDependencyException 异常。要解决此
问题,只能通过混合使用手动装配来指定装配哪个 JavaBean。
(4)constructor:通过构造方法的参数类型自动装配。此类型会使容器自动寻找与JavaBean 的构造方法的参数类型相同的 Bean,并注入到需要自动装配的 JavaBean 中。它
与 byType 类型存在相同的无法识别自动装配的情况。
(5)autudetect:这是最后一个自动装配类型,它首先使用 constructor 方式自动装配,然后使用 byType 方式。当然它也存在与 byType 和 constructor 相同的异常情况。
最后,我认为,自动装配虽然让开发变得更快速,但是同时却要花更大的力气维护,因为它增加了配置文件的复杂性,你甚至不知道哪一个bean会被自动注入到另一个bean中。建议在使用自动装配时,把容易出现问题的 JavaBean 使用手动装配注入依赖属性。Ps.我更愿意写配置文件来手工装配。