Spring详解(七)----Spring Bean的自动装配(基于XML的方式)

1、自动装配的几种方式

       上一章介绍 Bean 的装配都是在 Spring 容器中手动完成属性的装配,而在Spring 容器中给我们提供了完成 bean 之间的自动装配的功能(即对象类型的自动装配),这样的好处就是有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量,因为在稍微大一点的项目中,一个被引用的bean的id改变了,那么需要修改所有引用了它的id。Spring框架默认是不支持自动装配的,我们可以使用Spring的配置文件中< bean >元素的 autowire 属性为一个 bean 定义指定自动装配模式。其中<bean>元素中的autowire属性有5个可选值,如下:

  • no:默认的设置,表示不启用自动装配,需要我们手动通过"ref"属性手动完成装配。
  • byName:通过属性名称自动装配,如果一个JavaBean中的属性名称与Bean的id 相同,则自动装配这个Bean到JavaBean的属性中。Spring会查找该JavaBean中所有的set方法名,获得将set去掉并且首字母小写的字符串,然后去Spring容器中寻找是否有此字符串名称id 的Bean。如果有则就注入,如果没有则注入动作将不会执行。
  • byType:通过属性类型自动装配。Spring会在容器中查找JavaBean中的属性类型与Bean的类型一致的Bean,并自动装配这个Bean到JavaBean的属性中,如果容器中包含多个这个类型的Bean,Spring将抛出异常。如果没有找到这个类型的Bean,那么注入动作将不会执行。
  • constructor:类似于byType,也是通过类型自动装配,但是它是通过构造方法的参数类型来匹配。Spring会寻找与该JavaBean构造方法的各个参数类型相匹配的Bean,然后通过构造函数注入进来。如果在Spring容器中没有找一个构造函数参数类型的 Bean,则会报错。
  • autodetect:表示在constructor和byType之间自动的选择注入方式(spring5.x已经没有了)。首先尝试通过 constructor 来自动装配,如果它不执行,则Spring 尝试通过 byType 来自动装配。
  • default:由上级标签beans的default-autowire属性确定。

2、举例代码样例

       User类代码:

/**
 * 用户实体类
 */
public class User {
    private int userId;
    private String userName;
    private int userAge;
    private String userPwd;
    private String userAddress;
    //女朋友
    private GirlFriend girlFriend;

    public User() {
    }

    public User(int userId, String userName, int userAge, String userPwd,
                String userAddress, GirlFriend girlFriend) {
        this.userId = userId;
        this.userName = userName;
        this.userAge = userAge;
        this.userPwd = userPwd;
        this.userAddress = userAddress;
        this.girlFriend = girlFriend;
    }

    //getter、setter、toString方法省略......
}

       GirlFriend类代码:

/**
 * GirlFriend实体
 */
public class GirlFriend {
    private String girlName;
    private int girlAge;
    private String girlHeight;

    //getter、setter、toString方法省略......
}

       测试代码:

/**
 * 测试代码
 */
public class SpringTest {
    public static void main(String[] args) {
        //1.初始化Spring容器,加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.通过容器获取实例,getBean()方法中的参数是bean标签中的id
        User user =  applicationContext.getBean("user", User.class);
        //3.调用实例中的属性
        System.out.println(user.getUserName()+"-----"+user.getGirlFriend());
    }
}

3、byName装配

       byName装配是根据属性的名称自动装配,如果一个JavaBean中的属性名称与Bean的id 相同,则自动装配这个Bean到JavaBean的属性中。Spring会查找该JavaBean中所有的set方法名,获得将set去掉并且首字母小写的字符串,然后去Spring容器中寻找是否有此字符串名称id 的Bean。如果有则就注入,如果没有则注入动作将不会执行。

       applicationContext.xml配置内容为:

image

      运行测试代码,查看控制台输出:

image

       可以发现根据名称自动配置成功了,User类中girlFriend属性自动找到了 id 为 girlFriend 的 Bean,而配置文件中如果没有定义 id 为 girlFriend 的 Bean则会自动装配失败,例如,修改xml中Bean id为girlFriend1,更改后如下所示:

image

       再次运行测试代码,查看控制台输出:

image

       可以发现如果没有找到这个bean,那么就不装配。

4、byType装配

       byType装配表示通过属性类型自动装配。Spring会在容器中查找JavaBean中的属性类型与Bean的类型一致的Bean,并自动装配这个Bean到JavaBean的属性中,如果容器中包含多个这个类型的Bean,Spring将抛出异常。如果没有找到这个类型的Bean,那么注入动作将不会执行。

      我们将前面Spring配置文件中的autowire属性修改为byType:

image

      运行测试代码,查看控制台输出:

image

       注意:使用byType首先需要保证同一类型的Bean在Spring容器中是唯一的,若不唯一则会产生歧义,Spring容器不知道选择哪个实例注入,所以后面会报异常。

       假如这里出现了两个,那么 Spring 则不知道选择哪个,此时会报错:

image

       运行测试代码,查看控制台输出:

image

       所以,如果一旦选择了byType类型的自动装配,就必须确认配置文件中每个数据类型定义一个唯一的bean。

5、constructor装配

       constructor装配类似于byType,也是通过类型自动装配,但是它是通过构造方法的参数类型来匹配。Spring会寻找与该JavaBean构造方法的各个参数类型相匹配的Bean,然后通过构造函数注入进来。如果在Spring容器中没有找一个构造函数参数类型的 Bean,则会报错。

       applicationContext.xml配置内容为:

image

       运行测试代码,查看控制台输出:

image

       constructor自动装配具有和byType自动装配相同的局限性,就是当发现多个Bean匹配该JavaBean构造方法的类型时,Spring不知道用哪个Bean来装配,所以会导致装配失败。此外,如果一个JavaBean有多个构造方法,它们都满足自动装配的条件时,那么Spring也不会知道构造方法更适合使用,所以我们基本不会使用constructor装配。

6、default装配

       default装配表示由父级标签beans的default-autowire属性来配置。如果beans标签上设置了default-autowire属性,那么default-autowire属性会统一配置当前beans中的所有bean的自动装配方式。

image

  • 如果子标签<bean>没有单独的设置autowire属性,那么将采用父标签的default-autowire属性的模式。
  • 如果子标签<bean>单独设置了autowire属性,则采用自己的模式。

7、Bean自动装配的补充

       ①、上述的讲到byType和constructor装配是支持数组和强类型集合(即指定了集合元素类型)。如bean A有个属性定义是List<Foo>类型,Spring会在容器中查找所有类型为Foo的bean,注入到该属性。记住是Foo,不是List。另外如果集合是Map集合,那么Map的key必须是String类型,Spring会根据value的类型去匹配。例如有属性bean A中有一个属性为Map<String, Foo> p,容器中有bean B和C类型均为Foo,那么A实例化完成后,p属性的值为:{"B":B的实例对象,"C":C的实例对象}。

       ②、虽然autowrie给我们带来配置的便利性,但是也有缺点,比如会导致bean的关系没那么显而易见,所以用autowire还是ref还是需要根据项目来决定。

       ③、autowire-candidate:前面我们说到配置有autowire属性的bean,Spring在实例化这个bean的时候会在容器中查找匹配的bean对autowire bean进行属性注入,这些被查找的bean我们称为候选bean。作为候选bean,我凭什么就要被你用,老子不给你用。所以候选bean给自己增加了autowire-candidate="false"属性(默认是true),那么容器就不会把这个bean当做候选bean了,即这个bean不会被当做自动装配对象。同样,<beans/>标签可以定义default-autowire-candidate="false"属性让它包含的所有bean都不做为候选bean。我的地盘我做主。


       参考资料:

原文地址:https://www.cnblogs.com/tanghaorong/p/14157608.html