SpringBean自动装配

SpringBean自动装配

自动装配是 Spring 满足 bean 依赖的一种方式,之前都要手动给 bean 注入依赖,否则属性就为空;而使用自动装配,Spring 就会在容器自动寻找需要的依赖,并装配到 bean 中。

新建 Spring-05-Autowired 项目使用一下 Spring 的自动装配。

1. 测试环境搭建

在 Spring-05-Autowired 项目中创建 Person 类、Cat 类、Dog 类,类之间的关系为一个人有两个宠物猫和狗(为了方便就不创建包了,直接在 main/java 下建类)

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
    private String name;
    private Cat cat;
    private Dog dog;
}
public class Cat {
    public void shout(){
        System.out.println("喵喵喵");
    }
}
public class Dog {
    public void shout(){
        System.out.println("汪汪汪");
    }
}

再创建 beans.xml 配置文件,按之前的方式创建一个 Person 对象

<!--没有属性需要设置-->
<bean id="cat" class="Cat"/>
<bean id="dog" class="Dog"/>

<!--用一下 p 命名空间!-->
<bean id="person" class="Person" p:name="祈鸢" p:cat-ref="cat" p:dog-ref="dog"/>

搞完了,写个测试方法让宠物叫一下

public class MyTest {
    @Test
    public void Test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        person.getCat().shout();
        person.getDog().shout();
    }
}
// 执行结果
/*
    喵喵喵
    汪汪汪
*/

之前手动装配就是这样做的,下面用一下自动装配。

2. XML自动装配

使用自动装配,要在 bean 标签内配置 autowire 属性, autowire 属性有 byName、byType、constructor、default、no 几种属性

2.1 byName

使用 byName 方式自动装配,Spring 会自动在容器中通过 set 方法后的字段(如 setCat,字段是 Cat,就去寻找 cat 对象)寻找 Person 类缺少的依赖,这里缺少的是 Cat 类和 Dog 类,就会去寻找上面的 cat 和 dog

<bean id="person" class="Person" p:name="祈鸢" autowire="byName"/>

运行上面的测试方法,结果一样。

注意使用 byName 自动装配,对 bean 的 id 有要求。如上面的 id 的 cat 如果变成 cat1,则 Spring 就找不到这个对象了;而且要注意大小写,如 cat 如果 写成 Cat,也会让 Spring 找不到。自动装配找不到需要的对象,依赖就会变成 null 了。

2.2 byType

使用 byType 方式自动装配,Spring 会自动在容器中寻找依赖所需的类型对应的 bean,如 person 需要的依赖是 Cat 类型和 Dog类型,就会去寻找这两种类型的对象。由于只和类型相关,所以对应的 bean 甚至可以没有 id

<bean class="Cat"/>
<bean class="Dog"/>

<bean id="person" class="Person" p:name="祈鸢" autowire="byType"/>

再运行一下测试方法,结果也是一样的。

注意:既然是通过类型寻找目标的,那么问题也很明显,就是不能有多个同一类型的 bean!如不能同时有 cat 和 cat1,否则 IDEA 都会直接提示这个 bean 有问题,必须保证需要的依赖类型全局唯一

3. 注解自动装配

现在就要开始了解一下 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"
    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/>

</beans>

3.1 @Autowired

在属性上添加 @Autowired 注解可以对该属性实现自动装配(加在 set 方法上也行)

...
public class Person {
    private String name;
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
}

使用了 @Autowired 后,在装配时

  1. 先按照 byType 去寻找该属性对应的类型,如果只有一个该类型对象则直接装配
  2. 如果有多个该类型的对象,再按照 byName 去寻找命名为类名的对象(如 Cat 类型对应的 cat 对象)进行装配
  3. 如果两种方式都寻找失败,则装配失败,属性为 null
<!--使用注解自动装配-->
<!--对于 cat,使用的是 byName-->
<bean id="cat" class="Cat"/>
<bean id="cat2" class="Cat"/>
<!--对于 dog,使用的是 byType-->
<bean id="dog" class="Dog"/>
<bean id="person" class="Person" p:name="祈鸢"/>

另外,使用 @Autowired 可以不用编写 set 方法,也能注入依赖!

扩展一下,点进 @Autowired 的接口中看一下,有一个 required 属性,默认为 true

public @interface Autowired {
    boolean required() default true;
}

required 属性默认设置为 ture 的作用为限制添加了 @Autowired 注解的属性不能为空,即找不到要装配的对象时会报错;若将其设置为 false,则找不到要装配的对象程序也不会报错

如在 Dog 属性上设置为 false,同时删掉 beans.xml 配置的 dog 对象

...
public class Person {
    private String name;
    @Autowired
    private Cat cat;
    @Autowired(required = false)
    private Dog dog;
}

执行测试方法,结果为

// 执行结果

// 喵喵喵
// java.lang.NullPointerException
// ...

这里的空指针异常是因为调用了 getDog().shout() 方法,由于允许为空但又不存在符合的对象,所以这里的 getDog() 获取为空。

如果不设置 required = false,且删掉 beans.xml 配置的 dog 对象,执行测试方法会报错为

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type ‘Dog‘ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

即由于 required = true,但又找不到对象进行装配的错误( expected at least 1 bean )。

3.2 @Qualifier

如果 @Autowired 使用的环境比较复杂,可以和 @Qualifier 配合使用。 @Qualifier 中只有一个属性 value,设置后自动装配会直接寻找对应 id 的对象,如果找不到 IDEA 都会直接标红。

注意:配置 @Qualifier 后 @Autowired 只会去查找指定的对象,会屏蔽掉 byType 和 byName 的自动装配。

在 beans.xml 中添加两只猫和两只狗,避免 byType 装配,同时 id 后带上1和2,避免 byName 装配

<!-- @Qualifier寻找对象-->
<bean id="cat1" class="Cat"/>
<bean id="cat2" class="Cat"/>
<bean id="dog1" class="Dog"/>
<bean id="dog2" class="Dog"/>
<bean id="person" class="Person" p:name="祈鸢"/>

通过 @Qualifier 注解指定特定的对象

...
public class Person {
    private String name;
    @Autowired
    @Qualifier(value = "cat2")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
}

执行测试方法,结果为

// 执行结果
/*
    喵喵喵
    汪汪汪
*/

说明找到对象了,装配成功了!

3.3 @Resource

@Resource 注解是 JAVA 自带的注解,功能上是 @Autowired 和 @Qualifier 的集合体。

即 @Resource 也能根据 byType 和 byName 来自动装配,这里和 @Autowired 相同;同时 @Resource 也具有 name 属性,可以指定要装配的对象,这里和 @Qualifier 相同

...
public class Person {
    private String name;
    @Resource(name = "cat2")
    private Cat cat;
    @Resource(name = "dog2")
    private Dog dog;
}

运行测试方法没有问题,这里就省略了。

4. 总结

本节了解了如何自动装配 bean

  • 使用 xml,在 bean 标签中要设置 autowired 的属性为 byType 或 byName
  • 使用注解,一般来说 @Autowired 也够用了,再不行就结合 @Qualifier 或功能强大的 @Resource

总体而言使用注解比较简单,但要装配的对象一旦多起来,就会很乱??。

SpringBean自动装配

上一篇:我的WCF Data Service 系列 (一、为什么要有WCF Data Service)


下一篇:spring prototype bean 获取处理