Spring6 (for 理解&记忆)

1、IoC容器基础

2、Spring高级特性

3、SpringEL表达式

4、AOP面向切片

5、数据库框架整合

6、实现原理探究

(一)IoC容器基础

Spring为了简化开发而生,它是轻量级的IoC和AOP的容器框架。

2026-02-07T14:46:08.png

Spring框架的Maven依赖坐标:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.10</version>
</dependency>

一、用IoC容器管理bean

如何让Spring帮我们管理bean?

在resource中创建Spring配置文件。

(在Resource中创建的文件,编译时会被一起放到类路径下)

// spring.xml
<?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 name="student" class="com.test.bean.Student"/>
</beans>

在main中,创建应用程序上下文,它代表的就是IoC容器,负责实例化、配置和组装Bean

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlAppicationContext("spring.xml");
    Student student = (Student) context.getBean("student");
    student.hello();
}

给bean起别名

<bean name="a" class="com.test.bean.Student"/>
<alias name="a" alias="test"/>

设置作用域

默认情况下为单例模式,获取的对象始终为同一个,即singleton作用域,可以修改作用域,每次都获取新的对象

<bean class="com.test.bean.Student" scope="prototype"/>

设置懒加载

如果希望单例模式下,bean不用一开始就加载,可以开启懒加载,需要时再加载。

<bean class="com.test.bean.Student" lazy-init="true"/>

设置加载顺序

单例模式下,bean由IoC容器加载,但加载顺序我们不清楚,如果某个bean依赖于另一个bean,另一个bean必须要在这个bean之前创建,我们可以使用depends-on来设定前置加载bean。

<bean name="teacher" class="com.test.bean.Teacher"/>
<bean name="student" class="com.test.bean.Student" depends-on="teacher"/>

二、DI依赖注入&IoC控制反转

解决痛点

使用DI前:

public class ClassA {
    private Teacher teacher = new ArtTeacher();
}
public class ClassB {
    private Teacher teacher = new ArtTeacher();
}
public class ClassC {
    private Teacher teacher = new ArtTeacher();
}
  • 代码重复:创建逻辑分散在各个类中,修改时需要修改多处代码
  • 违反开闭原则:每次需求变更都需要修改ClassX类,难维护
  • 代码耦合:ClassX类直接依赖于具体实现类,硬编码,耦合高

使用DI后:

当需要新增一种Teacher时:

  1. 新增实现类
  2. 修改配置文件
  3. 不需要修改任何已有的业务类
<!-- 所有依赖关系在配置文件中一目了然 -->
<bean name="artTeacher" class="com.test.bean.ArtTeacher"/>
<bean name="programTeacher" class="com.test.bean.ProgramTeacher"/>
<bean name="student1" class="com.test.bean.Student">
    <property name="teacher" ref="artTeacher"/>
</bean>
<bean name="student2" class="com.test.bean.Student">
    <property name="teacher" ref="programTeacher"/>
</bean>
对于Springboot,利用配置类方式 - 新增业务类完全不需要修改业务代码

配置类方式,简单工厂模式,业务类依赖接口不依赖具体实现类,具体实现类由配置类提供bean,简单工厂的参数由application.properties配置提供,新增业务类只需修改简单工厂,修改依赖的业务类只需修改配置

@Configuration
public class TeacherConfiguration {
    
    @Bean
    public Teacher teacher(@Value("${teacher.type:art}") String type) {
        switch(type) {
            case "art": return new ArtTeacher();
            case "program": return new ProgramTeacher();
            case "math": return new MathTeacher();  // 新增时只需修改这里
            default: return new ArtTeacher();
        }
    }
}

// 业务类完全不变
@Component
public class Student {
    @Autowired
    private Teacher teacher;  // 注入哪个Teacher由配置决定
}

application.properties配置

# 需要切换时只需修改这里
teacher.type=math

如何进行依赖注入

注入bean
public class Student {
    private Teacher teacher;
}

在Student类中注入Teacher:

<bean name="teacher" class="com.test.bean.ProgramTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <property name="teacher" ref="teacher"/>
</bean>

其中,property的name是student的属性名,ref是要注入的bean的name。

public class Student {
    private Teacher teacher;
      //要使用依赖注入,我们必须提供一个set方法(无论成员变量的访问权限是什么)命名规则依然是驼峰命名法
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
    ...

要使用依赖注入,我们必须提供一个set方法,set + 属性名。

这样就注入成功了。

当需要修改Teacher实现类时,只需要修改xml。

<!--  只需要修改这里的class即可,现在改为ArtTeacher  -->
<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <property name="teacher" ref="teacher"/>
</bean>
注入值

从ref变成value

<bean name="student" class="com.test.bean.Student">
    <property name="name" value="ZhangSan"/>
</bean>
注入List/Map/Set等集合&数组

同样属性和对应set方法,几个属性就几个property。

public class Student {
    private List<String> list;

    public void setList(List<String> list) {
        this.list = list;
    }
}
<bean id="student" class="com.test.bean.Student">
    <!-- 注入 List -->
    <property name="hobbies">
        <list>
            <value>篮球</value>
            <value>编程</value>
            <value>阅读</value>
        </list>
    </property>

    <!-- 注入 Set -->
    <property name="subjects">
        <set>
            <value>数学</value>
            <value>英语</value>
            <value>物理</value>
            <value>数学</value> <!-- 重复项会被自动去重 -->
        </set>
    </property>

    <!-- 注入 Map -->
    <property name="scores">
        <map>
            <entry key="数学" value="95"/>
            <entry key="英语" value="88"/>
            <entry key="物理" value="92"/>
        </map>
    </property>

    <!-- 注入数组 -->
    <property name="nicknames">
        <array>
            <value>小明</value>
            <value>明明</value>
            <value>阿明</value>
        </array>
    </property>
</bean>

注入Properties:对应 Java 类型:java.util.Properties

<property name="config">
    <props>
        <prop key="timeout">30s</prop>
        <prop key="retries">3</prop>
    </props>
</property>

注入其他 Bean 到集合中:集合不仅可以存基本类型(String, int 等),也可以存 其他 bean 对象

public class Student {
    private List<Teacher> teachers;
    
    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }
}
<bean id="teacher1" class="com.test.bean.Teacher"/>
<bean id="teacher2" class="com.test.bean.Teacher"/>

<bean id="student" class="com.test.bean.Student">
    <property name="teachers">
        <list>
            <ref bean="teacher1"/>
            <ref bean="teacher2"/>
        </list>
    </property>
</bean>
在构造时依赖注入(提前)
public class Student {
    private final Teacher teacher;   //构造方法中完成,所以说是一个final变量

    public Student(Teacher teacher){   //Teacher属性是在构造方法中完成的初始化
        this.teacher = teacher;
    }
      ...

bean是由IoC容器进行创建的,现在我们提供了带参构造函数,Java就不再提供默认无参构造函数。因此再使用之前的xml,会报错找不到匹配的构造函数(xml的bean默认是找无参构造)

因此,需要在xml中指明构造方法:

<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <constructor-arg name="teacher" ref="teacher"/>
</bean>
public class Student {
    private final String name;

    public Student(String name) {
        System.out.println("我是一号构造方法");
        this.name = name;
    }

    public Student(int age) {
        System.out.println("我是二号构造方法");
        this.name = String.valueOf(age);
    }
}

此时我们希望使用的是二号构造方法,那么怎么才能指定呢?有2种方式,我们可以给标签添加类型:(value/type)

<bean name="student" class="com.test.bean.Student">
    <constructor-arg value="1" type="int"/>
</bean>

也可以指定为对应的参数名称:(value/name)

<bean name="student" class="com.test.bean.Student">
    <constructor-arg value="1" name="age"/>
</bean>
在构造时多参数注入

可以写多个constructor=arg,会自动匹配符合参数最多的构造方法。

默认按参数顺序:

<bean id="student" class="com.test.bean.Student">
    <constructor-arg value="张三"/>
    <constructor-arg value="18" type="int"/>
    <constructor-arg ref="teacher"/>
</bean>

或index指定顺序:

<bean id="student" class="com.test.bean.Student">
    <constructor-arg index="0" value="张三"/>
    <constructor-arg index="1" value="18" type="int"/>
    <constructor-arg index="2" ref="teacher"/>
</bean>

或name指定参数名,必须在编译时加上 -parameters 参数

<bean id="student" class="com.test.bean.Student">
    <constructor-arg name="name" value="张三"/>
    <constructor-arg name="age" value="18" type="int"/>
    <constructor-arg name="teacher" ref="teacher"/>
</bean>
为什么 <property> 注入不需要 -parameters,而 <constructor-arg name="..."> 需要?

因为:

  • <property name="teacher"> → Spring 用 JavaBean 规范 自动生成方法名 setTeacher,通过 方法名 + 参数类型 定位 setter,完全绕开了参数名
  • <constructor-arg name="age"> → Spring 必须读取构造函数的 真实参数名,而 Java 默认不保存它,所以需要 -parameters 来保留。
Setter 注入靠“约定”(命名规则),构造器注入靠“元数据”(参数名)

自动装配

不再手写property,而是让IoC容器自己寻找。

<bean name="student" class="com.test.bean.Student" autowire="byType"/>

当有多个候选项时,无法自动装配:

解决方法一:选定一个bean,关闭其他候选bean的自动装配

autowire-candidate="false"

<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="teacher2" class="com.test.bean.ProgramTeacher" autowire-candidate="false"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

解决方法二:选定一个bean,设定primary属性,作为首选项

primary="true"

<bean name="teacher" class="com.test.bean.ArtTeacher" primary="true"/>
<bean name="teacher2" class="com.test.bean.ProgramTeacher"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

三、bean的生命周期/bean的继承/工厂bean

生命周期

继承

工厂bean

四、使用注解开发

分类: Java-Backend 标签: JavaSpring

评论

暂无评论数据

暂无评论数据

目录