万字详解! Spring Bean自动装配
何为自动装配
自动装配是 Spring 满足 bean 依赖的一种方式。
在使用 Spring 配置 bean 时,我们都要给配置的 bean 的属性设置一个值,如果不手动设置则都是空。
而自动的好处就在于,我们不用手动去设置一个值,spring 会在上下文中自动寻找并装配
合适的值。
在 Spring 中有三种装配的方式:
- 在 XML 中显示配置
- 在 Java 代码中显示的配置
- 隐式的自动装配
本文重点放在隐式自动装配的说明,手动装配的部分可以参照Spring对象如何创建与管理,又如何使用巧妙的方法注入属性呢?
环境
测试自动装配前,先来定义好三个 POJO 类,Cat、Dog、Person。并且有一个关系就是 Person 拥有狗和猫。
package com.javastudyway.pojo;
public class Cat {
public void shout(){
System.out.println("我是猫");
}
}
package com.javastudyway.pojo;
public class Dog {
public void shout(){
System.out.println("I am Dog");
}
}
package com.javastudyway.pojo;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Person {
/* 人有猫和狗 */
private Cat cat;
private Dog dog;
private String name;
/* getter/setter 和 toString */
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"Cat = " + cat +
", Dog = " + dog +
", name = '" + name + '\'' +
'}';
}
}
Spring 配置文件部分:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.javastudyway.pojo.Cat"/>
<bean id="dog" class="com.javastudyway.pojo.Dog"/>
<bean id="person" class="com.javastudyway.pojo.Person">
<property name="name" value="Java学习之道"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
最后是测试类(MyTest):
import com.javastudyway.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
person.getDog().shout();
person.getCat().shout();
}
}
测试普通装配的结果:
装配没有问题。
配置实现自动装配
自动装配有三种方式:
- byName
- byType
- constructor
在配置 bean 时,我们可以加入一个 autowired 属性,来使用自动装配。
byName
byName
通过匹配 bean 的 id是否跟 setter 对应,对应则自动装配。
意思就是说,如果我的 Person 中有一个 setCat()
而配置文件中有一个 bean 的 id 为 cat,则能够自动装配。
把刚刚手动装配方式的 bean 做一些修改,来演示自动装配:
<bean id="cat" class="com.javastudyway.pojo.Cat"/>
<bean id="dog" class="com.javastudyway.pojo.Dog"/>
<!--加上了 person 的 bean 的 autowired 属性
并去掉了手动装配的 cat 和 dog -->
<bean id="person" class="com.javastudyway.pojo.Person" autowire="byName">
<property name="name" value="Java学习之道"/>
</bean>
刚刚的测试类运行结果依旧正常,表明了自动装配的确是运行了。
如若我 bean 的 id 跟 setter 不对应呢?
<bean id="dog11" class="com.javastudyway.pojo.Dog"/>
把刚刚的 dog id改掉,其余不变再来测试:
结果是出现了空指针异常,说明没有对象,也就反映出了自动装配失败了。
byType
byType
通过匹配 bean 中所需要的依赖类型在容器上下文中自动寻找装配。
byName 方式 dog11 自动装配失败了,那么 byType
呢?
稍稍改动一下 person
,把自动装配方式改为 byType:
<bean id="person" class="com.javastudyway.pojo.Person" autowire="byType">
<property name="name" value="Java学习之道"/>
</bean>
空指针异常不再出现,自动装配成功!
自动装配跟 id 无关?它真的还就没有关系,甚至在配置 cat 和 dog 这两个 bean 时不写 id 都不会报错。
但是 byType
的自动装配存在一个很严重的问题,因为不是通过唯一的 id 来匹配,而是通过类型来匹配,所以容器中不能存在多个相同类型的 bean。
这是官方文档中给出的自动装配的说明,Spring 官方也不是很支持使用 byType 来实现自动装配,使用唯一标识 id 来匹配也确实更为安全。
优异
byName 和 byType 也不能非说孰强孰弱,只是各有各的好处。
使用 byName 需要保证 bean 的 id 唯一,且这个 bean 需要自动注入的属性和set方法与 bean 的 id 要一致。
使用 byType 需要保证 bean 的 class 唯一,且这个 bean 需要自动注入的属性和类型要一致。
说到这里,对于自动装配也有一个初步的感悟,接下来就来使用注解开发的方式来实现自动装配。
使用注解实现自动装配
注解在 JDK1.5 之后被引入,而 Spring2.5 版本后也开始支持注解了。这些版本也都可以算得上远古版本,所以不用担心版本问题了。
关于使用注解
还是 XML
官方给出了答案,使用注解比使用 XML 配置更好。
不多吹,下面开始演示注解。
使用注解的准备
使用注解我们还需要对原本的配置文件做一些修改:
- 导入约束
导入约束,即增加 context 的约束。 - 配置注解支持
在配置中加入 --<context:annotation-config/>
。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解支持 -->
<context:annotation-config/>
<bean id="cat" class="com.javastudyway.pojo.Cat"/>
<bean id="dog" class="com.javastudyway.pojo.Dog"/>
<bean id="person" class="com.javastudyway.pojo.Person"/>
</beans>
这样一来,我们的配置文件就变得极度简洁了,剩下的事情全部都交给注解来搞定就行。
@AutoWired注解的使用
搞定了配置文件后就可以开始使用注解了,在 POJO 类需要自动装配的属性上加 @AutoWired
注解实现自动装配。
小贴士:
- @AutoWired注解也可以在 setter 上使用;
- 如果使用了 @AutoWired 注解,POJO 类中的 setter 方法也可以省略,因为自动装配注解是用反射来实现的。
@AutoWired 这个注解还有一个属性,required
。来看看 @AutoWired 的源码:
public @interface Autowired {
boolean required() default true;
}
required 的默认值为 true
,那么,这个值的作用是什么呢?
required -- 必需的,如果它的值为 true,则说明这个属性不是必需的(允许为null)。
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
/*
* 如此声明 required 为 false,
* 则该对象可以没有 cat 这个属性
*/
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"Cat = " + cat +
", Dog = " + dog +
", name = '" + name + '\'' +
'}';
}
}
@Qualifier
一般情况下,使用 @AutoWired
注解类似于 byType 的装配方式,但是如果我们在配置文件中,配置了同一个类的多个 bean,那么这时候 @AutoWired 该作何选择呢?
如果遇到这个情况我们就需要使用 @Qualifier
注解来辅助实现了。
先来看看 @Qualifier 注解的源码:
public @interface Qualifier {
String value() default "";
}
可以看到这个注解需要一个 value 的参数,这个参数即为我们要选择的 bean 的 id。
在配置文件中增加一个 dog 的 bean。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解的支持-->
<context:annotation-config/>
<bean id="cat" class="com.javastudyway.pojo.Cat"/>
<bean id="dog" class="com.javastudyway.pojo.Dog"/>
<bean id="dog2" class="com.javastudyway.pojo.Dog"/>
<bean id="person" class="com.javastudyway.pojo.Person" autowire="byType">
<property name="name" value="Java学习之道"/>
</bean>
</beans>
再搭配上 @Qualifier
注解
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Person {
@Autowired(required = false)
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"Cat = " + cat +
", Dog = " + dog +
", name = '" + name + '\'' +
'}';
}
}
这样便能在多个同类 bean 实现使用 @AutoWired 自动装配任意一个 bean。
@Resource
前文说到的两个注解皆是 Spring 的注解,在 Java 中还有一个原生注解,@Resource,这个注解的作用类似于 @AutoWired 和 @Qualifier的结合体。
在 bean 的自动装配情况下,可以使用到 @Resource
的 name 参数,这个 name 的参数相当于 @Qualifier 的 value 参数。
@Resource 与 @AutoWired 的不同在于:
- @Resource 默认是按照 名称 来装配注入的,当找不到与名称匹配的 bean 才会按照类型来装配注入。
- @Autowired 默认是按照 类型 装配注入的,如果想按照名称来装配注入,则需要结合 @Qualifier 一起使用;
将配置的两个 dog 改成 dog1 和 dog2,再来使用 @Resource 注解。
<bean id="dog1" class="com.javastudyway.pojo.Dog"/>
<bean id="dog2" class="com.javastudyway.pojo.Dog"/>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.annotation.Resource;
public class Person {
@Resource
private Cat cat;
@Resource
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"Cat = " + cat +
", Dog = " + dog +
", name = '" + name + '\'' +
'}';
}
}
运行的结果为:
因为 @Resource 默认是通过 id 来查找 bean,而配置的两个 dog 的 bean 没有一个名称为默认查找的 dog,故而创建失败。
修改 @Resource,加入参数:
@Resource(name = "dog1")
private Dog dog;
重新测试结果:
因为能够通过 name 这个参数匹配到 Dog 类型的 bean,所以程序便不会出现问题。
@Resource总结
@Resource 注解默认通过 byName 匹配 bean,如果 id 匹配失败则会通过类型匹配,但是如果同个类型的 bean 不唯一或者该类的 bean 不存在则装配失败。
标题:万字详解! Spring Bean自动装配
作者:spirit223
地址:https://www.mmzsblog.cn/articles/2021/02/21/1613915663167.html
如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议!
本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!