Spring(2-IOC/DI管理第三方)

IOC/DI配置管理第三方bean

数据库连接配置详解

数据库连接四要素

properties 复制代码
# 数据库连接配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=root

jdbc是自定义的,后面会提到

驱动类 ( .driver)

什么是驱动类?

  • 驱动类是Java程序与数据库通信的桥梁
  • 不同数据库有不同的驱动类

如何确定驱动类?

  1. 先确定数据库类型和版本

因为不同数据库、不同版本的对应的驱动类不同。

方法一:

bash 复制代码
# 连接到数据库后执行
mysql> SELECT version();

方法二:查看项目依赖

因为不同版本的驱动jar包不同,所以需要查看项目使用了哪个数据库驱动jar包以及这个jar包的版本号,以找到对应的驱动类名

查看项目的pom.xml文件,确定使用的数据库驱动版本(如果是新项目,则需要自己添加项目依赖):

xml 复制代码
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>  <!-- 这个就是版本号 -->
</dependency>
<!-- 这个版本支持 com.mysql.cj.jdbc.Driver 驱动类 -->

方法三:如果使用Navicat、DBeaver等工具,通常在连接信息中就能看到数据库和版本号。

  1. 再参考具体对应关系:
properties 复制代码
# MySQL 8.0及以上版本
jdbc.driver=com.mysql.cj.jdbc.Driver

# MySQL 5.x版本
jdbc.driver=com.mysql.jdbc.Driver

# Oracle数据库
jdbc.driver=oracle.jdbc.OracleDriver

# PostgreSQL数据库
jdbc.driver=org.postgresql.Driver

# SQL Server数据库
jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
连接URL ( .url)

参考官方文档能找到URL的正确格式和必要参数: MySQL Connector/J 开发者指南 / Connector/J 参考 / 连接 URL 语法

URL格式解析:

ini 复制代码
jdbc:mysql://主机地址:端口号/数据库名?参数1=值1&参数2=值2

各部分含义:

properties 复制代码
# 本地MySQL,数据库名为spring_db
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db

# 远程服务器,指定字符编码和时区
jdbc.url=jdbc:mysql://192.168.1.100:3306/myapp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

必要参数

  • MySQL 8.0+ 需要 serverTimezone 参数
  • 开发环境通常设置 useSSL=false
  • 可能需要的其他参数:characterEncoding, allowPublicKeyRetrieval
用户名和密码
properties 复制代码
# 根据你的数据库设置填写
jdbc.username=你的数据库用户名
jdbc.password=你的数据库密码

连接池配置参数

连接池数据源类

java 复制代码
// Druid连接池
com.alibaba.druid.pool.DruidDataSource

// C3P0连接池
com.mchange.v2.c3p0.ComboPooledDataSource

// HikariCP连接池(Spring Boot默认)
com.zaxxer.hikari.HikariDataSource

// Apache DBCP
org.apache.commons.dbcp2.BasicDataSource
properties 复制代码
# 连接池通用配置
pool.initialSize=5
pool.minIdle=5
pool.maxActive=20
pool.maxWait=60000

参数含义解释

参数名 含义 推荐值
initialSize 连接池启动时创建的初始连接数 5-10
minIdle 连接池中保持的最小空闲连接数 5-10
maxActive 连接池中最大活动连接数 20-50
maxWait 获取连接的最大等待时间(毫秒) 60000

关于前缀的解释

自定义前缀只是为了分类管理,可以任意命名,或者不加前缀

properties 复制代码
# 自定义前缀只是为了分类管理,可以任意命名
database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://...
database.username=root

connection.initialSize=5
connection.maxActive=20

# 或者不加前缀
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://...
username=root

案例:数据源对象管理

在本节中,我们将通过实际案例学习如何配置和管理第三方jar包中的bean。我们将使用两种常见的数据源Druid(德鲁伊)C3P0为例进行演示。

环境准备

  1. 创建Maven项目

  2. pom.xml依赖配置

    xml 复制代码
    <dependencies>
         <!-- Spring核心依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
  3. Spring配置文件

    在resources目录下创建applicationContext.xml:

    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
                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
     <!-- 后续将在这里配置数据源bean -->
    
    </beans>
  4. 应用程序入口类

    java 复制代码
    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
    }

思路分析

现在我们来分析如何配置和管理第三方数据源对象:

需求分析: 使用Spring的IOC容器管理Druid和C3P0连接池对象

实现思路:

  1. 添加第三方依赖 - 在pom.xml中添加Druid(或 C3P0)和数据库驱动的依赖
  2. 配置第三方Bean - 在Spring配置文件中将第三方类声明为bean
  3. 属性注入 - 将数据库连接四要素(驱动、URL、用户名、密码)注入到bean中
  4. 测试验证 - 从IOC容器获取bean并验证配置是否正确

关键技术点:

  • 第三方类的识别 :Druid的com.alibaba.druid.pool.DruidDataSource和C3P0的com.mchange.v2.c3p0.ComboPooledDataSource
  • 属性注入方式:使用setter方法注入基本类型属性
  • 特殊属性处理:对于需要特殊配置的属性(如连接池参数)也需要进行注入

Druid数据源管理实现

步骤1:添加Druid依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

步骤2:配置 第三方bean-DruidDataSource Bean

xml 复制代码
<beans>
    <!-- 管理DruidDataSource对象 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">  <!-- class="连接池数据源类" -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

配置说明:

属性名 说明 示例值
driverClassName 数据库驱动类 com.mysql.jdbc.Driver
url 数据库连接地址 jdbc:mysql://localhost:3306/spring_db
username 数据库用户名 root
password 数据库密码 root

注意:请根据实际数据库配置调整连接四要素。

步骤3:从IOC容器获取Bean

java 复制代码
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = (DataSource) ctx.getBean("dataSource");
        System.out.println(dataSource);
    }
}

步骤4:运行验证

程序成功运行并输出DruidDataSource对象,表明第三方bean已被Spring IOC容器成功管理。\

Druid程序运行虽然没有报错,但是当调用DruidDataSource的getConnection()方法获取连接的时候,也会因为没有添加MySQL驱动依赖而报错

C3P0数据源管理实现

步骤1:添加C3P0依赖

xml 复制代码
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

依赖查找方法:

步骤2:配置C3P0 Bean

xml 复制代码
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
    <property name="maxPoolSize" value="1000"/>
</bean>

步骤3:解决驱动缺失问题

运行程序时可能出现ClassNotFoundException,提示缺少com.mysql.jdbc.Driver类。

解决方案: 添加MySQL驱动依赖

xml 复制代码
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

步骤4:验证结果

添加驱动依赖后,程序成功运行并输出C3P0数据源对象。

关键注意事项

1. 属性配置差异

不同数据源的属性名称可能存在差异,配置时需注意:

数据源 驱动属性 URL属性 用户名属性 密码属性
Druid driverClassName url username password
C3P0 driverClass jdbcUrl user password

2. 驱动加载时机

  • Druid:初始化时不立即加载驱动,获取连接时才加载
  • C3P0:初始化时立即尝试加载驱动(如果不添加MySQL驱动依赖,初始化时即报错)

3. 扩展属性配置

数据源除了基本的四要素外,还可以配置其他属性:

xml 复制代码
<!-- Druid额外配置示例 -->
<bean class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 基础四要素 -->
    <property name="driverClassName" value="..."/>
    <property name="url" value="..."/>
    <property name="username" value="..."/>
    <property name="password" value="..."/>
    
    <!-- 连接池配置 -->
    <property name="initialSize" value="5"/>
    <property name="maxActive" value="20"/>
    <property name="maxWait" value="60000"/>
</bean>

加载Properties文件

在上一节中,我们已经完成了Druid和C3P0数据源的配置,但存在以下问题需要优化:

问题分析:

数据源配置中的数据库连接四要素(驱动、URL、用户名、密码)是硬编码形式,直接写在Spring配置文件中不利于后期维护,需要将这些值提取到外部的properties配置文件中,Spring框架需要能够从配置文件中读取属性值并完成属性注入。

第三方Bean属性优化

实现思路

需求目标: 将数据库连接四要素提取到properties配置文件,由Spring加载配置信息并使用这些信息完成属性注入。

实现步骤:

  1. 在resources目录下创建jdbc.properties配置文件(文件名可自定义)
  2. 将数据库连接四要素配置到properties文件中
  3. 在Spring配置文件中加载properties文件
  4. 使用加载到的值实现属性注入

其中第3、4步骤是实现的关键,需要重点关注。

实现步骤

步骤1:准备properties配置文件

src/main/resources目录下创建jdbc.properties配置文件,并添加数据库连接配置:

properties 复制代码
# 数据库连接配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=root

# 连接池通用配置
pool.initialSize=5
pool.minIdle=5
pool.maxActive=20
pool.maxWait=60000

步骤2:开启context命名空间

applicationContext.xml中开启context命名空间:

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 后续配置将在这里添加 -->
    
</beans>

步骤3:加载properties配置文件

在Spring配置文件中使用context命名空间下的标签加载properties配置文件:

xml 复制代码
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

配置说明:

  • location 属性指定配置文件的路径
  • classpath: 前缀表示从类路径下查找文件
    • 类路径包括:src/main/resourcessrc/test/resources、以及jar包中的资源
    • 不加前缀时,Spring也会尝试从类路径查找,但明确使用classpath:更安全
  • 可以加载多个配置文件,用逗号分隔:location="classpath:jdbc.properties,classpath:redis.properties"

步骤4:完成属性注入

使用${key}表达式读取properties配置文件中的内容并完成属性注入:

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 加载properties配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 使用${}表达式读取properties中的值 -->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        
        <!-- 连接池参数也使用配置文件 -->
        <property name="initialSize" value="${pool.initialSize}"/>
        <property name="minIdle" value="${pool.minIdle}"/>
        <property name="maxActive" value="${pool.maxActive}"/>
        <property name="maxWait" value="${pool.maxWait}"/>
    </bean>
</beans>
高级配置选项

多配置文件加载

xml 复制代码
<!-- 加载多个properties文件 -->
<context:property-placeholder 
    location="classpath:jdbc.properties,
              classpath:redis.properties,
              classpath:system.properties"/>

设置字符编码

xml 复制代码
<!-- 指定properties文件编码 -->
<context:property-placeholder 
    location="classpath:jdbc.properties"
    encoding="UTF-8"/>

忽略资源未找到

xml 复制代码
<!-- 忽略未找到的资源文件 -->
<context:property-placeholder 
    location="classpath:jdbc.properties,
              classpath:optional.properties"
    ignore-resource-not-found="true"/>

忽略无法解析的占位符

xml 复制代码
<!-- 忽略无法解析的占位符 -->
<context:property-placeholder 
    location="classpath:jdbc.properties"
    ignore-unresolvable="true"/>

注意事项:

  • 确保properties文件在类路径下
  • 属性key命名要有意义且唯一
  • 生产环境中敏感信息建议使用加密或环境变量
  • 可以使用@Value注解在Java代码中直接注入properties值

读取单个属性

实现思路

为了更好地演示从properties配置文件中读取单个属性的效果,我们使用一个新的案例:

需求说明:

  • 从properties配置文件中读取key为name的值
  • 将该值注入到BookDao中
  • 在BookDao的save方法中打印该属性值

实现步骤:

  1. 在项目中添加BookDao和BookDaoImpl类
  2. 为BookDaoImpl添加一个name属性并提供setter方法
  3. 在jdbc.properties中添加name属性
  4. 在applicationContext.xml中添加配置,完成配置文件加载和属性注入(${key})
实现步骤

步骤1:创建项目中的相关类

BookDao接口:

java 复制代码
package com.itheima.dao;

public interface BookDao {
    void save();
}

BookDaoImpl实现类:

java 复制代码
package com.itheima.dao.impl;

import com.itheima.dao.BookDao;

public class BookDaoImpl implements BookDao {
    private String name;

    // 为name属性提供setter方法,用于属性注入
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

步骤2:更新properties配置文件

jdbc.properties配置文件中添加name属性:

properties 复制代码
# 数据库连接配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=root

# 连接池通用配置
pool.initialSize=5
pool.minIdle=5
pool.maxActive=20
pool.maxWait=60000

# 自定义属性 - 用于单个属性读取演示(也可以不额外添加,注入已有属性,如jdbc.driver)
book.name=Spring开发指南

步骤3:配置Spring配置文件

applicationContext.xml中完成bean配置、properties文件加载和属性注入:

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 1. 加载properties配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 2. 配置BookDao bean并注入单个属性 -->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <property name="name" value="${book.name}"/>
    </bean>
    <!-- 也可以注入已有属性,如 jdbc.driver -->
    <!-- <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> -->
        <!-- <property name="name" value="${jdbc.driver}"/> -->
    <!-- </bean> -->
    
    <!-- 3. 数据源配置(原有的) -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="${pool.initialSize}"/>
        <property name="minIdle" value="${pool.minIdle}"/>
        <property name="maxActive" value="${pool.maxActive}"/>
        <property name="maxWait" value="${pool.maxWait}"/>
    </bean>

</beans>

步骤4:创建测试运行类

App测试类:

java 复制代码
package com.itheima;

import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        // 1. 创建Spring IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // 2. 从容器中获取BookDao bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        
        // 3. 调用方法,验证属性注入是否成功
        bookDao.save();
    }
}

预期运行结果

当运行App类时,控制台应该输出:

复制代码
book dao save ...Spring开发指南

注意事项

在使用Spring加载properties配置文件时,有几个重要的注意事项需要特别关注。

问题一:键值对的key为username引发的问题

具体问题 :当在properties文件中配置键值对将key设置为username,并打印属性username时,控制台显示的不是username的值,而是当前操作系统的用户名。

问题原因<context:property-placeholder/>标签不仅会加载指定的properties文件,还会加载系统的环境变量,而且系统环境变量的优先级更高

在Windows系统中,通常会有一个USERNAME环境变量,其值为当前登录用户的用户名。

查看系统环境变量:

java 复制代码
public static void main(String[] args) throws Exception{
    Map<String, String> env = System.getenv();
    System.out.println(env);
}

// System.getenv():调用Java系统类的方法,获取所有环境变量
// Map<String, String>:环境变量以"键值对"形式存储
//     String(键):环境变量名(如:`USERNAME`, `PATH`)
//     String(值):环境变量值(如:`zhangsan`, `C:\Windows\system32`)

解决方案

方案1:设置system-properties-mode属性

xml 复制代码
<!-- 设置不加载系统属性 -->
<context:property-placeholder 
    location="jdbc.properties" 
    system-properties-mode="NEVER"/>

system-properties-mode 的可选值:

  • ENVER:从不加载系统属性
  • FALLBACK:如果没有在指定属性中找到,则回退到系统属性(默认值)
  • OVERRIDE:系统属性覆盖指定的属性

方案2:避免使用冲突的key名称

properties 复制代码
# 使用有前缀的key,避免与系统环境变量冲突
db.username=root666
jdbc.username=root666
app.username=root666
问题二:多个配置文件的多种加载方式

场景描述:当项目中有多个properties配置文件需要加载时,Spring提供了多种配置方式。

配置文件内容:

jdbc.properties:

properties 复制代码
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root

jdbc2.properties:

properties 复制代码
username=root666
app.name=图书管理系统

多种加载方式

方式一:明确列出所有文件

xml 复制代码
<!-- 用逗号分隔多个配置文件 -->
<context:property-placeholder 
    location="jdbc.properties,jdbc2.properties" 
    system-properties-mode="NEVER"/>

适用场景:

  • 配置文件数量较少
  • 需要精确控制加载哪些文件

方式二:使用通配符

xml 复制代码
<!-- 加载所有properties文件 -->
<context:property-placeholder 
    location="*.properties" 
    system-properties-mode="NEVER"/>

注意事项:

  • 不够精确,可能加载到不需要的文件
  • 文件加载顺序不确定

方式三:使用classpath前缀

xml 复制代码
<!-- 从类路径根目录加载 -->
<context:property-placeholder 
    location="classpath:*.properties" 
    system-properties-mode="NEVER"/>

特点:

  • 标准写法
  • 只查找当前项目的类路径
  • 不会查找依赖项目中的配置文件

方式四:使用classpath*前缀

xml 复制代码
<!-- 从所有类路径加载 -->
<context:property-placeholder 
    location="classpath*:*.properties" 
    system-properties-mode="NEVER"/>

特点:

  • 查找当前项目及所有依赖项目的类路径
  • 适用于多模块项目

classpath:classpath:的区别

前缀 搜索范围 行为特点
classpath: 当前项目的类路径 找到第一个匹配的资源就停止
classpath*: 所有类路径(当前项目+所有依赖) 搜索所有类路径,返回所有匹配的资源
其他重要规则
  1. 属性覆盖规则 如果多个文件中有相同的key,后加载的会覆盖先加载的
properties 复制代码
# file1.properties
app.name=默认名称

# file2.properties  
app.name=覆盖后的名称
  1. 编码问题
xml 复制代码
<!-- 指定properties文件编码 -->
<context:property-placeholder 
    location="classpath:*.properties"
    file-encoding="UTF-8"
    system-properties-mode="NEVER"/>
  1. 忽略未找到的properties配置文件
xml 复制代码
<!-- 忽略不存在的配置文件 -->
<context:property-placeholder
    location="classpath:required.properties,
              classpath:optional.properties"
    ignore-resource-not-found="true"
    system-properties-mode="NEVER"/>
  1. 忽略无法解析的占位符
xml 复制代码
<!-- 忽略无法解析的${}占位符 -->
<context:property-placeholder 
    location="classpath:*.properties"
    ignore-unresolvable="true"
    system-properties-mode="NEVER"/>

核心容器详解

Spring核心容器是Spring框架的核心组成部分,主要负责Bean的创建、配置和管理。我们可以将核心容器简单理解为ApplicationContext,它是整个IOC(控制反转)功能的基础实现。

两种容器的创建方式

ClassPathXmlApplicationContext(推荐)

类路径下的XML配置文件方式:

java 复制代码
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

特点:

  • 从项目的classpath中查找配置文件
  • 路径相对简洁,便于项目迁移
  • 实际开发中最常用的方式

FileSystemXmlApplicationContext

文件系统下的XML配置文件方式:

java 复制代码
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\src\\main\\resources\\applicationContext.xml");

特点:

  • 从文件系统的绝对路径中查找配置文件
  • 需要写完整的绝对路径
  • 项目位置变化时需要修改代码,耦合度高
  • 了解即可,不推荐使用

Bean的三种获取方式

通过名称获取(需要类型转换)

java 复制代码
BookDao bookDao = (BookDao) ctx.getBean("bookDao");

优缺点:

  • ✅ 简单直接
  • ❌ 需要手动类型转换
  • 类型不安全,可能抛出ClassCastException

通过名称和类型获取

java 复制代码
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);

优缺点:

  • ✅ 类型安全,无需强制转换
  • ❌ 需要多传递一个类型参数

通过类型获取

java 复制代码
BookDao bookDao = ctx.getBean(BookDao.class);

优缺点:

  • ✅ 代码简洁,无需bean名称
  • ❌ 要求容器中该类型只能有一个对应的bean
  • ❌ 多个同类型bean时会抛出异常

容器类层次结构

(1)在IDEA中双击shift,输入BeanFactory

(2)点击进入BeanFactory类,ctrl+h,就能查看到如下结构的层次关系

从图中可以看出,容器类也是从无到有根据需要一层层叠加上来的,理解下这种设计思想------接口分层

  • 每层接口都专注于特定的功能
  • 上层接口继承下层接口的功能
  • 这种设计使得Spring容器具有良好的扩展性和灵活性

BeanFactory的使用与延迟加载

BeanFactory基础用法

使用BeanFactory来创建IOC容器的具体实现方式:

java 复制代码
public class AppForBeanFactory {
    public static void main(String[] args) {
        // 1. 创建资源对象
        Resource resources = new ClassPathResource("applicationContext.xml");
        
        // 2. 创建BeanFactory容器
        BeanFactory bf = new XmlBeanFactory(resources);
        
        // 3. 获取Bean对象
        BookDao bookDao = bf.getBean(BookDao.class);
        bookDao.save();
    }
}

BeanFactory与 ApplicationContext 加载时机对比

容器类型 加载时机 特点
BeanFactory 延迟加载 只有在调用getBean()时才创建bean实例
ApplicationContext 立即加载 容器初始化时就创建所有配置的bean实例

使用BeanFactory:不调用getBean()时,构造函数不会执行

使用ApplicationContext:容器创建时,构造函数立即执行

ApplicationContext实现延迟加载------lazy-init="true"

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 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 使用lazy-init="true"实现延迟加载 -->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
</beans>

小结

bean相关

依赖注入相关

Spring IOC/DI 注解开发

Spring 的 IOC/DI 配置开发虽然功能强大,但在配置文件的编写上相对复杂。为了真正简化代码开发,Spring 提供了注解开发方式。

我们将从两个主要方面讲解 注解开发:

  1. 注解开发定义 Bean(基于 2.5 版本注解)
  2. 纯注解开发(基于 3.0 版本注解)

环境准备

建立项目结构 和 初始代码 同前面 IOC和DI入门案例-代码实现 的基本一致。

运行类App改为:

java 复制代码
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
    }
}

注解开发定义bean

实现步骤

步骤 1: 删除原 XML 配置

删除 XML 配置文件中的所有<bean> 标签配置

步骤 2: Dao 层添加注解

在 BookDaoImpl 类上添加 @Component 注解:

java 复制代码
@Component("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

重要说明:

  • 注解应该添加在具体的实现类上,而不能添加在接口上
  • @Component("bookDao") 中的 "bookDao" 指定了 Bean 的 ID(而非name属性!)

XML 与注解配置对应关系:

步骤 3: 配置包扫描

为了让Spring框架能够扫描到写在类上的注解,在 XML 配置文件中添加 启用组件扫描功能:

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 配置组件扫描 -->
    <context:component-scan base-package="com.itheima"/>
</beans>

配置说明:

配置项 说明
context:component-scan 启用组件扫描功能
base-package 指定要扫描的包路径,将扫描指定包及其子包中的所有类上的注解

包扫描策略建议:

  • 精细路径 (如:com.itheima.dao.impl
    • 扫描范围小,速度快
    • 需要配置多个包路径
  • 粗粒度路径 (如:com.itheima
    • 扫描范围大,速度稍慢
    • 配置简单,推荐使用

实践建议: 一般扫描到项目的组织名称(Maven 的 groupId)即可(如:com.itheima)。

步骤 4: 运行测试

运行应用程序验证配置:

java 复制代码
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
    }
}

输出:

css 复制代码
com.itheima.dao.impl. BookDaoImpl@73d4cc9e

步骤 5: Service 层添加注解

在 BookServiceImpl 类上添加 @Component 注解:

java 复制代码
@Component
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

步骤 6: 验证 Service Bean

测试 Service 层的 Bean 定义:

java 复制代码
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        
        // 按类型获取 Bean
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

Bean 名称说明:

  • @Component 不指定名称时,使用默认名称指定 Bean 的 ID

  • 默认名称:类名首字母小写 (如:bookServiceImpl

  • 也可以通过默认名称获取 Bean:

    java 复制代码
    BookService bookService = (BookService) ctx.getBean("bookServiceImpl");
    // 默认名称是bookServiceImpl

组件注解详解

注解层次结构

Spring 提供了 @Component 及其衍生注解:

less 复制代码
@Component
    ├── @Controller    (表现层)
    ├── @Service       (业务层)
    └── @Repository    (数据层)

各层注解说明

注解 适用层次 主要用途
@Controller 表现层 标注控制器类
@Service 业务层 标注业务逻辑类
@Repository 数据层 标注数据访问类
@Component 通用层 标注其他组件类

注解详细说明

属性 说明
名称 @Component/@Controller/@Service/@Repository
类型 类注解
位置 类定义上方
作用 设置该类为 Spring 管理的 Bean
属性 value:定义 Bean 的 ID(默认值为类名首字母小写)

分层注解使用示例

java 复制代码
// 数据层
@Repository
public class UserDaoImpl implements UserDao {
    // ...
}

// 业务层
@Service
public class UserServiceImpl implements UserService {
    // ...
}

// 表现层
@Controller
public class UserController {
    // ...
}

// 其他组件
@Component
public class CustomComponent {
    // ...
}

纯注解开发模式

Spring 3.0版本开始支持纯注解开发模式,使用Java类替代传统的XML配置文件,大大简化了Spring应用的配置过程,提高了开发效率。

实现步骤

步骤 1: 创建配置类

首先创建一个普通的Java类作为配置类:

java 复制代码
public class SpringConfig {
}

步骤 2: 标识配置类

使用@Configuration注解标识该类为Spring配置类,替代 applicationContext.xml配置文件:

java 复制代码
@Configuration
public class SpringConfig {
}

步骤 3: 配置包扫描

使用@ComponentScan注解配置组件扫描路径,替代 XML中的<context:component-scan/>

java 复制代码
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}

步骤 4: 创建运行类并执行

创建新的运行类,使用注解配置方式初始化Spring容器:

java 复制代码
public class AppForAnnotation {

    public static void main(String[] args) {
        // 加载配置类初始化容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

核心注解详解

@Configuration

属性 说明
名称 @Configuration
类型 类注解
位置 类定义上方
作用 标识该类为Spring配置类
属性 value(默认):定义bean的id

@ComponentScan

属性 说明
名称 @ComponentScan
类型 类注解
位置 类定义上方
作用 设置Spring配置类扫描路径,用于加载使用注解格式定义的bean
属性 value(默认):扫描路径,此路径可以逐层向下扫描
多包扫描配置 多个数据用数组格式(如:@ComponentScan({"com.itheima.service", "com.itheima.dao"})

注解替代对象

  • @Configuration:标识配置类,替代XML配置文件
  • @ComponentScan:配置组件扫描路径,替代<context:component-scan/>

两种容器初始化方式

加载XML配置文件初始化容器

java 复制代码
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

加载配置类初始化容器

java 复制代码
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
  • ClassPathXmlApplicationContext:用于加载XML配置文件
  • AnnotationConfigApplicationContext:用于加载配置类

注解开发Bean作用范围与生命周期管理

在使用注解完成Bean的基本管理后,本节将学习如何通过注解配置Bean的作用范围和生命周期,替代之前通过XML配置实现的功能。

环境准备

  1. 创建Maven项目
  2. 添加Spring依赖
  3. 创建Spring配置类
java 复制代码
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
  1. 核心类定义

BookDao接口

java 复制代码
public interface BookDao {
    public void save();
}

BookDaoImpl实现类

java 复制代码
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

应用程序入口

java 复制代码
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao1 = ctx.getBean(BookDao.class);
        BookDao bookDao2 = ctx.getBean(BookDao.class);
        System.out.println(bookDao1);
        System.out.println(bookDao2);
    }
}

Bean的作用范围 @Scope

默认行为验证

运行App类,控制台输出显示两个相同的地址,表明默认情况下Bean是单例的

kotlin 复制代码
com.itheima.dao.BookDaoImpl@5e025e70
com.itheima.dao.BookDaoImpl@5e025e70

配置非单例作用域

使用@Scope注解将BookDaoImpl配置为非单例:

java 复制代码
@Repository
@Scope("prototype")  // 设置Bean作用范围为非单例
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

再次运行App类,输出显示两个不同的地址,证明Bean已变为非单例:

kotlin 复制代码
com.itheima.dao.BookDaoImpl@5e025e70
com.itheima.dao.BookDaoImpl@1d296da

注解详解:@Scope

属性 说明
名称 @Scope
类型 类注解
位置 类定义上方
作用 设置该类创建对象的作用范围,控制创建的Bean是否为单例对象
属性 value:定义Bean作用范围 默认值singleton(单例) 可选值prototype(非单例)

Bean的生命周期管理 @PostConstruct、@PreDestroy

方法定义与注解配置

通过生命周期方法定义

在BookDaoImpl中添加初始化和销毁方法:

java 复制代码
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    
    public void init() {
        System.out.println("init ...");
    }
    
    public void destroy() {
        System.out.println("destroy ...");
    }
}

通过生命周期注解配置

使用注解标识初始化和销毁方法:

java 复制代码
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    
    @PostConstruct  // 在构造方法之后执行,替代init-method
    public void init() {
        System.out.println("init ...");
    }
    
    @PreDestroy     // 在销毁方法之前执行,替代destroy-method
    public void destroy() {
        System.out.println("destroy ...");
    }
}

验证生命周期方法

修改App类以触发销毁方法:

java 复制代码
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao1 = ctx.getBean(BookDao.class);
        BookDao bookDao2 = ctx.getBean(BookDao.class);
        System.out.println(bookDao1);
        System.out.println(bookDao2);
        ctx.close(); // 关闭容器,触发销毁方法
    }
}

运行结果验证初始化和销毁方法都被正确执行:

kotlin 复制代码
init ...
com.itheima.dao.BookDaoImpl@5e025e70
com.itheima.dao.BookDaoImpl@1d296da
destroy ...
注解详解

重要提示 :如果@PostConstruct@PreDestroy注解无法找到,需要添加以下依赖:

xml 复制代码
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

原因:从JDK9开始,javax.annotation包被从JDK中移除,而这两个注解恰好位于该包中。

@PostConstruct & @PreDestroy

属性 说明 说明
名称 @PostConstruct @PreDestroy
类型 方法注解 方法注解
位置 方法上一行 方法上一行
作用 设置该方法为初始化方法 设置该方法为销毁方法

执行流程:Bean创建 → 构造函数 → @PostConstruct方法 → Bean就绪 → 容器关闭 → @PreDestroy方法

XML配置与注解配置 成分对应关系 :\

注解开发依赖注入

Spring框架为了简化开发,提供了注解方式的依赖注入实现。虽然没有直接提供构造函数注入和setter注入的专用注解,但通过自动装配注解可以更简洁地完成依赖注入。

环境准备

建立项目结构的步骤 同前面 IOC和DI入门案例-代码实现 的基本一致

额外添加一个配置类:

java 复制代码
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}

环境准备好后,运行后会发现有问题:\

问题原因 :在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,但是目前是没有提供配置注入BookDao的,所以bookDao对象为Null,调用其save方法就会报 控指针异常

@Autowired 按类型注入

在BookServiceImpl类的bookDao属性上添加@Autowired注解实现自动装配:

java 复制代码
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

核心要点:

  • @Autowired可以写在属性上,也可以写在setter方法上
  • 推荐使用方式:直接写在属性上并删除setter方法
  • 工作原理:基于反射机制创建对象,通过暴力反射为私有属性设值

技术原理说明:

  • 普通反射只能获取public修饰的内容
  • 暴力反射还可以获取private修饰的内容
  • 因此无需提供setter方法即可完成依赖注入

多实现类冲突问题

当接口有多个实现类时,会出现注入冲突:

java 复制代码
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...1");
    }
}

@Repository
public class BookDaoImpl2 implements BookDao {
    public void save() {
        System.out.println("book dao save ...2");
    }
}

运行结果:报错 NoUniqueBeanDefinitionException

解决方案:按id名称匹配

通过为Bean指定名称来解决多实现类问题:

java 复制代码
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...1");
    }
}

@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
    public void save() {
        System.out.println("book dao save ...2");
    }
}

@Autowired匹配规则:

  1. 首先按照类型进行自动装配
  2. 如果找到多个同类型Bean,则按照变量名Bean名称 进行匹配(变量名 指接受注入的属性字段的名称;Bean名称指id属性)
  3. 变量名bookDao会匹配到名称id为bookDao的Bean
  4. 找不到匹配的变量名与Bean名称会报错

@Qualifier 按名称注入

当需要明确指定注入特定名称的Bean时,使用@Qualifier注解:

java 复制代码
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDao1")
    private BookDao bookDao;
    
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

重要规则:

  • @Qualifier必须与@Autowired一起使用
  • @Qualifier注解的参数指定要注入的Bean名称id

@Value 简单数据类型注入

对于基本数据类型字符串类型 的注入,使用@Value注解:

java 复制代码
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    @Value("itheima")
    private String name;
    
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

注意事项:

  • 必须确保数据格式匹配(如字符串不能注入到int类型)
  • 硬编码方式将值直接写入注解的参数

@Value注解通常用于从properties配置文件中读取配置值,实现外部化配置管理。

注解读取Properties配置文件 @PropertySource

@PropertySource + @Value 组合实现配置读取

实现步骤

步骤1:创建Properties配置文件

在resource目录下创建配置文件 jdbc.properties:

properties 复制代码
name=itheima888

步骤2:加载Properties配置文件

在Spring配置类中使用@PropertySource注解加载配置文件:

java 复制代码
@Configuration
@ComponentScan("com.itheima")
@PropertySource("jdbc.properties")
public class SpringConfig {
}

步骤3:读取配置值

在Bean中使用@Value注解读取配置文件中的值:

java 复制代码
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    @Value("${name}")
    private String name;
    // @Value("${app.timeout:5000}")  默认值
    // private int timeout;
    
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

步骤4:验证结果

运行应用程序,控制台输出应显示配置值已成功加载:

复制代码
book dao save ...itheima888
高级配置选项

多配置文件加载:支持同时加载多个properties配置文件

java 复制代码
@PropertySource({"jdbc.properties", "application.properties", "config.properties"})

类路径指定:明确指定从类路径加载配置文件

java 复制代码
@PropertySource("classpath:jdbc.properties")

不支持通配符 :不能使用*.properties模式匹配

java 复制代码
// 错误用法 - 会导致运行报错
@PropertySource({"*.properties"})

技术要点总结

  1. 注解组合:@PropertySource + @Value 实现配置读取
  2. 路径规范:建议使用classpath:前缀指定明确路径
  3. 多文件支持:通过数组形式加载多个配置文件
  4. 默认值支持:@Value("${key:defaultValue}") 语法提供默认值
  5. 编码要求:配置文件必须使用正确的字符编码

注解详解

@Autowired

名称 @Autowired
类型 属性注解、方法注解(了解)、方法形参注解(了解)
位置 属性定义上方、setter方法上方、方法形参前面
作用 为引用类型属性设置值
属性 required:true/false,定义该属性是否允许为null

@Qualifier

属性 说明
类型 属性注解、方法注解(了解)
位置 属性定义上方、setter方法上方
作用 为引用类型属性指定注入的beanId
属性 value:设置注入的bean Id

@Value

属性 说明
类型 属性注解、方法注解(了解)
位置 属性定义上方、setter方法上方
作用 为基本数据类型或字符串类型属性设置值
属性 value:要注入的属性值

@PropertySource

属性 说明
类型 类注解
位置 类定义上方
作用 加载properties文件中的属性值
属性 value:设置加载的properties文件名

IOC/DI注解开发管理第三方bean

在Spring框架中,当我们管理自己开发的类时,可以直接在类上使用注解(如@Component@Service等)来定义Bean。但对于第三方库中的类,由于无法修改第三方库中的类源代码 ,不能直接在类上添加注解。此时,我们需要使用@Bean注解来灵活地管理第三方Bean。

环境准备

项目结构

  • Maven项目
  • 添加Spring依赖
  • 创建配置类和相关组件

配置类

java 复制代码
@Configuration
public class SpringConfig {
}

数据访问层

java 复制代码
public interface BookDao {
    public void save();
}

@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

应用启动类

java 复制代码
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = 
            new AnnotationConfigApplicationContext(SpringConfig.class);
    }
}

注解开发管理第三方bean

在上述环境中完成对Druid数据源的管理,具体的实现步骤为:

步骤1:添加Druid依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

步骤2:在配置类中创建Bean方法

在配置类中添加一个方法,该方法返回要创建的Bean对象类型,并在方法上添加@Bean注解

java 复制代码
@Configuration
public class SpringConfig {
    
    @Bean
    public DataSource dataSource(){
        // 创建Druid数据源实例
        DruidDataSource ds = new DruidDataSource();
        
        // 配置数据源连接参数
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        
        return ds;
    }
}

关键说明

  1. @Bean注解作用:将方法的返回值制作为Spring容器管理的一个Bean对象
  2. 方法命名:方法名默认作为Bean的名称
  3. 返回值类型:方法的返回值类型决定了Bean的类型
  4. 实现方式 :必须使用具体实现类(DruidDataSource)而非接口(DataSource)来创建实例,因为接口中没有对应的setter方法

步骤3:从IOC容器获取并使用Bean

java 复制代码
public class App {
    public static void main(String[] args) {
        // 创建注解配置应用上下文
        AnnotationConfigApplicationContext ctx = 
            new AnnotationConfigApplicationContext(SpringConfig.class);
        
        // 从容器中获取数据源Bean
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource);
        
        // 关闭上下文
        ctx.close();
    }
}

至此使用@Bean来管理第三方bean的案例就已经完成。

扩展应用:管理多个第三方Bean

如果需要管理多个第三方Bean,只需在配置类中添加多个使用@Bean注解的方法:

java 复制代码
@Configuration
public class SpringConfig {
    
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
    
    @Bean
    public AnotherThirdPartyBean anotherBean(){
        return new AnotherThirdPartyBean();
    }
    
    // 可以继续添加更多的@Bean方法...
}

引入外部配置类

概述

在实际项目中,如果将所有的第三方Bean都配置在单一的Spring配置类中,会导致:

  • 配置类过于臃肿,代码可读性差
  • 不利于按功能模块进行分类管理,维护困难

问题场景

将所有第三方Bean配置在SpringConfig中时:

java 复制代码
@Configuration
public class SpringConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
    
    // 其他第三方Bean配置...
    // 导致配置类越来越庞大,难以维护
}

为此,Spring提供了两种主要方式来引入外部配置类:包扫描引入、@Import引入。

使用包扫描引入

步骤1:创建独立的配置类

将数据源配置提取到独立的JdbcConfig类中:

java 复制代码
@Configuration
public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

步骤2:在Spring的配置类中配置包扫描

java 复制代码
@Configuration
@ComponentScan("com.itheima.config")  // 关键:扫描指定包下的所有配置类
public class SpringConfig {
    // 主配置类保持简洁
}

步骤3:确保配置类在扫描路径中

JdbcConfig类必须位于com.itheima.config包或其子包下。

优缺点分析

  • 优点:自动扫描,配置简单
  • 缺点
    • 无法快速知晓引入了哪些具体配置类
    • 依赖包结构,不够灵活
    • 不推荐在生产环境中使用

使用@Import引入(推荐)

步骤1:创建配置类(可省略@Configuration)

java 复制代码
public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

注意 :使用@Import时,配置类上的@Configuration注解可以省略。

步骤2:在Spring配置类中使用@Import引入

java 复制代码
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig {
    // 主配置类
}
  • 引入多个配置类 :可以以数组形式引入多个配置类,而不能在一个Spring配置类中多次使用@Import注解

    java 复制代码
    @Configuration
    @Import({
        JdbcConfig.class,      // 数据源配置
        CacheConfig.class      // 缓存配置
        // 可以继续添加其他配置类...
    })
    public class SpringConfig {
        // 一次性引入多个配置类
    }

总结

方案对比

特性 包扫描方式 @Import方式
配置复杂度 简单 简单
可读性 较差 优秀
灵活性 依赖包结构 灵活指定
显式控制
推荐程度 不推荐 推荐

核心注解详解

@Bean 注解

属性 说明
名称 @Bean
类型 方法注解
位置 方法定义上方
作用 设置该方法的返回值作为Spring管理的Bean
属性 value(默认):定义Bean的id,默认值为方法名

使用示例:

java 复制代码
@Bean("myDataSource")  // 自定义Bean名称
public DataSource dataSource(){
    return new DruidDataSource();
}

@Import 注解

属性 说明
名称 @Import
类型 类注解
位置 类定义上方
作用 导入配置类
属性 value:定义导入的配置类类名,导入多个时使用数组格式

使用示例:

java 复制代码
@Import({JdbcConfig.class, CacheConfig.class, SecurityConfig.class})

实践推荐

  1. 按功能模块划分配置
java 复制代码
// 数据库配置
public class JdbcConfig {
    @Bean
    public DataSource dataSource() {
        // 数据源配置
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

// 缓存配置
public class CacheConfig {
    @Bean
    public RedisTemplate redisTemplate() {
        // Redis配置
    }
}

// 消息队列配置
public class MqConfig {
    @Bean
    public RabbitTemplate rabbitTemplate() {
        // RabbitMQ配置
    }
}
  1. 主配置类统一管理
java 复制代码
@Configuration
@Import({
    JdbcConfig.class,      // 数据库相关配置
    CacheConfig.class,     // 缓存相关配置  
    MqConfig.class,        // 消息队列配置
    ServiceConfig.class    // 服务层配置
})
@PropertySource("classpath:application.properties")
public class SpringConfig {
    // 主配置类保持简洁,只做配置聚合
}

注解开发实现为第三方bean注入资源

概述

在使用@Bean注解创建第三方Bean对象时,经常需要在创建过程中注入其他资源。这些资源主要分为两大类:

  • 简单数据类型:字符串、数值等基本类型
  • 引用数据类型:其他Spring管理的Bean对象

简单数据类型注入

需求分析

在配置数据源时,数据库连接参数(驱动类名、URL、用户名、密码)不应该硬编码在代码中,而应该从外部配置文件读取,提高配置的灵活性和可维护性。

原始硬编码方式(不推荐):

java 复制代码
public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        // 硬编码数据库配置(存在问题)
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}
实现步骤

步骤1:定义配置属性字段

在配置类中定义需要注入的属性字段:

java 复制代码
public class JdbcConfig {
    // 定义数据库连接配置属性
    private String driver;
    private String url;
    private String userName;
    private String password;
    
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        // 暂时仍使用硬编码(后续改进)
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

步骤2:使用@Value注解注入值

使用@Value注解为属性注入具体的值:

java 复制代码
public class JdbcConfig {
    @Value("com.mysql.jdbc.Driver")
    private String driver;
    
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    
    @Value("root")
    private String userName;
    
    @Value("password")
    private String password;
    
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        // 使用注入的属性值
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
扩展:使用配置文件注入(生产环境推荐)

步骤1:创建properties配置文件

resources目录下创建jdbc.properties文件:

properties 复制代码
# 数据库连接配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db
jdbc.username=root
jdbc.password=123456

# 连接池配置
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.minIdle=5

步骤2:加载配置文件并注入属性

java 复制代码
// 关键:使用@PropertySource加载配置文件
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    
    // 关键:使用${key}格式从配置文件中读取值
    @Value("${jdbc.driver}")
    private String driver;
    
    @Value("${jdbc.url}")
    private String url;
    
    @Value("${jdbc.username}")
    private String userName;
    
    @Value("${jdbc.password}")
    private String password;
    
    @Value("${jdbc.initialSize:5}")  // 默认值5
    private int initialSize;
    
    @Value("${jdbc.maxActive:20}")   // 默认值20
    private int maxActive;
    
    @Value("${jdbc.minIdle:5}")      // 默认值5
    private int minIdle;
    
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        
        // 使用配置文件中的值
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        
        // 设置连接池参数
        ds.setInitialSize(initialSize);
        ds.setMaxActive(maxActive);
        ds.setMinIdle(minIdle);
        
        return ds;
    }
}

步骤3:主配置类引入

java 复制代码
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig {
    // 主配置类
}

@Value注解详解

属性 说明
名称 @Value
类型 字段注解、方法参数注解
位置 字段定义上方、方法参数前
作用 为基本数据类型或字符串类型字段注入值
常用格式 @Value("硬编码值") @Value("${配置文件key}") @Value("${配置文件key:默认值}")

引用数据类型注入

需求分析

在创建第三方Bean时,可能需要依赖其他Spring管理的Bean。例如,配置数据源时需要使用BookDao进行某些操作。

实现步骤

步骤1:确保依赖Bean被Spring管理

首先确保BookDao被Spring扫描并管理:

java 复制代码
// BookDao接口
public interface BookDao {
    void save();
    void update();
}

// BookDao实现类
@Repository  // 关键:让Spring管理此Bean
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    
    public void update() {
        System.out.println("book dao update ...");
    }
}

步骤2:配置组件扫描

在主配置类中配置组件扫描路径:

java 复制代码
@Configuration
// 关键:扫描DAO包,让Spring管理BookDao等组件
@ComponentScan("com.itheima.dao")
@Import({JdbcConfig.class})
public class SpringConfig {
    // 主配置类
}

步骤3:在@Bean方法中注入引用类型

在JdbcConfig的dataSource方法中通过参数注入BookDao:

java 复制代码
public class JdbcConfig {
    
    @Value("${jdbc.driver}")
    private String driver;
    
    @Value("${jdbc.url}")
    private String url;
    
    @Value("${jdbc.username}")
    private String userName;
    
    @Value("${jdbc.password}")
    private String password;
    
    /**
     * 创建数据源Bean
     * 关键:在方法参数中声明需要注入的Bean,Spring会自动装配
     * 
     * @param bookDao Spring会自动注入BookDao实例
     * @return DataSource 数据源实例
     */
    @Bean
    public DataSource dataSource(BookDao bookDao) {       //(参数类型 自定义参数名称)
        // 可以在这里使用注入的bookDao
        System.out.println("注入的BookDao: " + bookDao);
        System.out.println("BookDao类型: " + bookDao.getClass().getName());
        
        // 演示使用bookDao
        bookDao.save();
        
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        
        return ds;
    }
    
    /** 可以注入多个依赖Bean的示例 */
    @Bean 
    public JdbcTemplate jdbcTemplate(DataSource dataSource, BookDao bookDao) {
        // 这里同时注入了dataSource和bookDao
        System.out.println("创建JdbcTemplate,使用数据源: " + dataSource);
        System.out.println("同时可访问BookDao: " + bookDao);
        
        return new JdbcTemplate(dataSource);
    }
}

注解开发总结

前面我们已经完成了XML配置和注解的开发实现,对比回顾两者之间的差异:

Spring整合

在进行企业级开发的时候,除了将自己写的类让Spring管理之外,还有一部分重要的工作就是整合第三方的技术。下面结合IoC和DI,整合2个常用技术,进一步加深对Spring的使用理解。

Spring整合Mybatis思路分析

环境准备

在整合之前,我们先回顾Mybatis开发的相关内容,并准备基础环境。

1. 数据库表准备

Mybatis是来操作数据库表,所以先创建一个数据库及表

sql 复制代码
-- 创建数据库
create database spring_db character set utf8;
use spring_db;

-- 创建账户表
create table tbl_account(
    id int primary key auto_increment,
    name varchar(35),
    money double
);

2. 项目依赖配置

在pom.xml中添加相关依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

3. 模型类创建

根据数据库表创建对应的实体类:

java 复制代码
public class Account implements Serializable {
    private Integer id;
    private String name;
    private Double money;
    
    // setter、getter、toString方法省略
}

4. 数据访问层接口

创建AccountDao接口,使用Mybatis注解方式:

java 复制代码
public interface AccountDao {
    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    @Delete("delete from tbl_account where id = #{id}")
    void delete(Integer id);

    @Update("update tbl_account set name = #{name}, money = #{money} where id = #{id}")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id = #{id}")
    Account findById(Integer id);
}

5. 业务层实现

创建Service接口和实现类:

java 复制代码
public interface AccountService {
    void save(Account account);
    void delete(Integer id);
    void update(Account account);
    List<Account> findAll();
    Account findById(Integer id);
}

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void save(Account account) {
        accountDao.save(account);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

6. 配置文件

添加 jdbc.properties - resources目录下添加,用于配置数据库连接四要素:

properties 复制代码
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root

useSSL:关闭MySQL的SSL连接

添加 Mybatis核心配置文件(SqlMapConfig.xml)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 读取外部properties配置文件 -->
    <properties resource="jdbc.properties"></properties>
    
    <!-- 别名扫描的包路径 -->
    <typeAliases>
        <package name="com.itheima.domain"/>
    </typeAliases>
    
    <!-- 数据源配置 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <!-- 映射文件扫描包路径 -->
    <mappers>
        <package name="com.itheima.dao"/>
    </mappers>
</configuration>

7. 编写应用程序

java 复制代码
public class App {
    public static void main(String[] args) throws IOException {
        // 1. 创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        
        // 2. 加载SqlMapConfig.xml配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        
        // 3. 创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        
        // 4. 获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        
        // 5. 获取Mapper接口代理对象并执行查询
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
        Account account = accountDao.findById(1);
        System.out.println(account);
        
        // 6. 释放资源
        sqlSession.close();
    }
}

8. 运行程序

通过以上步骤,我们完成了Mybatis基础环境的搭建。在后续的整合过程中,我们将使用Spring来管理这些组件,简化开发流程并提高代码的可维护性。

Spring整合Mybatis思路分析

在完成Mybatis基础环境搭建后,我们需要深入分析哪些对象可以交给Spring的IoC容器来管理,以实现两者的无缝整合。

Mybatis程序核心对象分析

通过对Mybatis运行流程的分析,我们可以识别出以下核心组件:

从图中可以获取到,真正需要交给Spring管理的是 SqlSessionFactory ,管理好SqlSessionFactory就等于管理了整个Mybatis的核心,其他组件都可以通过它来间接管理。

Mybatis配置文件分析

整合Mybatis,就是将Mybatis用到的内容交给Spring管理,详细分析Mybatis配置文件中各个部分与Spring整合的关系:

1. 外部properties配置文件读取

xml 复制代码
<properties resource="jdbc.properties"></properties>
  • Spring提供了专门的@PropertySource注解来加载properties配置文件
  • 可以通过Environment对象或@Value注解来读取配置属性
  • 需要交给Spring管理

2. 类型别名包扫描

xml 复制代码
<typeAliases>
    <package name="com.itheima.domain"/>
</typeAliases>
  • 在Spring配置中可以通过SqlSessionFactoryBean的typeAliasesPackage属性设置
  • 主要用于为SqlSessionFactory服务
  • 我们不需要直接将SqlSession交给Spring管理,SqlSession是由SqlSessionFactory创建出来的,SqlSessionFactory交给Spring管理,让Spring间接控制SqlSession的生命周期。
  • 需要交给Spring管理

3. 数据源与连接池配置

xml 复制代码
<dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>
  • Spring已经提供了完善的Druid连接池整合方案
  • 可以单独配置DataSource bean,实现更好的连接池管理
  • 需要交给Spring管理

4. 映射文件扫描配置

xml 复制代码
<mappers>
    <package name="com.itheima.dao"/>
</mappers>
  • 在Spring中可以通过MapperScannerConfigurer或@MapperScan注解实现
  • Mapper接口的实例化时机与SqlSessionFactory不同,需要单独管理
  • 需要交给Spring管理
  • 如果使用注解就没有该映射文件

整合策略总结

Mybatis组件 是否由Spring管理 Spring整合方案
外部properties配置 ✅ 是 @PropertySource + Environment
类型别名扫描 ✅ 是 SqlSessionFactoryBean.typeAliasesPackage
数据源连接池 ✅ 是 独立的DataSource Bean配置
SqlSessionFactory ✅ 是 SqlSessionFactoryBean
SqlSession ❌ 否 由SqlSessionFactory创建,线程级别对象
Mapper接口代理 ✅ 是 MapperScannerConfigurer或@MapperScan

Spring整合Mybatis

通过前面的分析,我们已经明确了Spring整合Mybatis需要完成的两项核心任务:

  1. Spring管理MyBatis中的SqlSessionFactory
  2. Spring管理Mapper接口的扫描

下面是具体的实现步骤:

步骤1:添加整合依赖包

在pom.xml中添加必要的依赖:

xml 复制代码
<dependencies>
    <!-- Spring操作数据库需要的jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    
    <!-- Spring与Mybatis整合的jar包(由Mybatis提供) -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.0</version>
    </dependency>
</dependencies>

步骤2:创建Spring主配置类

java 复制代码
/**
 * Spring主配置类
 * 负责整个应用的核心配置
 */
@Configuration
@ComponentScan("com.itheima")  // 包扫描,主要扫描项目中的业务组件如AccountServiceImpl
public class SpringConfig {
    // 主配置类,后续会逐步完善
}

步骤3:创建数据源配置类

java 复制代码
/**
 * 数据源配置类
 * 负责数据库连接池的配置和管理
 */
public class JdbcConfig {
    
    // 使用@Value注解注入properties文件中的配置值
    @Value("${jdbc.driver}")
    private String driver;
    
    @Value("${jdbc.url}")
    private String url;
    
    @Value("${jdbc.username}")
    private String userName;
    
    @Value("${jdbc.password}")
    private String password;

    /**
     * 创建数据源Bean
     * @return DruidDataSource实例
     */
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

步骤4:完善主配置类

java 复制代码
/**
 * 完善后的Spring主配置类
 * 整合properties配置和数据源配置
 */
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")  // 加载jdbc.properties配置文件
@Import(JdbcConfig.class)  // 引入数据源配置类
public class SpringConfig {
    // 现在配置类已经具备了数据库连接的能力
}

步骤5:创建Mybatis配置类

java 复制代码
/**
 * Mybatis配置类
 * 负责SqlSessionFactory和Mapper扫描的配置
 */
public class MybatisConfig {
    
    /**
     * 定义SqlSessionFactoryBean
     * 用于创建SqlSessionFactory对象
     * 
     * @param dataSource 数据源,Spring会自动注入
     * @return SqlSessionFactoryBean实例
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        
        // 设置模型类的别名扫描包路径
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        
        // 设置数据源
        ssfb.setDataSource(dataSource);
        
        return ssfb;
    }
    
    /**
     * 定义MapperScannerConfigurer
     * 用于扫描Mapper接口并创建代理对象
     * 
     * @return MapperScannerConfigurer实例
     */
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        
        // 设置Mapper接口的扫描包路径
        msc.setBasePackage("com.itheima.dao");
        
        return msc;
    }
}

核心组件说明

SqlSessionFactoryBean 的作用原理:

  • SqlSessionFactoryBean是Spring的FactoryBean接口的实现类,封装了SqlSessionFactory的创建过程;
  • Spring在需要SqlSessionFactory时,自动调用其getObject()方法,getObject()方法内部使用之前通过setter方法设置的配置(数据源、类型别名包等),构建并返回真正的SqlSessionFactory实例,完成Mybatis初始化
  • 它简化了配置,我们只需要设置必要的参数(数据源、类型别名包等)

MapperScannerConfigurer 的作用原理

  • MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor接口的实现类
  • 在Spring容器初始化阶段,它会自动扫描指定包下的所有接口,为每个Mapper接口创建BeanDefinition,并设置其工厂为MapperFactoryBean,MapperFactoryBean再为每个接口创建JDK动态代理对象
  • 这样Service层就能直接通过@Autowired注入Mapper接口

步骤6:整合Mybatis配置到主配置类

java 复制代码
/**
 * 完整的Spring主配置类
 * 整合了所有必要的配置
 */
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})  // 同时引入数据源和Mybatis配置
public class SpringConfig {
    // 现在配置类已经完整,包含了数据源和Mybatis的整合配置
}

步骤7:编写运行测试类

java 复制代码
/**
 * 应用运行类
 * 测试Spring与Mybatis的整合效果
 */
public class App2 {
    public static void main(String[] args) {
        // 创建Spring容器(基于注解配置)
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        // 从容器中获取AccountService实例
        AccountService accountService = ctx.getBean(AccountService.class);

        // 调用业务方法,测试整合效果
        Account account = accountService.findById(1);
        System.out.println(account);
    }
}

步骤8:运行验证

运行程序后,如果控制台正确输出查询结果,说明Spring与Mybatis整合成功。

整合总结

通过以上步骤,我们成功完成了Spring与Mybatis的整合,核心要点包括:

  1. 两个关键类
    • SqlSessionFactoryBean:负责创建SqlSessionFactory
    • MapperScannerConfigurer:负责扫描和注册Mapper接口
  2. 配置分离
    • 数据源配置独立管理
    • Mybatis配置独立管理
    • 主配置类统一导入
  3. 依赖注入
    • SqlSessionFactory自动注入DataSource
    • Service层自动注入Mapper接口

这种整合方式大大简化了Mybatis的使用,让我们能够专注于业务逻辑的开发,而无需关心底层的资源管理和对象创建。

Spring整合JUnit(非 现代JUnit5测试类)

一、整合差异

整合Junit与整合Druid、MyBatis有本质区别:

整合类型 作用 在程序中的角色
Druid/MyBatis 程序功能组件 参与程序运行,是程序主体部分
Junit 单元测试工具 不参与最终程序运行,是辅助测试工具

二、环境准备

  1. 基础环境:复用Spring+MyBatis整合项目结构
  2. 项目结构 直接使用Spring与MyBatis整合的环境:\

三、整合步骤详解

步骤1:添加依赖

xml 复制代码
<!-- JUnit单元测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<!-- Spring测试支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

步骤2:编写测试类

在test\java下创建一个AccountServiceTest.java,这个.java文件名字自定义

java 复制代码
// 设置使用Spring的测试运行器
@RunWith(SpringJUnit4ClassRunner.class)
// 设置Spring环境对应的配置类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class AccountServiceTest {
    
    // 自动注入要测试的Bean
    @Autowired
    private AccountService accountService;
    
    // 编写测试方法
    @Test
    public void testFindById(){
        System.out.println(accountService.findById(1));
    }
    
    @Test
    public void testFindAll(){
        System.out.println(accountService.findAll());
    }
}

四、核心注解详解

  1. @RunWith - 设置测试运行器,写在测试类定义上方
    作用:告诉JUnit不要使用默认的运行器,而是使用指定的运行器

    java 复制代码
    // 固定写法:使用Spring的JUnit运行器
    @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration - 加载Spring配置,写在测试类定义上方
    根据配置方式选择其中一种:

    java 复制代码
    // 方式1:加载注解配置类(推荐)
    @ContextConfiguration(classes = {配置类名.class})
    
    // 方式2:加载XML配置文件
    @ContextConfiguration(locations = {"配置文件名.xml"})
    
    // 方式3:加载多个配置,用数组格式表示
    @ContextConfiguration(classes = {Config1.class, Config2.class})
    @ContextConfiguration(locations = {"classpath:config1.xml", "classpath:config2.xml"})

五、工作原理解析:传统测试 vs Spring整合测试

传统JUnit测试流程

启动JUnit → 创建测试类实例 → 执行测试方法(手动创建容器 → 手动获取Bean → 执行测试)

传统测试代码示例(对比):

java 复制代码
public class TraditionalTest {
    @Test
    public void testFindById() {
        // 手动创建容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        // 手动获取Bean
        AccountService accountService = ctx.getBean(AccountService.class);
        // 执行测试
        System.out.println(accountService.findById(1));
    }
}

Spring整合JUnit测试流程

启动JUnit → 创建Spring容器 → 从容器中获取测试类实例 → 自动注入依赖 → 执行测试方法

Spring整合JUnit测试代码:

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class IntegratedTest {
    @Autowired  // 自动注入
    private AccountService accountService;
    
    @Test
    public void testFindById() {
        // 直接使用注入的Bean
        System.out.println(accountService.findById(1));
    }
}
相关推荐
洗澡水加冰2 小时前
MCP与Skills的辨析
后端·aigc·mcp
该用户已不存在2 小时前
Python正在死去,2026年Python还值得学吗?
后端·python
程序员西西2 小时前
SpringBoot轻松整合Sentinel限流
java·spring boot·后端·计算机·程序员
三七互娱后端团队2 小时前
告别“玄学”调参:DSPy 框架入门,让 AI 自动优化 AI 的提示词
人工智能·后端
星星电灯猴2 小时前
iPhone 抓包工具怎么选?从 HTTPS 调试、TCP 数据流分析到多工具协同的完整方案
后端
三七互娱后端团队2 小时前
别再只用 Vector Search 了:手把手教你落地 GraphRAG(图谱增强检索)
人工智能·后端
南雨北斗3 小时前
Kotlin中遍历集合的方法
后端
aiopencode3 小时前
Fiddler使用教程与抓包实战 HTTPHTTPS抓包、代理配置与接口调试完整指南
后端
星星电灯猴3 小时前
iOS 上架 H5 应用的可行性与实现路径,壳应用、合规要求与构建流程的技术分析
后端