Spring中的自动装配
1. Bean中的自动装配
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
1 在xml中显式配置;
2 在java中显式配置;
3 隐式的bean发现机制和自动装配。
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
1 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少。
推荐不使用自动装配xml配置 , 而使用注解
测试环境 byName byType 构造函数
新建三个实体类:主人,猫,狗
package com.lx.pojo;
public class User {
private Dog dog;
private Cat cat;
private String name;
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"dog=" + dog.name +
", cat=" + cat.name +
", name='" + name + '\'' +
'}';
}
}
package com.lx.pojo;
public class Dog {
public String name;
public void setName(String name) {
this.name = name;
}
public void shout(){
System.out.println("狗的叫方法");
}
}
package com.lx.pojo;
public class Cat {
public String name;
public void setName(String name) {
this.name = name;
}
public void shout(){
System.out.println("猫的叫方法");
}
}
在beans.xml中注册猫狗:
<bean id="dog" class="com.lx.pojo.Dog"> <property name="name" value="大黄" /> </bean>
<bean id="cat" class="com.lx.pojo.Cat"> <property name="name" value="小白" /> </bean>
1 byName
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
<bean id="user" class="com.lx.pojo.User" autowire="byName">
<property name="name" value="lx"/>
</bean>
测试单元:
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
user.getCat().shout();
System.out.println(user.toString());
}
测试结果:
我们将 cat 的bean id修改为 catXXX 再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方
法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
2 byType
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
NoUniqueBeanDefinitionException
将user的bean配置修改一下 : autowire=“byType”
<bean id="user2" class="com.lx.pojo.User" autowire="byType">
<property name="name" value="空之律者"/>
</bean>
测试单元:
@Test
public void test02(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user2 = context.getBean("user2", User.class);
System.out.println(user2.toString());
}
测试结果:
如果在注册一个cat 的bean对象 cat2
测试:报错:NoUniqueBeanDefinitionException
删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
这就是按照类型自动装配!
3. 构造函数
新建两个实体类:
package com.lx.pojo;
public class Wife {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Wife{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
package com.lx.pojo;
public class Owner {
private String name;
private Wife wife;
public Owner(String name, Wife wife) {
this.name = name;
this.wife = wife;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", wife=" + wife +
'}';
}
}
<bean id="wife" class="com.lx.pojo.Wife" >
<property name="name" value="琪亚娜"/>
<property name="age" value="18"/>
</bean>
<bean class="com.lx.pojo.Wife" id="wife2">
<property name="name" value="八重樱"/>
<property name="age" value="20"/>
</bean>
<bean id="owner" class="com.lx.pojo.Owner" autowire="constructor">
<constructor-arg name="name" value="我"/>
</bean>
测试单元:
测试结果:
样例所示,如果注释掉琪亚娜,输出结果为八重樱,不注释掉输出位琪亚娜,把琪亚娜的id改为girlfriend 报错两个都无法输出,
根据查询结果,如果查询结果只有一个,byType或者说直接给
若果查询结果有多个,则根据byName
2 使用注解自动装配 ★★★★★★★★★★★★
2.1 环境搭建
2.1.1 创建实体类
package com.lx.pojo;
public class Wife {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Wife{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.lx.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Ownner {
private String name;
private int age;
private Wife wife;
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 Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Ownner{" +
"name='" + name + '\'' +
", age=" + age +
", wife=" + wife +
'}';
}
}
2.1.2 beans.xml创建
1 在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2 开启属性注解支持
<!--开启注解的支持-->
<context:annotation-config/>
2.1.3 创建bean
<bean id="wife" class="com.lx.pojo.Wife" >
<property name="name" value="琪亚娜"/>
<property name="age" value="18"/>
</bean>
<bean id="wife2" class="com.lx.pojo.Wife">
<property name="name" value="空之律者"/>
<property name="age" value="21"/>
</bean>
<bean id="wife3" class="com.lx.pojo.Wife">
<property name="name" value="月光"/>
<property name="age" value="20"/>
</bean>
<bean id="ownner" class="com.lx.pojo.Ownner">
<property name="age" value="18"/>
<property name="name" value="lx"/>
</bean>
测试单元:
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Ownner ownner = context.getBean("ownner", Ownner.class);
System.out.println(ownner.toString());
}
2.2 三种注解
2.2.1 @Autowired
其实在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性
在使用@Autowired时,首先在容器中查询对应类型的bean
- 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据
-
如果查询的结果不止一个,那么@Autowired会根据名称来查找
-
如果查询的结果为空,那么会抛出异常。解决方法时,使用required=false
@Autowired
private Wife wife;
@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。
2.2.2 @Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用。
2.2.3 @Resource
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
测试样例1:
测试样例2:
@Autowired与@Resource异同:
- @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
- @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果
要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我
们想使用名称装配可以结合@Qualifier注解进行使用 - @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果
没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在
setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是
需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先 byName。
参考文章: