Java-Spring入门指南(五)Spring自动装配

发布于:2025-09-11 ⋅ 阅读:(14) ⋅ 点赞:(0)


前言

在上一篇博客中,我们掌握了Spring依赖注入的两种核心手动方式——构造器注入与setter注入,但也留下了一个明显的“痛点”:当Bean的依赖关系复杂(比如一个Service依赖多个Dao)时,我们需要在XML中反复编写<property ref="..."/>,配置繁琐且容易出错

有没有办法让容器“主动找”依赖,而不是我们“手动指”依赖?

  • 答案就是这篇要讲的Spring自动装配(Autowire)。它是Spring在手动注入基础上的优化,核心是“容器根据预设规则自动匹配并注入依赖”,能大幅减少XML配置冗余。

本文将从“自动装配的本质”切入,手把手实战两种核心自动装配方式(byNamebyType),再详解alias(Bean别名)和import(XML整合)这两个辅助配置。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

在这里插入图片描述


一、什么是Spring自动装配?

在讲实战前,我们先搞懂“自动装配”到底解决了什么问题——它不是替代手动注入,而是对setter注入的“简化”

1.1 手动注入的痛点

回顾上一篇的User依赖Address,我们需要在XML中手动配置<property ref="address"/>

<!-- 手动注入:必须明确写ref指向依赖的Bean -->
<bean id="user" class="org.example.pojo.User">
    <property name="address" ref="address"></property> <!-- 手动关联 -->
</bean>
<bean id="address" class="org.example.pojo.Address"></bean>

如果一个OrderService依赖OrderDaoUserDaoLogDao三个Bean,就需要写3个<property>标签——依赖越多,配置越繁琐

1.2 自动装配的定义

自动装配(Autowire)的核心是:容器根据预设的“匹配规则”,自动在IoC容器中查找当前Bean的依赖(如Person依赖的Dog、Cat),并完成注入,无需手动编写<property ref="..."/>

1.3 自动装配的本质

  1. 容器先通过无参构造器创建当前Bean(如Person);
  2. 按规则(byName/byType)在容器中找依赖的Bean;
  3. 找到后,自动调用当前Bean的setter方法完成注入。

关键前提:自动装配仅支持setter注入不支持构造器注入!所以Bean必须有依赖属性的setter方法。

二、自动装配的核心方式

Spring提供多种自动装配方式,最常用、最核心的是byName(按名称匹配)byType(按类型匹配)

2.1 准备工作

首先编写我们的核心Bean类:

// Dog类(被依赖方)
public class Dog {
    public void eat() {
        System.out.println("狗喜欢吃骨头");
    }
}

// Cat类(被依赖方)
public class Cat {
    public void eat() {
        System.out.println("猫喜欢吃小鱼干");
    }
}

// Person类(依赖方:依赖Dog和Cat)
public class Person {
    private Dog dog;
    private Cat cat;

    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;
    }
}

2.2 方式一:byName

2.2.1 核心规则

容器会根据当前Bean的“属性名”,去匹配IoC容器中其他Bean的“id属性”——属性名与Bean的id完全一致时,自动注入

用我们的Person举例:

// Person类(依赖方:依赖Dog和Cat)
public class Person {
    private Dog dog;
    private Cat cat;

    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;
    }
}
  • Person有两个属性:dog(属性名)、cat(属性名);
  • 容器中需有id="dog"的Dog类Bean、id="cat"的Cat类Bean;
  • 配置autowire="byName"后,容器自动匹配并注入。

2.2.2 配置实战

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 1. 配置被依赖方Bean:id必须与Person的属性名一致 -->
    <bean id="dog" class="org.example.pojo.autowire.Dog"></bean> <!-- id=dog 匹配 Person的dog属性 -->
    <bean id="cat" class="org.example.pojo.autowire.Cat"></bean> <!-- id=cat 匹配 Person的cat属性 -->
  

    <!-- 2. 配置依赖方Bean:开启byName自动装配 -->
    <bean id="person" class="org.example.pojo.autowire.Person" autowire="byName">
        <!-- 无需写<property ref="dog"/>和<property ref="cat"/>,容器自动匹配 -->
    </bean>

</beans>

2.2.3 测试代码与结果

编写测试类,验证自动装配是否成功:

     @Test
    public void test2() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("autowire.xml");
        Person person =  applicationContext.getBean("person", Person.class);
        person.getCat().eat();
        person.getDog().eat();
    }

预期结果

在这里插入图片描述

2.2.4 关键注意点

  1. 属性名与Bean的id必须完全一致:若Person的dog属性改名为doggy,而Bean的id还是dog,容器找不到匹配的Bean,会注入null
  2. 允许同类型多Bean:如你代码中注释的cat1,即使开启,只要Person没有cat1属性,就不影响cat属性的匹配(byName只看名称,不看类型);
  3. 必须有setter方法:容器通过setter方法注入,若删除setDog(),会注入null

2.3 方式二:byType

2.3.1 核心规则

容器会根据当前Bean的“属性类型”,去匹配IoC容器中其他Bean的“class类型”——属性类型与Bean的class完全一致,且容器中该类型Bean唯一时,自动注入

2.3.2 配置实战

只需将person Bean的autowire改为byType,其他不变:

<!-- 被依赖方Bean:id可任意(byType不依赖id) -->
<bean id="dog" class="org.example.pojo.autowire.Dog"></bean> <!-- 类型是Dog -->
<bean id="cat" class="org.example.pojo.autowire.Cat"></bean> <!-- 类型是Cat -->

<!-- 依赖方Bean:开启byType自动装配 -->
<bean id="person" class="org.example.pojo.autowire.Person" autowire="byType">
    <!-- 同样无需手动配置property -->
</bean>

2.3.3 测试代码与结果

复用上面的test方法(只需保证XML配置是byType),预期结果与byName完全一致
在这里插入图片描述

  • 关键区别:此时即使把dog Bean的id改为myDogcat Bean的id改为myCat,依然能注入成功(byType不看id)。

2.3.4 关键注意点

  1. 容器中该类型Bean必须唯一:这是byType的核心限制!若此时容器有两个Cat类型Bean,启动容器会直接报错:
    在这里插入图片描述

    NoUniqueBeanDefinitionException: No qualifying bean of type 'org.example.pojo.autowire.Cat' available: expected single matching bean but found 2: cat,cat1
    
  2. 不依赖Bean的id:即使Bean的id与属性名完全不同(如id=myDog,属性名=dog),只要类型匹配且唯一,就能注入;

  3. 支持父子类/接口:若有Dog的子类BigDog,Person的dog属性是Dog类型,容器中只有BigDog类型Bean,也能匹配(多态支持)。

2.4 byName vs byType 对比

两种自动装配方式各有适用场景,用表格对比更清晰:

对比维度 byName(按名称) byType(按类型)
匹配规则 属性名 ≡ Bean的id 属性类型 ≡ Bean的class(且唯一)
依赖条件 必须保证属性名与Bean的id一致 必须保证容器中该类型Bean唯一
灵活性 依赖命名规范,改id需同步改属性名 不依赖id,改id不影响注入
容错性 同类型多Bean不报错(只看名称) 同类型多Bean直接报错
适用场景 命名规范明确(如属性名=Bean id) 类型唯一且稳定(如工具类Bean)

三、alias与import

3.1 alias:给Bean起别名

3.1.1 核心作用

给IoC容器中的Bean分配多个“别名”,后续通过“原id”或“任意别名”都能获取该Bean,解耦Bean的引用与id的绑定

比如:

<alias name="person" alias="person_name_1"/>
  • 原id:person
  • 别名:person_name_1
  • 获取Bean时,ac.getBean("person")ac.getBean("person_name_1")得到的是同一个对象。

3.1.2 适用场景

  1. 多模块引用同一Bean:A模块习惯用personA称呼,B模块习惯用personB,给Bean起两个别名,无需修改原id;
  2. 避免id冲突:若导入的第三方XML中已有user Bean,你本地的user Bean可起别名myUser,避免冲突。

3.2 import:拆分与整合XML配置

3.2.1 核心作用

当项目中Bean数量庞大时,将所有Bean写在一个XML中会非常臃肿。import可以将多个分散的XML配置文件导入到一个“主配置文件”,只需加载主配置,就能加载所有XML中的Bean。

比如:

<import resource="beans.xml"/>
  • 主配置文件:autowire.xml(当前配置文件);
  • 导入的子配置:beans.xml(可存放其他Bean,如上一篇的Student、User);
  • 加载主配置时,autowire.xmlbeans.xml中的Bean都会被加载到容器。

3.2.2 实战场景

假设项目分3个模块,每个模块的Bean存放在独立XML中:

  • dao.xml:存放Dao层Bean(如UserDao、OrderDao);
  • service.xml:存放Service层Bean(如UserService、OrderService);
  • autowire.xml:存放Person、Dog、Cat等Bean(主配置);

通过import整合主配置:

<!-- 主配置文件:autowire.xml -->
<import resource="dao.xml"/>
<import resource="service.xml"/>
<!-- 本地Bean配置 -->
<bean id="dog" class="org.example.pojo.autowire.Dog"></bean>
<bean id="cat" class="org.example.pojo.autowire.Cat"></bean>
<bean id="person" class="org.example.pojo.autowire.Person" autowire="byName"></bean>
  • 加载时只需加载autowire.xml,就能获取所有模块的Bean,大幅提升可维护性。

3.2.3 注意点

  1. 路径问题resource属性写XML的“相对路径”,若子XML在config目录下,需写resource="config/dao.xml"
  2. 顺序无关import的顺序不影响Bean的加载(Spring会处理依赖关系);
  3. 重复导入不报错:同一XML被多次导入,Spring只会加载一次。

四、自动装配的优缺点与使用建议

4.1 优点

  1. 简化配置:无需手动写大量<property ref="..."/>,减少XML冗余;
  2. 降低维护成本:依赖关系变更时,只需修改Bean的id或类型,无需修改注入配置;
  3. 支持模块化:结合importalias,适合大型项目拆分配置。

4.2 缺点

  1. 可读性下降:无法直观看到Bean的依赖来源(手动注入能明确看到ref指向);
  2. 排错难度高:注入null时,需排查命名、类型、Bean唯一性等多个维度;
  3. 功能局限:仅支持setter注入,不支持构造器注入;不支持复杂依赖(如集合类型)。

4.3 推荐使用原则

  1. 简单场景用自动装配:如工具类、单一依赖的Bean(如Person依赖Dog/Cat),优先用byName(命名规范明确时)或byType(类型唯一时);
  2. 复杂场景用手动注入:如多依赖、构造器注入、集合类型注入,手动配置更明确,排错更简单;
  3. 大型项目结合注解:XML自动装配是基础,后续我们会讲注解驱动的自动装配(@Autowired@Qualifier),比XML更灵活、更简洁。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到