Spring 核心容器从入门到精通

本文是 SSM 框架中 Spring 核心容器模块 的完整笔记,覆盖从课程介绍、IoC/DI 基础,到 Bean 配置、生命周期、依赖注入、容器总结的全流程,结构清晰、代码可直接运行,适合 CSDN 博客发布,新手可循序渐进学习,开发者可作为速查手册使用。


Spring-00-Spring 课程介绍

0.1 Spring 是什么

Spring 是 Java 生态中最主流的开源轻量级应用框架 ,由 Rod Johnson 于 2003 年推出,核心目标是简化 Java 企业级开发,解决传统 EJB 开发的笨重、复杂问题。

0.2 Spring 核心优势

  • 非侵入式:业务代码无需依赖 Spring API,耦合度极低。
  • IoC 容器:控制反转,将对象创建、依赖管理交给容器,解耦代码。
  • AOP 面向切面:统一处理日志、事务、权限等横切逻辑,代码复用。
  • 一站式框架:整合 MyBatis、SpringMVC、Spring Security 等,一站式开发。
  • 生态完善:Spring Boot、Spring Cloud 等衍生框架,覆盖微服务、云原生场景。

0.3 课程学习目标

  • 掌握 Spring 核心概念:IoC、DI、Bean、容器
  • 熟练配置 Bean、依赖注入、Bean 生命周期
  • 理解 Spring 容器原理,能独立完成 Spring 基础项目开发
  • 为后续 SSM 整合、Spring Boot 学习打下基础

Spring-01 - 初识 Spring

1.1 Spring 发展历程

  • 2003 年:Spring 1.0 发布,核心 IoC/AOP
  • 2006 年:Spring 2.0 支持 XML 命名空间、AOP 增强
  • 2014 年:Spring Boot 1.0 发布,简化配置
  • 2017 年:Spring 5.0 支持响应式编程、Java 8+
  • 2022 年:Spring 6.0 全面升级,支持 Jakarta EE 9+

1.2 Spring 核心模块

Spring 框架是模块化设计,核心模块包括:

  • Spring Core:核心容器,IoC/DI 基础
  • Spring AOP:面向切面编程
  • Spring Context:容器上下文,扩展配置
  • Spring JDBC:数据库操作封装
  • Spring ORM:整合 MyBatis、Hibernate 等 ORM 框架
  • Spring Web:Web 开发支持,SpringMVC 基础

1.3 第一个 Spring 程序(Hello Spring)

1. 环境准备
  • JDK 8+
  • Maven 3.6+
  • IDEA 开发工具
  • Spring 5.x 依赖(核心依赖 spring-context
2. Maven 依赖
复制代码
<dependencies>
    <!-- Spring 核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>
3. 代码实现
复制代码
// 业务类
public class HelloSpring {
    public void sayHello() {
        System.out.println("Hello Spring!");
    }
}

// Spring 配置文件:applicationContext.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:将HelloSpring交给Spring容器管理 -->
    <bean id="helloSpring" class="com.example.HelloSpring"/>
</beans>

// 测试类
public class SpringTest {
    public static void main(String[] args) {
        // 加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器获取Bean
        HelloSpring helloSpring = context.getBean("helloSpring", HelloSpring.class);
        // 调用方法
        helloSpring.sayHello();
    }
}
4. 运行结果

输出 Hello Spring!,第一个 Spring 程序运行成功。


Spring-02-Spring 系统架构

2.1 Spring 整体架构图

Spring 框架采用分层架构,核心分为 7 大模块:

  1. Core Container(核心容器)spring-corespring-beansspring-contextspring-expression,IoC/DI 核心
  2. AOP(面向切面)spring-aop,AOP 编程支持
  3. Data Access/Integration(数据访问)spring-jdbcspring-txspring-ormspring-jms,数据库、消息队列支持
  4. Web(Web 开发)spring-webspring-webmvcspring-websocket,Web 开发支持
  5. Test(测试)spring-test,单元测试、集成测试支持
  6. Instrumentation( instrumentation)spring-instrument,JVM 代理支持
  7. Messaging(消息)spring-messaging,消息编程支持

2.2 核心模块详解

1. Core Container(核心容器)
  • spring-core:核心工具类,Spring 基础
  • spring-beans:Bean 管理,IoC 核心
  • spring-context:容器上下文,加载配置、管理 Bean
  • spring-expression:Spring 表达式语言(SpEL),动态配置
2. AOP 模块
  • 提供面向切面编程,支持方法拦截、事务管理、日志记录等
  • 基于动态代理实现,兼容 JDK 动态代理和 CGLIB 代理
3. Data Access 模块
  • spring-jdbc:封装 JDBC 操作,简化数据库开发
  • spring-tx:声明式事务管理,无需手动编写事务代码
  • spring-orm:整合 MyBatis、Hibernate 等 ORM 框架

2.3 Spring 架构优势

  • 模块化设计:按需引入依赖,无需全部加载
  • 解耦设计:IoC 容器解耦业务代码,提升可维护性
  • 扩展性强:支持自定义 Bean、AOP 切面、扩展容器功能

Spring-03 - 核心概念

3.1 IoC(Inversion of Control,控制反转)

1. 什么是 IoC

IoC 是 Spring 的核心思想,指将对象的创建、依赖管理的控制权,从代码中反转给 Spring 容器 。传统开发中,对象由开发者手动 new 创建,依赖手动管理;IoC 中,对象由 Spring 容器创建、管理,开发者仅需从容器获取对象。

2. IoC 的本质
  • 控制权反转:对象创建权从代码转移到容器
  • 解耦:业务代码无需关注对象创建、依赖,仅关注业务逻辑
  • 容器管理:Spring 容器负责对象的生命周期、依赖注入

3.2 DI(Dependency Injection,依赖注入)

1. 什么是 DI

DI 是 IoC 的具体实现方式,指Spring 容器在创建对象时,自动将对象依赖的其他对象注入到对象中,无需手动赋值。

2. DI 与 IoC 的关系
  • IoC 是设计思想,DI 是 IoC 的具体实现
  • 没有 DI,IoC 无法落地;DI 是 IoC 的核心实现方式

3.3 Bean

Bean 是 Spring 容器管理的对象,是 Spring 应用的核心组成部分。

  • 所有由 Spring 创建、管理的对象,都称为 Bean
  • Bean 在 Spring 容器中通过配置(XML / 注解)定义,包含对象的创建、依赖、生命周期等信息

3.4 Spring 容器

Spring 容器是 Spring 的核心,负责创建、管理、销毁所有 Bean,是 IoC 的载体。

  • 核心接口:ApplicationContext(常用实现 ClassPathXmlApplicationContextAnnotationConfigApplicationContext
  • 容器职责:加载配置、创建 Bean、注入依赖、管理 Bean 生命周期

Spring-04-IoC 入门案例

4.1 案例需求

传统开发中,用户服务依赖用户 DAO,手动创建 DAO 对象,耦合度高;通过 IoC 容器管理对象,解耦代码。

4.2 传统开发实现(耦合度高)

复制代码
// DAO 层
public class UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// Service 层
public class UserService {
    // 手动创建DAO对象,耦合度高
    private UserDao userDao = new UserDao();

    public void addUser() {
        userDao.addUser();
    }
}

// 测试类
public class TraditionalTest {
    public static void main(String[] args) {
        UserService service = new UserService();
        service.addUser();
    }
}

问题:Service 与 DAO 硬编码耦合,修改 DAO 实现需修改 Service 代码,维护成本高。

4.3 IoC 容器实现(解耦)

1. 代码改造
复制代码
// DAO 层(不变)
public class UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// Service 层(改造:移除手动创建DAO)
public class UserService {
    private UserDao userDao;

    // 提供setter方法,用于DI注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser() {
        userDao.addUser();
    }
}
2. 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置UserDao Bean -->
    <bean id="userDao" class="com.example.UserDao"/>
    <!-- 配置UserService Bean,注入userDao -->
    <bean id="userService" class="com.example.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>
3. 测试类
复制代码
public class IoCTest {
    public static void main(String[] args) {
        // 加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器获取UserService
        UserService service = context.getBean("userService", UserService.class);
        // 调用方法
        service.addUser();
    }
}

4.4 IoC 优势

  • 解耦:Service 与 DAO 解耦,修改 DAO 实现仅需修改配置,无需修改 Service 代码
  • 可维护性:对象创建、依赖由容器管理,代码更简洁
  • 可扩展性:轻松替换 DAO 实现,无需修改业务代码

Spring-05-DI 入门案例

5.1 DI 核心概念

DI(依赖注入)是 IoC 的具体实现,指 Spring 容器在创建 Bean 时,自动将 Bean 依赖的其他 Bean 注入到当前 Bean 中,无需手动赋值。

5.2 DI 注入方式

Spring 支持 3 种 DI 注入方式:

  1. setter 注入:通过 setter 方法注入依赖(最常用)
  2. 构造器注入:通过构造方法注入依赖(Spring 4.3+ 推荐)
  3. 接口注入:Spring 不推荐,已基本淘汰

5.3 DI 入门案例(setter 注入)

承接上一章 IoC 案例,DI 核心是 <property> 标签注入依赖:

复制代码
<bean id="userService" class="com.example.UserService">
    <!-- setter注入:name对应setter方法名(去掉set,首字母小写),ref对应依赖的Bean id -->
    <property name="userDao" ref="userDao"/>
</bean>

5.4 DI 注入普通属性

除了注入 Bean,DI 还支持注入普通属性(基本类型、String 等):

复制代码
<bean id="user" class="com.example.User">
    <!-- 注入普通属性:value直接赋值 -->
    <property name="name" value="张三"/>
    <property name="age" value="20"/>
</bean>

5.5 DI 优势

  • 自动注入:无需手动创建依赖对象,容器自动完成
  • 灵活配置:通过配置修改依赖,无需修改代码
  • 解耦:彻底解耦业务代码与依赖创建

Spring-06-bean 基础配置

6.1 Bean 配置基础

Bean 在 Spring 中通过 <bean> 标签配置,核心属性:

表格

属性 说明
id Bean 的唯一标识,容器中通过 id 获取 Bean
class Bean 的全类名,容器通过反射创建对象
name Bean 的别名,可多个别名(逗号 / 空格分隔)
scope Bean 的作用域(默认 singleton)
init-method Bean 初始化方法
destroy-method Bean 销毁方法
lazy-init 延迟初始化(默认 false,容器启动时创建)

6.2 核心配置详解

1. id 与 class
  • id:Bean 的唯一标识,不可重复,容器通过 getBean(id) 获取
  • class:Bean 的全类名,容器通过反射创建对象,必须配置
2. scope 作用域

Spring 支持 6 种 Bean 作用域,常用 2 种:

  • singleton(默认):单例,容器中仅创建一个 Bean 实例
  • prototype:多例,每次获取 Bean 都创建新实例
  • request:Web 环境,每个请求创建一个实例
  • session:Web 环境,每个会话创建一个实例
  • application:Web 环境,每个应用创建一个实例
  • websocket:Web 环境,每个 WebSocket 会话创建一个实例
3. 配置示例
复制代码
<!-- 单例Bean(默认) -->
<bean id="userDao" class="com.example.UserDao" scope="singleton"/>
<!-- 多例Bean -->
<bean id="userService" class="com.example.UserService" scope="prototype"/>
<!-- 别名配置 -->
<bean id="user" class="com.example.User" name="user1,user2"/>

6.3 Bean 配置注意事项

  • id 必须唯一,不可重复
  • class 必须是全类名,否则容器无法创建对象
  • scope 单例模式下,Bean 在容器启动时创建;多例模式下,首次获取时创建

Spring-07-bean 实例化 ------ 构造方法

7.1 Bean 实例化方式

Spring 容器创建 Bean 实例,支持 3 种方式:

  1. 构造方法实例化(默认):通过无参构造器创建对象(最常用)
  2. 静态工厂实例化:通过静态工厂方法创建对象
  3. 实例工厂实例化:通过实例工厂方法创建对象

7.2 构造方法实例化

1. 核心原理

Spring 容器通过反射调用 Bean 的无参构造方法创建对象,这是 Spring 默认的实例化方式。

2. 代码示
复制代码
<bean id="user" class="com.example.User"/>

注意 :如果 Bean 没有无参构造方法,容器会抛出 NoSuchMethodException 异常。

3. 有参构造方法

如果 Bean 只有有参构造方法,需通过 <constructor-arg> 标签配置参数:

复制代码
<bean id="user" class="com.example.User">
    <constructor-arg name="name" value="张三"/>
</bean>

Spring-08-bean 实例化 ------ 静态工厂

8.1 静态工厂实例化原理

静态工厂实例化,指通过静态工厂类的静态方法创建 Bean 实例,Spring 容器调用静态方法获取对象。

8.2 代码示例

1. 静态工厂类
复制代码
public class UserStaticFactory {
    // 静态工厂方法:创建User对象
    public static User createUser() {
        System.out.println("静态工厂创建User对象");
        return new User();
    }
}
2. Spring 配置
复制代码
<!-- 静态工厂实例化Bean:class指定工厂类,factory-method指定静态方法 -->
<bean id="user" class="com.example.UserStaticFactory" factory-method="createUser"/>

8.3 适用场景

  • 第三方类库的对象创建,无法修改源码,通过静态工厂封装
  • 复杂对象创建,统一工厂管理,简化配置

Spring-09-bean 实例化 - 实例工厂与 FactoryBean

9.1 实例工厂实例化

1. 原理

实例工厂实例化,指通过实例工厂类的非静态方法创建 Bean 实例,需先创建工厂实例,再调用方法创建对象。

2. 代码示例
复制代码
<!-- 1. 配置实例工厂Bean -->
<bean id="userFactory" class="com.example.UserInstanceFactory"/>
<!-- 2. 实例工厂实例化Bean:factory-bean指定工厂实例,factory-method指定方法 -->
<bean id="user" factory-bean="userFactory" factory-method="createUser"/>

9.2 FactoryBean 实例化

1. 什么是 FactoryBean

FactoryBean 是 Spring 提供的工厂 Bean 接口,用于自定义 Bean 的创建逻辑,是 Spring 框架的扩展点。

  • 接口方法:T getObject()(创建对象)、Class<?> getObjectType()(对象类型)、boolean isSingleton()(是否单例)
2. 代码示例
复制代码
<!-- 配置FactoryBean:id为user,容器获取的是getObject()返回的User对象 -->
<bean id="user" class="com.example.UserFactoryBean"/>
3. FactoryBean 优势
  • 自定义 Bean 创建逻辑,灵活控制对象创建
  • 整合第三方框架(如 MyBatis 的 SqlSessionFactoryBean
  • 简化复杂对象的配置

Spring-10-bean 的生命周期

10.1 Bean 生命周期概述

Bean 的生命周期,指Bean 从创建到销毁的完整过程,Spring 容器负责管理 Bean 的整个生命周期,包含多个阶段。

10.2 Bean 完整生命周期(11 个阶段)

  1. 实例化:容器通过构造方法 / 工厂创建 Bean 实例
  2. 属性赋值:DI 注入依赖,给 Bean 的属性赋值
  3. Aware 接口回调 :调用 BeanNameAwareBeanFactoryAwareApplicationContextAware 等接口方法
  4. BeanPostProcessor 前置处理 :调用 postProcessBeforeInitialization() 方法
  5. 初始化 :调用 init-method 方法、InitializingBeanafterPropertiesSet() 方法
  6. BeanPostProcessor 后置处理 :调用 postProcessAfterInitialization() 方法
  7. Bean 就绪:Bean 可以使用
  8. 运行:Bean 正常工作
  9. 销毁 :容器关闭时,调用 destroy-method 方法、DisposableBeandestroy() 方法
  10. Bean 销毁:Bean 实例被回收

10.3 代码示例:Bean 生命周期

复制代码
public class User implements BeanNameAware, InitializingBean, DisposableBean {
    private String name;

    public User() {
        System.out.println("1. 实例化:User 构造方法执行");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2. 属性赋值:setName执行");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3. Aware回调:setBeanName执行,Bean名称:" + name);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5. 初始化:InitializingBean的afterPropertiesSet执行");
    }

    // 自定义初始化方法
    public void initMethod() {
        System.out.println("5. 初始化:init-method执行");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("9. 销毁:DisposableBean的destroy执行");
    }

    // 自定义销毁方法
    public void destroyMethod() {
        System.out.println("9. 销毁:destroy-method执行");
    }
}

<bean id="user" class="com.example.User" init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="张三"/>
</bean>

10.4 BeanPostProcessor 示例

复制代码
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("4. BeanPostProcessor前置处理:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("6. BeanPostProcessor后置处理:" + beanName);
        return bean;
    }
}

Spring-11-setter 注入

11.1 setter 注入核心

setter 注入是 Spring 最常用的 DI 注入方式,指通过 Bean 的 setter 方法注入依赖 ,核心是 <property> 标签。

11.2 setter 注入语法

复制代码
<bean id="beanId" class="全类名">
    <property name="属性名" ref="依赖BeanId"/> <!-- 注入Bean -->
    <property name="属性名" value="普通值"/> <!-- 注入普通属性 -->
</bean>
  • name:对应 Bean 的 setter 方法名(去掉 set,首字母小写)
  • ref:引用容器中的其他 Bean(注入依赖)
  • value:注入普通属性(基本类型、String、包装类等)

11.3 代码示例

复制代码
public class UserService {
    private UserDao userDao;
    private String serviceName;

    // setter方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }
}

<bean id="userDao" class="com.example.UserDao"/>
<bean id="userService" class="com.example.UserService">
    <!-- 注入Bean依赖 -->
    <property name="userDao" ref="userDao"/>
    <!-- 注入普通属性 -->
    <property name="serviceName" value="用户服务"/>
</bean>

11.4 setter 注入优势

  • 灵活:可选择性注入依赖,无需全部注入
  • 直观:配置清晰,易于理解
  • 兼容:兼容无参构造方法,使用简单

Spring-12 - 构造器注入

12.1 构造器注入核心

构造器注入是 Spring 推荐的注入方式(Spring 4.3+),指通过 Bean 的构造方法注入依赖 ,核心是 <constructor-arg> 标签。

12.2 构造器注入语法

复制代码
<bean id="beanId" class="全类名">
    <constructor-arg name="参数名" ref="依赖BeanId"/> <!-- 注入Bean -->
    <constructor-arg name="参数名" value="普通值"/> <!-- 注入普通属性 -->
</bean>
  • name:对应构造方法的参数名
  • index:按参数索引注入(0 开始)
  • type:按参数类型注入

12.3 代码示例

复制代码
public class UserService {
    private UserDao userDao;
    private String serviceName;

    // 有参构造方法
    public UserService(UserDao userDao, String serviceName) {
        this.userDao = userDao;
        this.serviceName = serviceName;
    }
}

<bean id="userDao" class="com.example.UserDao"/>
<bean id="userService" class="com.example.UserService">
    <constructor-arg name="userDao" ref="userDao"/>
    <constructor-arg name="serviceName" value="用户服务"/>
</bean>

12.4 构造器注入 vs setter 注入

特性 构造器注入 setter 注入
依赖完整性 强制注入所有依赖,避免空指针 可选择性注入,可能漏注入
不可变性 注入后属性不可修改(final) 属性可修改
兼容性 需提供有参构造方法 需提供无参构造方法
适用场景 必须依赖的核心场景 可选依赖、简单场景

Spring-13 - 自动装配

13.1 自动装配核心

自动装配(Autowire)是 Spring 提供的简化 DI 配置的功能 ,指 Spring 容器自动为 Bean 注入依赖,无需手动配置 <property><constructor-arg>

13.2 自动装配模式

Spring 支持 5 种自动装配模式:

  1. no(默认):不自动装配,需手动配置
  2. byName:按属性名自动装配,属性名与 Bean id 一致
  3. byType:按类型自动装配,容器中类型匹配的 Bean 自动注入
  4. constructor:按构造器参数类型自动装配
  5. default:使用父标签 <beans> 的默认配置

13.3 代码示例

1. byName 自动装配
复制代码
<bean id="userDao" class="com.example.UserDao"/>
<!-- byName:属性名userDao与Bean id一致,自动注入 -->
<bean id="userService" class="com.example.UserService" autowire="byName"/>
2. byType 自动装配
复制代码
<bean id="userDao" class="com.example.UserDao"/>
<!-- byType:按UserDao类型匹配,自动注入 -->
<bean id="userService" class="com.example.UserService" autowire="byType"/>

13.4 自动装配注意事项

  • byType 模式下,容器中只能有一个匹配类型的 Bean,否则抛出异常
  • 自动装配无法注入简单类型(基本类型、String 等),仅能注入 Bean
  • 自动装配优先级低于手动配置,手动配置会覆盖自动装配

Spring-14 - 集合注入

14.1 集合注入核心

Spring 支持为 Bean 注入集合类型的属性,包括 List、Set、Map、Properties 等,通过对应标签配置。

14.2 集合注入语法

1. List 注入
复制代码
<property name="list">
    <list>
        <value>元素1</value>
        <ref bean="beanId"/>
    </list>
</property>
2. Set 注入
复制代码
<property name="set">
    <set>
        <value>元素1</value>
        <ref bean="beanId"/>
    </set>
</property>
3. Map 注入
复制代码
<property name="map">
    <map>
        <entry key="key1" value="value1"/>
        <entry key="key2" ref="beanId"/>
    </map>
</property>
4. Properties 注入
复制代码
<property name="properties">
    <props>
        <prop key="key1">value1</prop>
        <prop key="key2">value2</prop>
    </props>
</property>

14.3 代码示

复制代码
<bean id="user" class="com.example.User"/>
<bean id="collectionBean" class="com.example.CollectionBean">
    <!-- List注入 -->
    <property name="list">
        <list>
            <value>张三</value>
            <value>李四</value>
        </list>
    </property>
    <!-- Map注入 -->
    <property name="map">
        <map>
            <entry key="user1" ref="user"/>
        </map>
    </property>
</bean>

Spring-15 - 案例:数据源对象管理

15.1 案例需求

通过 Spring 容器管理数据源(Druid 数据源),将数据源的创建、配置交给 Spring,解耦业务代码与数据源配置。

15.2 环境准备

  • 引入 Druid 依赖

    <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.16</version> </dependency>

15.3 代码实现

1. Spring 配置文件
复制代码
<!-- 配置Druid数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="initialSize" value="5"/>
    <property name="maxActive" value="20"/>
</bean>
2. 测试类
复制代码
public class DataSourceTest {
    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = context.getBean("dataSource", DataSource.class);
        // 获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("获取连接成功:" + connection);
        connection.close();
    }
}

15.4 案例优势

  • 解耦:数据源配置与业务代码分离,修改配置无需修改代码
  • 统一管理:Spring 容器管理数据源生命周期,简化维护
  • 灵活配置:通过配置文件修改数据源参数,无需重启代码

Spring-16 - 加载 properties 文件

16.1 加载 properties 文件核心

Spring 支持加载外部 properties 配置文件,将配置信息注入到 Bean 中,避免硬编码,核心是 <context:property-placeholder> 标签。

16.2 实现步骤

1. 引入 context 命名空间
复制代码
<?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">
2. 加载 properties 文件
复制代码
<!-- 加载properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
3. properties 文件(jdbc.properties)
复制代码
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
4. 注入配置到 Bean
复制代码
<bean id="dataSource" 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}"/>
</bean>

16.3 优势

  • 配置分离:将配置信息与代码分离,便于维护
  • 环境适配:不同环境(开发 / 测试 / 生产)使用不同配置文件
  • 避免硬编码:修改配置无需修改代码,重启服务即可生效

Spring-17 - 容器

17.1 Spring 容器核心

Spring 容器是 Spring 的核心,负责创建、管理、销毁所有 Bean ,是 IoC 的载体,核心接口是 ApplicationContext

17.2 容器核心接口

1. BeanFactory
  • Spring 最底层的容器接口,提供基础的 Bean 管理功能
  • 懒加载:容器启动时不创建 Bean,首次获取时创建
  • 适用于资源受限的环境(如移动设备)
2. ApplicationContext
  • BeanFactory 的子接口,扩展了更多功能(国际化、事件发布、资源加载等)
  • 预加载:容器启动时立即创建所有单例 Bean
  • 开发中常用,核心实现:
    • ClassPathXmlApplicationContext:加载类路径下的 XML 配置文件
    • FileSystemXmlApplicationContext:加载文件系统中的 XML 配置文件
    • AnnotationConfigApplicationContext:加载注解配置(Spring 3.0+)

17.3 容器启动流程

  1. 加载配置:加载 XML / 注解配置,解析 Bean 定义
  2. 实例化 Bean:创建 Bean 实例(单例 Bean 预加载)
  3. DI 注入:为 Bean 注入依赖
  4. 初始化 Bean:调用初始化方法、BeanPostProcessor
  5. 容器就绪:Bean 可以使用
  6. 容器关闭:销毁 Bean,释放资源

17.4 容器常用方法

复制代码
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取Bean
T getBean(String id);
T getBean(Class<T> clazz);
// 判断Bean是否存在
boolean containsBean(String id);
// 获取Bean类型
Class<?> getType(String id);
// 关闭容器
void close();

Spring-18 - 核心容器总结

18.1 核心知识点梳理

一、基础概念
  • IoC:控制反转,对象创建权交给容器
  • DI:依赖注入,IoC 的具体实现
  • Bean:Spring 容器管理的对象
  • 容器:Spring 核心,管理 Bean 生命周期
二、Bean 配置与实例化
  • Bean 基础配置:id、class、scope、init-method、destroy-method
  • Bean 实例化方式:构造方法、静态工厂、实例工厂、FactoryBean
  • Bean 生命周期:实例化→属性赋值→Aware 回调→初始化→就绪→销毁
三、依赖注入
  • 注入方式:setter 注入、构造器注入
  • 自动装配:byName、byType、constructor
  • 集合注入:List、Set、Map、Properties
四、容器与扩展
  • 容器接口:BeanFactoryApplicationContext
  • 配置加载:XML 配置、properties 文件加载
  • 案例:数据源管理、容器启动流程

18.2 核心最佳实践

  1. 依赖注入:优先使用构造器注入,保证依赖完整性
  2. Bean 作用域 :默认单例,多例场景使用 prototype
  3. 自动装配 :优先使用 byType,避免 byName 的命名依赖
  4. 配置分离:使用 properties 文件管理配置,避免硬编码
  5. 容器管理 :使用 ApplicationContext 作为容器,预加载 Bean
  6. 生命周期 :合理使用 init-methoddestroy-method 管理 Bean 生命周期

18.3 学习路线建议

  1. 掌握 IoC/DI 核心思想,理解容器作用
  2. 熟练 Bean 配置、依赖注入、Bean 生命周期
  3. 掌握集合注入、自动装配、properties 加载等实用技能
  4. 实战案例:数据源管理、Spring 整合 MyBatis
  5. 进阶学习:AOP、SpringMVC、Spring Boot

Spring-19 - 注解开发:定义 Bean

19.1 为什么使用注解开发

  • 替代 XML:减少 XML 配置文件,代码更集中
  • 开发效率高:标记即配置,无需写大量 XML 标签
  • 主流开发方式:Spring Boot 全面推荐注解开发

19.2 核心注解

1. 定义 Bean 的四大注解(功能等价)
注解 说明 适用场景
@Component 通用组件 通用工具类
@Controller 控制层 Web 层(MVC)
@Service 服务层 Service 业务层
@Repository 数据访问层 DAO/MyBatis 映射类
2. 核心配置步骤
  1. 开启组件扫描:<context:component-scan base-package="com.example"/>
  2. 在类上添加注解:@Component("userDao")

19.3 代码示例

复制代码
// 1. 定义Bean
@Repository("userDao") // 等价于<bean id="userDao" class="...UserDao"/>
public class UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 2. Spring配置(JavaConfig方式)
@Configuration
@ComponentScan("com.example")
public class SpringConfig {}

// 3. 测试
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserDao userDao = context.getBean("userDao", UserDao.class);
        userDao.addUser();
    }
}

Spring-20 - 纯注解开发模式

20.1 纯注解开发的核心

完全移除 XML 配置文件,全部使用 Java 类作为配置中心,是 Spring 3.0+ 推荐的开发模式。

20.2 核心配置类

  • @Configuration:标记该类为 Spring 配置类
  • @ComponentScan:组件扫描
  • @Import:导入其他配置类
  • @PropertySource:加载 properties 文件

20.3 完整示例

复制代码
// 1. 数据源配置
@Configuration
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        return ds;
    }
}

// 2. 核心配置
@Configuration
@ComponentScan("com.example")
@Import(DataSourceConfig.class)
public class SpringConfig {}

Spring-21 - 注解开发:Bean 作用范围与生命周期

21.1 作用范围

  • @Scope("singleton"):单例(默认)
  • @Scope("prototype"):多例
  • @Scope("request")/@Scope("session"):Web 环境

21.2 生命周期注解

  • @PostConstruct:初始化方法
  • @PreDestroy:销毁方法

21.3 代码示例

复制代码
@Scope("prototype")
@Service
public class UserService {
    @PostConstruct
    public void init() {
        System.out.println("初始化");
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("销毁");
    }
}

Spring-22 - 注解开发:依赖注入

22.1 注入方式

1. @Autowired(推荐)
  • 按类型自动装配
  • 可配合 @Qualifier("userDao") 按名称装配
2. @Resource(JDK 自带)
  • 默认按名称,其次按类型
3. @Value
  • 注入普通属性、配置文件属性

22.2 代码示例

复制代码
@Service
public class UserService {
    @Autowired
    @Qualifier("userDaoImpl")
    private UserDao userDao;
    
    @Value("${service.name}")
    private String name;
}

Spring-23 - 注解开发:管理第三方 Bean

23.1 场景

第三方类(如 DruidDataSourceJdbcTemplate)无法添加 Spring 注解,需通过 @Bean 注解配置。

23.2 核心注解

  • @Bean:定义第三方 Bean,方法名即 Bean id
  • @Configuration:配置类

23.3 代码示例

复制代码
@Configuration
public class ThirdPartyConfig {
    @Bean
    public DruidDataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return ds;
    }
}

Spring-24 - 注解开发:实现为第三方 Bean 注入

24.1 注入方式

  1. 方法参数注入@Bean 方法直接传参
  2. 成员变量注入 :在配置类中使用 @Autowired

24.2 代码示例

复制代码
@Configuration
public class AppConfig {
    // 1. 第三方Bean
    @Bean
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    // 2. 依赖注入:方法参数自动注入dataSource
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

Spring-25 - 注解开发:总结

25.1 全流程总结

  1. 配置@Configuration + @ComponentScan
  2. 定义 Bean@Component 家族 / @Bean
  3. 依赖注入@Autowired / @Resource / @Value
  4. 生命周期@PostConstruct / @PreDestroy
  5. 作用域@Scope

25.2 最佳实践

  • 类级别的 Bean 用 @Component 系列
  • 第三方类用 @Bean
  • 构造器注入优先(@Autowired 构造器)

Spring-26-Spring 整合 MyBatis 思路分析

26.1 整合核心思想

  • MyBatis 负责创建 SqlSessionFactoryMapper
  • Spring 负责管理这些对象,完成依赖注入,控制事务

26.2 整合步骤(思路)

  1. 引入依赖(MyBatis + Spring + 整合包)
  2. 配置 SqlSessionFactoryBean(核心)
  3. 配置 MapperScannerConfigurer 扫描 Mapper
  4. 注入 Mapper 到 Service

26.3 依赖包

复制代码
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>

Spring-27-Spring 整合 MyBatis(实战)

27.1 核心配置(XML 版)

复制代码
<!-- 1. 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">...</bean>

<!-- 2. SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

<!-- 3. Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.dao"/>
</bean>

27.2 注解版配置(Spring-19 至 Spring-24 知识结合)

复制代码
@Configuration
@MapperScan("com.example.dao")
public class MyBatisConfig {
    @Bean
    public DataSource dataSource() {...}
    
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }
}

27.3 业务使用

复制代码
@Service
public class UserService {
    @Autowired
    private UserDao userDao; // 直接注入,无需SqlSession
}

Spring-28-Spring 整合 JUnit

28.1 作用

在 Spring 环境中进行单元测试,自动加载 Spring 容器,无需手动创建 ApplicationContext

28.2 依赖

复制代码
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.20</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>

28.3 代码示例

复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
    @Autowired
    private UserService userService;
    
    @Test
    public void testAdd() {
        userService.addUser();
    }
}

Spring-29-AOP 简介

29.1 什么是 AOP

AOP(Aspect-Oriented Programming,面向切面编程)

  • 核心思想:将日志、事务、权限等横切逻辑,从业务代码中剥离,动态织入到业务代码执行前后
  • 核心目标:解耦代码复用集中管理

29.2 核心优势

  • 业务代码仅关注核心业务,无关逻辑(日志、事务)外置。
  • 横切逻辑集中管理,修改时无需修改业务代码。
  • 是 Spring 核心机制之一,Spring 事务管理基于 AOP。

29.3 AOP 核心概念(背下来!)

术语 中文 说明
Aspect 切面 封装横切逻辑的类(如日志切面、事务切面)
Joinpoint 连接点 程序执行的某个点(如方法调用、异常抛出),Spring AOP 中仅支持方法执行
Pointcut 切入点 定义哪些连接点需要被拦截(表达式匹配)
Advice 通知 / 增强 切面在切入点执行的具体代码(如前置、后置、环绕)
Weaving 织入 将切面应用到目标对象,生成代理对象的过程
Proxy 代理 目标对象的代理对象,包含目标逻辑 + 切面逻辑

Spring-30-AOP 入门案例

30.1 案例需求

在不修改业务代码的情况下,在方法执行前后打印日志。

30.2 依赖(AOP 核心)

复制代码
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

30.3 实现步骤(XML 版)

  1. 定义业务类
  2. 定义切面类(加 @Aspect
  3. 配置切入点、通知

30.4 代码示例

1. 业务类
复制代码
@Service
public class UserService {
    public void addUser() {
        System.out.println("执行添加用户业务");
    }
}
2. 切面类(日志)
复制代码
@Aspect
@Component
public class LogAspect {
    // 定义切入点:execution(返回值 包.类.方法(参数))
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void pt() {}

    // 前置通知:方法执行前
    @Before("pt()")
    public void beforeLog() {
        System.out.println("--- 日志:方法执行前 ---");
    }
    
    // 后置通知:方法执行后
    @After("pt()")
    public void afterLog() {
        System.out.println("--- 日志:方法执行后 ---");
    }
}
3. Spring 配置
复制代码
<aop:aspectj-autoproxy/> <!-- 开启AOP自动代理 -->
<context:component-scan base-package="com.example"/>

Spring-31-AOP 工作流程

31.1 核心流程(四步走)

  1. 织入(Weaving) :Spring 容器启动时,扫描 @Aspect 切面,根据切入点匹配目标类。
  2. 创建代理(Proxy):对目标类创建动态代理(JDK 动态代理 / CGLIB 代理)。
  3. 调用:业务代码调用代理对象,代理对象拦截方法。
  4. 执行通知:代理对象按照通知类型(前置、环绕等)执行切面逻辑,再调用目标方法。

31.2 代理创建规则

  • 目标类实现了接口 → JDK 动态代理(基于接口)
  • 目标类未实现接口 → CGLIB 代理(基于继承)

31.3 关键流程图示

业务代码代理对象拦截切入点执行通知执行目标方法返回结果


Spring-32-AOP 切入点表达式

32.1 核心语法

复制代码
execution(访问修饰符 返回值 包名.类名.方法名(参数))

32.2 通配符

  • *:匹配任意字符
  • ..:匹配任意参数(方法参数)或任意子包
  • +:匹配类及其子类(通常用于类名后)

32.3 常用示例

  1. 匹配任意方法execution(* *.*(..))
  2. 匹配某包下所有类方法execution(* com.example.service.*.*(..))
  3. 匹配特定方法execution(* com.example.service.UserService.add*(..))
  4. 匹配特定参数execution(* com.example.service.UserService.*(String, int))

32.4 其他指示符

  • within(com.example.service.*):匹配某包下所有类
  • this(UserService):匹配代理对象是 UserService 的类
  • target(UserService):匹配目标对象是 UserService 的类
  • @annotation(MyLog):匹配方法上有 @MyLog 注解的方法

Spring-33-AOP 通知类型

Spring AOP 定义了 5 种通知类型,控制方法执行的不同时机:

通知类型 注解 执行时机 说明
前置通知 @Before 方法执行 做日志、权限校验
后置通知 @After 方法执行(无论正常 / 异常) 做资源清理
返回通知 @AfterReturning 方法正常返回后 获取返回值
异常通知 @AfterThrowing 方法抛出异常后 处理异常逻辑
环绕通知 @Around 方法执行前后,可控制方法执行 最强大,可完全控制流程(如事务管理)

33.1 环绕通知(重点)

复制代码
@Around("pt()")
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕:前");
    Object result = pjp.proceed(); // 执行目标方法
    System.out.println("环绕:后");
    return result;
}

Spring-34 - 案例:业务层接口执行效率监控(完整版)

34.1 需求

对所有业务层(Service)方法进行执行耗时统计,在控制台打印方法名 + 执行时间,不侵入业务代码。

34.2 思路

  • 使用 AOP 环绕通知 @Around
  • 在执行目标方法前记录开始时间
  • 执行目标方法
  • 执行后记录结束时间
  • 计算耗时并输出

34.3 完整代码

1)切面类:耗时统计切面

复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MethodCostTimeAspect {

    // 切入点:service包及其子包下所有类的所有方法
    @Pointcut("execution(* com.example.service..*(..))")
    public void servicePointcut() {}

    // 环绕通知
    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 1. 获取方法签名
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();
        String methodFullName = className + "." + methodName;

        // 2. 开始时间
        long start = System.currentTimeMillis();

        // 3. 执行目标方法
        Object result = pjp.proceed();

        // 4. 结束时间 & 耗时
        long end = System.currentTimeMillis();
        long costTime = end - start;

        // 5. 输出
        System.out.printf("[方法耗时] %-30s 耗时:%d ms%n", methodFullName, costTime);

        return result;
    }
}

2)业务层(随便写一个测试方法)

复制代码
import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void addUser() {
        // 模拟业务耗时
        try {
            Thread.sleep(150);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行添加用户业务");
    }
}

3)Spring 配置类(开启 AOP)

复制代码
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy // 开启AOP自动代理
public class SpringConfig {
}

4)测试类

复制代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestAop {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = ctx.getBean(UserService.class);
        userService.addUser();
    }
}

34.4 运行结果示例

复制代码
执行添加用户业务
[方法耗时] UserService.addUser             耗时:153 ms

34.5 扩展(可选)

  • 超过阈值(如 500ms)打印警告
  • 存入日志文件 / 监控平台
  • 统计接口调用次数

Spring-35-AOP 注解配置总结与 XML 对比

35.1 AOP 注解开发核心步骤

  1. 导入 spring-aspects 依赖
  2. 开启 AOP 自动代理:@EnableAspectJAutoProxy
  3. 编写切面类并加 @Aspect + @Component
  4. 定义切入点 @Pointcut
  5. 配置 5 种通知:@Before / @After / @AfterReturning / @AfterThrowing / @Around

35.2 XML 配置 vs 注解配置

  • XML:配置集中,适合老项目、复杂 AOP 规则
  • 注解:简洁直观、开发快,SpringBoot 主流方式

35.3 AOP 使用注意事项

  1. 同一个切面内多个通知执行顺序:Around → Before → 方法 → After → AfterReturning/AfterThrowing
  2. 环绕通知必须手动调用 proceed(),否则目标方法不执行
  3. 切入点表达式尽量精准,避免拦截范围过大影响性能
  4. 同类内部方法调用(this.method ())不会走 AOP 代理

Spring-36-Spring 事务简介

36.1 什么是事务

一组操作要么全部成功 ,要么全部失败,保证数据一致性。

36.2 事务四大特性 ACID

  • 原子性:不可分割
  • 一致性:执行前后数据合法
  • 隔离性:多事务互不干扰
  • 持久性:提交后永久生效

36.3 Spring 事务管理方式

  1. 编程式事务:代码手动控制(极少用)
  2. 声明式事务 :XML / 注解控制(主流)底层基于 AOP 动态代理 实现

36.4 核心接口

  • PlatformTransactionManager:事务管理器(真正干活)
  • TransactionDefinition:事务定义(传播、隔离、超时等)
  • TransactionStatus:事务运行状态

Spring-37 - 声明式事务注解开发

37.1 核心注解

@Transactional:加在方法 / 类上表示开启事务

37.2 常用属性

属性 说明
propagation 事务传播行为
isolation 事务隔离级别
timeout 超时时间(秒)
readOnly 是否只读事务
rollbackFor 哪些异常回滚
noRollbackFor 哪些异常不回滚

37.3 实现步骤

  1. 配置数据源、SqlSessionFactory
  2. 配置事务管理器 DataSourceTransactionManager
  3. 开启事务注解:@EnableTransactionManagement
  4. Service 层方法上加 @Transactional

37.4 完整配置示例

复制代码
@Configuration
@MapperScan("com.example.dao")
@ComponentScan("com.example")
@EnableTransactionManagement // 开启事务
public class SpringConfig {

    // 数据源
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///test");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }

    // SqlSessionFactory
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }

    // 事务管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

37.5 Service 使用事务

复制代码
@Service
public class AccountService {

    @Autowired
    private AccountDao accountDao;

    // 转账:开启事务,出现异常就回滚
    @Transactional(rollbackFor = Exception.class)
    public void transfer(String from, String to, double money){
        // 转出
        accountDao.outMoney(from, money);
        // 模拟异常
        int i = 1/0;
        // 转入
        accountDao.inMoney(to, money);
    }
}

Spring-38 - 事务传播行为与隔离级别(完结)

38.1 事务传播行为(7 种)

控制多个带事务方法互相调用时事务如何传递:

  1. **REQUIRED(默认)**有事务就加入,没有就新建
  2. REQUIRES_NEW新建独立事务,挂起当前事务
  3. SUPPORTS有事务就用,没有就非事务运行
  4. MANDATORY必须在事务内运行,否则抛异常
  5. NOT_SUPPORTED非事务运行,挂起当前事务
  6. NEVER必须非事务运行,否则抛异常
  7. NESTED嵌套事务,有保存点

38.2 事务隔离级别

解决并发问题:脏读、不可重复读、幻读

  • DEFAULT:使用数据库默认
  • READ_UNCOMMITTED:读未提交(问题最多)
  • READ_COMMITTED:读已提交(Oracle 默认)
  • REPEATABLE_READ:可重复读(MySQL 默认)
  • SERIALIZABLE:串行化(最安全、性能低)

38.3 事务使用要点

  1. @Transactional 只对 public 方法 生效
  2. 默认只对 RuntimeException 回滚
  3. 想要所有异常都回滚:rollbackFor = Exception.class
  4. 同类内部调用不生效(不走代理)
  5. 读操作设 readOnly = true 可优化性能

38.4 SSM 框架整体总结

  • Spring:IoC 管理对象、AOP 处理横切逻辑、事务控制
  • SpringMVC:接收请求、参数绑定、页面跳转
  • MyBatis:数据库操作、ORM 映射
  • 整合关键点:数据源、SqlSessionFactory、事务、包扫描

Spring-39-Spring 事务角色

39.1 Spring 事务核心角色

Spring 事务是基于 AOP 动态代理 实现的,核心由 3 大角色协作完成:

表格

角色 核心接口 / 类 职责
事务管理器(Transaction Manager) PlatformTransactionManager 事务的核心执行者,负责事务的开启、提交、回滚,是 Spring 事务的抽象层,屏蔽不同数据源的差异。
事务定义(Transaction Definition) TransactionDefinition 定义事务的属性配置,如传播行为、隔离级别、超时时间、只读等,是事务的 "配置说明书"。
事务状态(Transaction Status) TransactionStatus 记录事务的运行状态,如是否有保存点、是否已提交、是否回滚等,用于事务的控制与监控。
事务切面(Transaction Aspect) TransactionInterceptor AOP 切面,负责拦截目标方法,在方法执行前后开启 / 提交 / 回滚事务,是事务的 "执行入口"。

39.2 各角色核心作用详解

1. 事务管理器(PlatformTransactionManager)
  • 是 Spring 事务的核心入口,所有事务操作都通过它完成。
  • 常用实现类:
    • DataSourceTransactionManager:用于 JDBC/MyBatis 等基于数据源的事务(最常用)
    • HibernateTransactionManager:用于 Hibernate 框架
    • JpaTransactionManager:用于 JPA 框架
  • 核心方法:
    • getTransaction(TransactionDefinition definition):获取事务
    • commit(TransactionStatus status):提交事务
    • rollback(TransactionStatus status):回滚事务
2. 事务定义(TransactionDefinition)
  • 用于配置事务的属性@Transactional 注解的所有属性,最终都会封装为 TransactionDefinition 对象。
  • 核心属性:
    • 传播行为(Propagation):REQUIREDREQUIRES_NEW
    • 隔离级别(Isolation):READ_COMMITTEDREPEATABLE_READ
    • 超时时间(Timeout):事务最大执行时间
    • 只读(ReadOnly):是否为只读事务
    • 回滚规则(Rollback Rules):哪些异常回滚、哪些不回滚
3. 事务状态(TransactionStatus)
  • 用于跟踪事务的运行状态,是事务管理器操作事务的依据。
  • 核心方法:
    • isNewTransaction():是否为新事务
    • hasSavepoint():是否有保存点
    • isCompleted():事务是否已完成(提交 / 回滚)
    • setRollbackOnly():标记事务为回滚状态
4. 事务切面(TransactionInterceptor)
  • 是 Spring 事务的 AOP 实现载体 ,继承了 MethodInterceptor,用于拦截目标方法。
  • 执行流程:
    1. 拦截目标方法,获取事务定义
    2. 调用事务管理器开启事务
    3. 执行目标方法
    4. 方法正常执行 → 提交事务
    5. 方法抛出异常 → 回滚事务

39.3 Spring 事务执行完整流程

  1. 调用目标方法 → 被 TransactionInterceptor 拦截
  2. 拦截器读取 @Transactional 注解,封装为 TransactionDefinition
  3. 调用 PlatformTransactionManager.getTransaction() 开启事务
  4. 执行目标业务方法
  5. 方法正常返回 → 调用 commit() 提交事务
  6. 方法抛出异常 → 调用 rollback() 回滚事务

Spring-40-Spring 事务属性

40.1 事务属性总览

Spring 事务的所有配置,都通过 @Transactional 注解的属性实现,核心分为 5 大类:


40.2 核心属性详解

1. 传播行为(propagation)

用于控制多个带事务方法互相调用时,事务的传递规则,共 7 种:

传播行为 说明 适用场景
REQUIRED(默认) 有事务就加入,没有就新建一个事务 绝大多数业务场景,保证方法在同一个事务中执行
REQUIRES_NEW 新建一个独立事务,挂起当前事务 日志记录、消息发送等,不希望影响主事务的场景
SUPPORTS 有事务就用,没有就以非事务方式运行 只读查询方法,不强制事务
MANDATORY 必须在事务中运行,否则抛出异常 核心业务方法,必须依赖上层事务
NOT_SUPPORTED 以非事务方式运行,挂起当前事务 耗时较长的非核心操作,不占用事务资源
NEVER 必须以非事务方式运行,否则抛出异常 禁止事务的工具方法
NESTED 嵌套事务,在当前事务中创建保存点 子方法失败仅回滚子方法,不影响主事务

2. 隔离级别(isolation)

用于解决多事务并发时的数据一致性问题,共 5 种:

表格

隔离级别 说明 解决的问题 数据库默认
DEFAULT(默认) 使用数据库的默认隔离级别 适配不同数据库 MySQL:REPEATABLE_READ Oracle:READ_COMMITTED
READ_UNCOMMITTED 读取未提交的数据 无,会出现脏读、不可重复读、幻读 几乎不用
READ_COMMITTED 读取已提交的数据 解决脏读 Oracle 默认
REPEATABLE_READ 可重复读,同一事务中多次读取结果一致 解决脏读、不可重复读 MySQL 默认
SERIALIZABLE 串行化,事务依次执行 解决所有并发问题 性能极低,仅极端场景使用

并发问题说明:

  • 脏读:一个事务读取了另一个事务未提交的数据
  • 不可重复读:同一事务中,多次读取同一数据,结果不一致(另一个事务修改了数据)
  • 幻读:同一事务中,多次查询,结果集行数不一致(另一个事务插入 / 删除了数据)

3. 超时时间(timeout)
  • 单位:秒
  • 作用:设置事务的最大执行时间,超过时间自动回滚,避免长时间占用数据库连接。
  • 示例:@Transactional(timeout = 30) → 事务最多执行 30 秒,超时回滚。

4. 只读(readOnly)
  • 作用:标记事务为只读事务,数据库会对只读事务进行优化(如不加锁、提升性能)。
  • 适用场景:查询方法,避免误写操作。
  • 示例:@Transactional(readOnly = true)

5. 回滚规则(rollbackFor /noRollbackFor)
  • rollbackFor :指定哪些异常触发事务回滚(默认仅 RuntimeExceptionError 回滚)
  • noRollbackFor:指定哪些异常不触发事务回滚
  • 生产环境推荐:@Transactional(rollbackFor = Exception.class) → 所有异常都回滚,避免漏回滚。

40.3 事务属性使用最佳实践

  1. 传播行为 :默认 REQUIRED 即可,特殊场景用 REQUIRES_NEW
  2. 隔离级别 :默认 DEFAULT,用数据库默认级别,无需手动修改
  3. 回滚规则 :必须加 rollbackFor = Exception.class,避免受检异常不回滚
  4. 只读 :查询方法加 readOnly = true,优化性能
  5. 超时时间:核心业务方法设置合理超时,避免死锁

SpringMVC 全体系


SpringMVC-01-SpringMVC 简介

1.1 什么是 SpringMVC

SpringMVC 是 Spring 框架提供的 Web 层 MVC 框架,是 Java 生态中最主流的 Web 开发框架,用于处理 Web 请求、实现前后端交互。

1.2 MVC 设计模式

MVC 是一种软件设计模式,将应用分为 3 层,实现解耦:

  • Model(模型层):业务逻辑、数据模型(Service、DAO、Entity)
  • View(视图层):页面展示(JSP、Thymeleaf、Vue 等)
  • Controller(控制层):接收请求、调用业务、响应结果(SpringMVC 核心)

1.3 SpringMVC 核心优势

  • 基于 Spring:无缝整合 Spring IoC/AOP/ 事务,一站式开发
  • 轻量级:配置简单,学习成本低
  • 功能强大:支持 RESTful、文件上传、拦截器、异常处理等
  • 生态完善:适配 Spring Boot、Spring Cloud,支持前后端分离

SpringMVC-02-SpringMVC 入门案例

2.1 环境准备

  • JDK 8+
  • Maven 3.6+
  • Tomcat 8.5+
  • Spring 5.x + SpringMVC 依赖

2.2 Maven 依赖

复制代码
<dependencies>
    <!-- Spring核心 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- SpringMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- ServletAPI -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.3 核心配置

1. web.xml(配置前端控制器)
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 前端控制器DispatcherServlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 加载SpringMVC配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
2. springmvc.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">

    <!-- 组件扫描:扫描Controller -->
    <context:component-scan base-package="com.example.controller"/>

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
3. Controller 类
复制代码
@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        System.out.println("Hello SpringMVC!");
        return "success"; // 对应 /WEB-INF/pages/success.jsp
    }
}
4. 页面(success.jsp)
复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Success</title>
</head>
<body>
    <h1>Hello SpringMVC!</h1>
</body>
</html>

2.4 运行效果

启动 Tomcat,访问 http://localhost:8080/hello,页面显示 Hello SpringMVC!,入门案例成功。


SpringMVC-03 - 入门案例工作流程

3.1 SpringMVC 核心执行流程(10 步)

  1. 用户发送请求 → 被 DispatcherServlet(前端控制器)拦截
  2. DispatcherServlet 调用 HandlerMapping(处理器映射器)
  3. HandlerMapping 根据请求路径匹配 Controller,返回 HandlerExecutionChain
  4. DispatcherServlet 调用 HandlerAdapter(处理器适配器)
  5. HandlerAdapter 执行 Controller 中的目标方法
  6. Controller 执行完成,返回 ModelAndView(模型数据 + 视图名)
  7. HandlerAdapterModelAndView 返回给 DispatcherServlet
  8. DispatcherServlet 调用 ViewResolver(视图解析器),解析视图名,返回真实视图
  9. DispatcherServlet 渲染视图(将模型数据填充到视图)
  10. 响应结果给用户

3.2 核心组件说明

组件 作用
DispatcherServlet 前端控制器,SpringMVC 核心,统一处理所有请求
HandlerMapping 处理器映射器,匹配请求与 Controller
HandlerAdapter 处理器适配器,执行 Controller 方法
ViewResolver 视图解析器,解析视图名,生成真实视图
Controller 处理器,处理业务逻辑
View 视图,展示结果

SpringMVC-04-bean 加载控制

4.1 问题背景

Spring 容器和 SpringMVC 容器是两个独立的容器

  • Spring 容器(根容器):加载 Service、DAO、工具类等
  • SpringMVC 容器(子容器):加载 Controller、视图解析器等
  • 子容器可以访问父容器的 Bean,父容器不能访问子容器的 Bean

4.2 加载控制方案

1. 组件扫描分离
  • Spring 容器:扫描 com.example.servicecom.example.dao,排除 Controller
  • SpringMVC 容器:仅扫描 com.example.controller
2. 配置示例

Spring 容器配置(applicationContext.xml)

复制代码
<context:component-scan base-package="com.example">
    <!-- 排除Controller -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

SpringMVC 容器配置(springmvc.xml)

复制代码
<!-- 仅扫描Controller -->
<context:component-scan base-package="com.example.controller" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

4.3 加载流程

  1. Tomcat 启动 → 加载 web.xml
  2. 加载 Spring 根容器(ContextLoaderListener
  3. 加载 SpringMVC 子容器(DispatcherServlet
  4. 子容器继承父容器,可访问父容器 Bean

SpringMVC-05-PostMan 工具介绍

5.1 什么是 PostMan

PostMan 是一款接口测试工具,用于测试 Web 接口(GET/POST/PUT/DELETE 等),支持参数传递、文件上传、环境配置等,是后端开发必备工具。

5.2 核心功能

  • 发送各种类型的 HTTP 请求(GET、POST、PUT、DELETE 等)
  • 传递参数(URL 参数、请求体、请求头、Cookie 等)
  • 保存接口用例,批量执行测试
  • 生成接口文档,自动化测试

5.3 使用场景

  • 测试 SpringMVC 接口,无需启动前端页面
  • 测试 RESTful 接口,前后端分离开发
  • 接口联调,模拟各种请求场景

SpringMVC-06 - 设置请求映射路径

6.1 核心注解:@RequestMapping

@RequestMapping 是 SpringMVC 核心注解,用于映射请求路径与 Controller 方法,可作用于类和方法。

6.2 注解属性

属性 说明
value/path 请求路径(支持通配符)
method 请求方法(GET/POST/PUT/DELETE 等)
params 请求参数条件
headers 请求头条件
consumes 接收的请求体类型
produces 响应的内容类型

6.3 代码示例

复制代码
// 类上添加:提取公共路径
@Controller
@RequestMapping("/user")
public class UserController {

    // 方法上添加:拼接路径 /user/hello
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return "success";
    }

    // 简化写法:@GetMapping 等价于 @RequestMapping(method = GET)
    @GetMapping("/get")
    public String get() {
        return "success";
    }
}

6.4 简化注解(Spring 4.3+)

  • @GetMapping:GET 请求
  • @PostMapping:POST 请求
  • @PutMapping:PUT 请求
  • @DeleteMapping:DELETE 请求
  • @PatchMapping:PATCH 请求

SpringMVC-07-get 请求与 post 请求发送

7.1 GET 请求

  • 特点:参数拼接在 URL 中,长度有限,不安全,用于查询操作

  • 参数传递?key=value&key2=value2

  • 代码示例

    @GetMapping("/getUser")
    public String getUser(String name, Integer age) {
    System.out.println("name: " + name + ", age: " + age);
    return "success";
    }

  • 请求示例http://localhost:8080/getUser?name=张三&age=20

7.2 POST 请求

  • 特点:参数放在请求体中,长度无限制,安全,用于新增 / 修改操作

  • 参数传递:表单提交、JSON 格式

  • 代码示例

    @PostMapping("/addUser")
    public String addUser(String name, Integer age) {
    System.out.println("name: " + name + ", age: " + age);
    return "success";
    }

  • 请求示例 :PostMan 发送 POST 请求,Body 中填写 name=张三&age=20

7.3 GET vs POST 对比

特性 GET POST
参数位置 URL 请求体
长度限制
安全性 低(参数可见) 高(参数不可见)
适用场景 查询 新增 / 修改

SpringMVC-08-5 种类型参数传递

SpringMVC 支持 5 种常见的参数传递方式,自动绑定参数:

8.1 1. 普通参数(基本类型 / 字符串)

  • 要求:参数名与请求参数名完全一致

  • 代码示例:

    @GetMapping("/param1")
    public String param1(String name, Integer age) {
    System.out.println("name: " + name + ", age: " + age);
    return "success";
    }

8.2 2. 实体类参数

  • 要求:实体类属性名与请求参数名完全一致

  • 代码示例:

    // 实体类
    public class User {
    private String name;
    private Integer age;
    // getter/setter
    }

    // Controller
    @PostMapping("/param2")
    public String param2(User user) {
    System.out.println("user: " + user);
    return "success";
    }

8.3 3. 数组参数

  • 要求:多个参数名相同,自动封装为数组

  • 代码示例:

    @GetMapping("/param3")
    public String param3(String[] hobby) {
    System.out.println("hobby: " + Arrays.toString(hobby));
    return "success";
    }

  • 请求示例http://localhost:8080/param3?hobby=篮球&hobby=足球

8.4 4. 集合参数(List/Map)

  • List 参数:需用 @RequestParam 注解

  • Map 参数:自动封装请求参数为 Map

  • 代码示例:

    @GetMapping("/param4")
    public String param4(@RequestParam List<String> hobby, Map<String, Object> map) {
    System.out.println("hobby: " + hobby);
    System.out.println("map: " + map);
    return "success";
    }

8.5 5. 路径变量参数(RESTful)

  • 用于 RESTful 风格,参数在 URL 路径中

  • 核心注解:@PathVariable

  • 代码示例:

    @GetMapping("/user/{id}/{name}")
    public String param5(@PathVariable Integer id, @PathVariable String name) {
    System.out.println("id: " + id + ", name: " + name);
    return "success";
    }

  • 请求示例http://localhost:8080/user/1/张三


SpringMVC-09-json 数据传递参数

9.1 核心说明

前后端分离开发中,前端传递 JSON 格式参数 ,后端用 @RequestBody 注解接收。

9.2 依赖配置

需要引入 Jackson 依赖,用于 JSON 与 Java 对象的转换:

复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>

SpringMVC 会自动配置 MappingJackson2HttpMessageConverter,实现 JSON 转换。

9.3 代码示例

复制代码
@PostMapping("/json")
public String json(@RequestBody User user) {
    System.out.println("user: " + user);
    return "success";
}
  • 请求示例:PostMan 发送 POST 请求,Body 选择 JSON 格式:

    {
    "name": "张三",
    "age": 20
    }

9.4 核心注解:@RequestBody

  • 作用:将请求体中的 JSON 数据,自动转换为 Java 对象
  • 要求:请求头 Content-Typeapplication/json

SpringMVC-10 - 日期型参数传递

10.1 问题背景

前端传递日期字符串(如 2024-01-01),后端需要自动转换为 Date/LocalDateTime 类型。

10.2 解决方案

1. 全局日期转换器(推荐)

配置 WebMvcConfigurer,添加全局日期转换器:

复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 日期转换器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        registry.addConverter(String.class, LocalDateTime.class, s -> LocalDateTime.parse(s, formatter));
    }
}
2. 注解方式(@DateTimeFormat

在实体类属性上添加注解:

复制代码
public class User {
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}

10.3 代码示例

复制代码
@PostMapping("/date")
public String date(Date date, LocalDateTime localDateTime) {
    System.out.println("date: " + date);
    System.out.println("localDateTime: " + localDateTime);
    return "success";
}
  • 请求示例http://localhost:8080/date?date=2024-01-01&localDateTime=2024-01-01 12:00:00

SpringMVC-11 - 响应

SpringMVC 支持多种响应方式,核心分为 3 类:

11.1 1. 响应页面(传统 MVC)

  • 方法返回 String,对应视图名,由视图解析器解析为页面

  • 代码示例:

    @GetMapping("/page")
    public String page(Model model) {
    model.addAttribute("msg", "Hello SpringMVC");
    return "success"; // 跳转到 success.jsp
    }

11.2 2. 响应数据(JSON / 字符串)

  • 核心注解:@ResponseBody,将方法返回值直接写入响应体,不跳转页面

  • 代码示例:

    @GetMapping("/data")
    @ResponseBody
    public User data() {
    User user = new User("张三", 20);
    return user; // 自动转换为JSON
    }

  • 响应结果{"name":"张三","age":20}

11.3 3. 响应状态码 / 头信息

  • 通过 ResponseEntity 自定义响应状态码、头信息

  • 代码示例:

    @GetMapping("/response")
    public ResponseEntity<User> response() {
    User user = new User("张三", 20);
    HttpHeaders headers = new HttpHeaders();
    headers.add("token", "123456");
    return new ResponseEntity<>(user, headers, HttpStatus.OK);
    }


SpringMVC-12-REST 风格简介

12.1 什么是 REST 风格

REST(Representational State Transfer)是一种软件架构风格 ,用于设计 Web 接口,核心是用 HTTP 方法表示操作,URL 表示资源。

12.2 REST 核心规范

HTTP 方法 操作 示例 URL 说明
GET 查询 /user/1 查询 id=1 的用户
POST 新增 /user 新增用户
PUT 修改 /user 修改用户
DELETE 删除 /user/1 删除 id=1 的用户

12.3 REST 优势

  • 简洁:URL 仅表示资源,HTTP 方法表示操作
  • 统一:接口风格统一,便于前后端联调
  • 无状态:每个请求独立,便于扩展
  • 适合前后端分离:完美适配前后端分离开发

SpringMVC-13-RESTful 入门案例

13.1 案例需求

实现用户的 CRUD 接口,遵循 REST 风格:

  • GET /user/{id}:查询用户
  • POST /user:新增用户
  • PUT /user:修改用户
  • DELETE /user/{id}:删除用户

13.2 代码示例

复制代码
@RestController // 等价于 @Controller + @ResponseBody
@RequestMapping("/user")
public class UserController {

    // 查询用户
    @GetMapping("/{id}")
    public User getUser(@PathVariable Integer id) {
        return new User("张三", 20);
    }

    // 新增用户
    @PostMapping
    public String addUser(@RequestBody User user) {
        return "新增成功";
    }

    // 修改用户
    @PutMapping
    public String updateUser(@RequestBody User user) {
        return "修改成功";
    }

    // 删除用户
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Integer id) {
        return "删除成功";
    }
}

13.3 配置过滤器(解决 PUT/DELETE 请求参数问题)

web.xml 中配置 HiddenHttpMethodFilter,将 POST 请求转换为 PUT/DELETE:

复制代码
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

SpringMVC-14-RESTful 快速开发

14.1 核心注解:@RestController

  • 等价于 @Controller + @ResponseBody,所有方法默认返回 JSON,无需重复添加 @ResponseBody

  • 代码示例:

    @RestController
    @RequestMapping("/user")
    public class UserController {
    // 所有方法自动返回JSON
    @GetMapping("/{id}")
    public User getUser(@PathVariable Integer id) {
    return new User("张三", 20);
    }
    }

14.2 统一返回结果封装

封装统一的返回结果类,简化接口开发:

复制代码
public class Result<T> {
    private Integer code; // 状态码
    private String msg;  // 提示信息
    private T data;      // 数据

    // 成功响应
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("success");
        result.setData(data);
        return result;
    }

    // 失败响应
    public static <T> Result<T> error(String msg) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMsg(msg);
        return result;
    }
}

14.3 快速开发示例

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/{id}")
    public Result<User> getUser(@PathVariable Integer id) {
        return Result.success(new User("张三", 20));
    }
}

SpringMVC-15 - 案例:基于 RESTful 页面数据交互

15.1 案例需求

实现前后端分离的页面数据交互,前端通过 AJAX 调用 RESTful 接口,渲染页面。

15.2 后端接口

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/list")
    public Result<List<User>> list() {
        List<User> list = Arrays.asList(
                new User("张三", 20),
                new User("李四", 21)
        );
        return Result.success(list);
    }
}

15.3 前端 AJAX 调用(jQuery


SpringMVC-16 - 案例:基于 RESTful 页面数据交互(分页)

16.1 案例需求

实现分页查询接口,基于 RESTful 风格,传递页码、每页条数,返回分页数据。

16.2 分页实体类

复制代码
public class PageBean<T> {
    private Integer currentPage; // 当前页码
    private Integer pageSize;   // 每页条数
    private Integer total;      // 总条数
    private List<T> data;       // 分页数据
}

16.3 后端接口

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/page/{currentPage}/{pageSize}")
    public Result<PageBean<User>> page(@PathVariable Integer currentPage, @PathVariable Integer pageSize) {
        // 模拟分页数据
        List<User> list = Arrays.asList(
                new User("张三", 20),
                new User("李四", 21)
        );
        PageBean<User> pageBean = new PageBean<>();
        pageBean.setCurrentPage(currentPage);
        pageBean.setPageSize(pageSize);
        pageBean.setTotal(100);
        pageBean.setData(list);
        return Result.success(pageBean);
    }
}

16.4 前端分页渲染

复制代码
$.ajax({
    url: "/user/page/1/10",
    type: "GET",
    success: function(res) {
        if (res.code === 200) {
            let pageBean = res.data;
            // 渲染分页数据
            pageBean.data.forEach(user => {
                $("#table").append(`<tr><td>${user.name}</td><td>${user.age}</td></tr>`);
            });
            // 渲染分页栏
            $("#pagination").html(`当前第${pageBean.currentPage}页,共${pageBean.total}条`);
        }
    }
});

SpringMVC-17-SSM 整合(整合配置)

17.1 整合核心思路

  • Spring:管理 Service、DAO、数据源、事务
  • SpringMVC:管理 Controller、视图解析器、拦截器
  • MyBatis:管理 Mapper、SqlSessionFactory
  • 整合关键点:Spring 容器管理 MyBatis 的 SqlSessionFactory、Mapper,SpringMVC 容器管理 Controller

17.2 整合配置步骤

1. 依赖配置
复制代码
<dependencies>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- SpringMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <!-- MyBatis-Spring整合包 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version>
    </dependency>
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.16</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
</dependencies>
2. Spring 配置(applicationContext.xml)
复制代码
<!-- 组件扫描(排除Controller) -->
<context:component-scan base-package="com.example">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- 加载properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 数据源 -->
<bean id="dataSource" 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}"/>
</bean>

<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<!-- Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.dao"/>
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3. SpringMVC 配置(springmvc.xml)
复制代码
<!-- 组件扫描(仅Controller) -->
<context:component-scan base-package="com.example.controller" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>

<!-- 开启注解驱动 -->
<mvc:annotation-driven/>

<!-- 静态资源放行 -->
<mvc:default-servlet-handler/>
4. web.xml 配置
复制代码
<!--  Spring容器监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- 前端控制器 -->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 编码过滤器 -->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

SpringMVC-18-SSM 整合(功能模块开发)

18.1 功能模块开发流程

  1. 创建实体类User 实体,对应数据库表
  2. 创建 DAO 层UserDao 接口 + UserMapper.xml 映射文件
  3. 创建 Service 层UserService 接口 + UserServiceImpl 实现类
  4. 创建 Controller 层UserController,调用 Service,处理请求
  5. 测试接口:PostMan 测试接口,验证功能

18.2 代码示例

1. 实体类(User)
复制代码
public class User {
    private Integer id;
    private String name;
    private Integer age;
    // getter/setter
}
2. DAO 层(UserDao)
复制代码
public interface UserDao {
    List<User> findAll();
}
3. Mapper 映射文件(UserMapper.xml)
复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserDao">
    <select id="findAll" resultType="com.example.entity.User">
        select * from user
    </select>
</mapper>
4. Service 层(UserService)
复制代码
public interface UserService {
    List<User> findAll();
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    @Transactional
    public List<User> findAll() {
        return userDao.findAll();
    }
}
5. Controller 层(UserController)
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public Result<List<User>> list() {
        List<User> list = userService.findAll();
        return Result.success(list);
    }
}

18.3 测试验证

启动 Tomcat,访问 http://localhost:8080/user/list,返回 JSON 数据,SSM 整合成功。

SpringMVC-19-SSM 整合(接口测试)

19.1 接口测试核心目标

验证 SSM 整合后各层(Controller → Service → DAO → 数据库)的链路是否通畅,接口功能是否正常。

19.2 测试工具与流程

1. 测试工具
  • PostMan:发送 HTTP 请求,测试接口
  • JUnit + Spring Test:单元测试,验证 Service/DAO 层
  • Swagger:接口文档自动生成,在线测试(可选)
2. 测试流程
  1. 测试 DAO 层:直接调用 Mapper,验证数据库操作
  2. 测试 Service 层:调用 Service,验证业务逻辑与事务
  3. 测试 Controller 层:通过 PostMan 发送请求,验证接口

19.3 代码示例(JUnit 测试 Service 层)

复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserServiceTest {
    @Autowired
    private UserService userService;

    @Test
    public void testFindAll() {
        List<User> list = userService.findAll();
        System.out.println("查询结果:" + list);
        Assertions.assertFalse(list.isEmpty());
    }
}

19.4 PostMan 测试 Controller 层

  • 请求 URL:http://localhost:8080/user/list
  • 请求方法:GET
  • 预期结果:返回 JSON 格式的用户列表,状态码 200

SpringMVC-20-SSM 整合 - 表现层与前端页面数据交互(基础)

20.1 核心目标

实现后端 Controller 与前端页面的数据交互,包括:

  • 后端向前端传递数据(Model/ModelAndView)
  • 前端向后端传递参数(表单 / AJAX)
  • 页面渲染与跳转

20.2 后端传数据到前端的 3 种方式

1. Model 接口(最常用)
复制代码
@GetMapping("/list")
public String list(Model model) {
    List<User> list = userService.findAll();
    model.addAttribute("userList", list); // 存入数据,前端通过${userList}获取
    return "userList"; // 跳转到userList.jsp
}
2. ModelAndView
复制代码
@GetMapping("/list")
public ModelAndView list() {
    ModelAndView mv = new ModelAndView();
    List<User> list = userService.findAll();
    mv.addObject("userList", list);
    mv.setViewName("userList");
    return mv;
}
3. @SessionAttributes 注解(数据存入 Session)
复制代码
@Controller
@SessionAttributes("user")
public class UserController {
    @GetMapping("/info")
    public String info(Model model) {
        model.addAttribute("user", new User("张三", 20));
        return "info";
    }
}

20.3 前端页面获取数据(JSP 示例)

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>用户列表</title>
</head>
<body>
    <table>
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>年龄</th>
        </tr>
        <c:forEach items="${userList}" var="user">
            <tr>
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.age}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

SpringMVC-21-SSM 整合 - 表现层与前端页面数据交互(AJAX)

21.1 核心目标

实现前后端分离的异步数据交互,前端通过 AJAX 调用接口,无需刷新页面。

21.2 代码示例(AJAX 调用接口)

1. 后端 Controller
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/ajax/list")
    public Result<List<User>> ajaxList() {
        List<User> list = userService.findAll();
        return Result.success(list);
    }
}
2. 前端 AJAX(jQuery)
复制代码
$.ajax({
    url: "/user/ajax/list",
    type: "GET",
    dataType: "json",
    success: function(res) {
        if (res.code === 200) {
            // 动态渲染页面
            let html = "";
            res.data.forEach(user => {
                html += `<tr><td>${user.id}</td><td>${user.name}</td><td>${user.age}</td></tr>`;
            });
            $("#userTable").html(html);
        }
    }
});

21.3 核心优势

  • 异步交互,无需刷新页面,用户体验好
  • 前后端分离,后端仅提供接口,前端负责页面渲染
  • 适合现代 Web 开发(Vue/React 等前端框架)

SpringMVC-22-SSM 整合 - 异常处理器

22.1 核心目标

统一处理系统异常,避免将异常信息直接返回给用户,提升用户体验与系统安全性。

22.2 异常处理器实现方式

1. 局部异常处理(@ExceptionHandler

在 Controller 中处理当前类的异常:

复制代码
@Controller
public class UserController {
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", e.getMessage());
        mv.setViewName("error");
        return mv;
    }
}
2. 全局异常处理(@ControllerAdvice + @ExceptionHandler

统一处理所有 Controller 的异常(推荐):

复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result<Void> globalException(Exception e) {
        e.printStackTrace();
        return Result.error("系统异常:" + e.getMessage());
    }

    // 处理特定异常
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public Result<Void> nullPointerException(NullPointerException e) {
        return Result.error("空指针异常");
    }
}

22.3 异常处理流程

  1. Controller 抛出异常 → 被 @ControllerAdvice 拦截
  2. 匹配对应的 @ExceptionHandler 方法
  3. 执行异常处理逻辑,返回统一结果给前端

SpringMVC-23-SSM 整合 - 项目异常处理

23.1 项目异常分类

表格

异常类型 说明 处理方式
业务异常 业务逻辑异常(如用户不存在、余额不足) 主动抛出,返回提示信息
系统异常 系统级异常(如空指针、数据库异常) 全局捕获,统一返回
参数异常 请求参数异常(如参数缺失、类型错误) 全局捕获,返回参数错误提示

23.2 自定义业务异常

复制代码
// 自定义业务异常
public class BusinessException extends RuntimeException {
    private Integer code;

    public BusinessException(Integer code, String msg) {
        super(msg);
        this.code = code;
    }

    public Integer getCode() {
        return code;
    }
}

// 全局异常处理中捕获
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result<Void> businessException(BusinessException e) {
    return new Result<>(e.getCode(), e.getMessage(), null);
}

23.3 异常处理最佳实践

  • 自定义业务异常,区分系统异常与业务异常
  • 全局异常处理统一返回格式,前端统一处理
  • 记录异常日志,便于问题排查
  • 避免将敏感异常信息返回给用户

SpringMVC-24-SSM 整合 - 前后台协议联调(基础)

24.1 前后台协议核心

定义前后端交互的统一规范,包括:

  • 请求 / 响应格式(JSON)
  • 状态码定义
  • 数据结构
  • 错误码定义

24.2 统一响应结果封装

复制代码
public class Result<T> {
    private Integer code; // 状态码:200成功,500失败
    private String msg;  // 提示信息
    private T data;      // 数据

    // 成功响应
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("success");
        result.setData(data);
        return result;
    }

    // 失败响应
    public static <T> Result<T> error(String msg) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMsg(msg);
        return result;
    }

    // 自定义状态码
    public static <T> Result<T> error(Integer code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}

24.3 状态码规范

状态码 说明
200 成功
400 参数错误
401 未登录
403 无权限
404 资源不存在
500 系统异常

SpringMVC-25-SSM 整合 - 前后台协议联调(参数校验)

25.1 核心目标

对前端传递的参数进行校验,避免非法参数导致系统异常。

25.2 参数校验实现方式

1. 手动校验(简单场景)
复制代码
@PostMapping("/add")
public Result<Void> add(User user) {
    if (user.getName() == null || user.getName().isEmpty()) {
        return Result.error(400, "姓名不能为空");
    }
    if (user.getAge() < 0 || user.getAge() > 150) {
        return Result.error(400, "年龄不合法");
    }
    userService.add(user);
    return Result.success(null);
}
2. 注解校验(JSR-303/Hibernate Validator,推荐)

引入依赖:

复制代码
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.5.Final</version>
</dependency>

实体类添加校验注解:

复制代码
public class User {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
}

Controller 中开启校验:

复制代码
@PostMapping("/add")
public Result<Void> add(@Valid @RequestBody User user, BindingResult result) {
    if (result.hasErrors()) {
        // 获取校验错误信息
        String msg = result.getFieldError().getDefaultMessage();
        return Result.error(400, msg);
    }
    userService.add(user);
    return Result.success(null);
}

SpringMVC-26-SSM 整合 - 前后台协议联调(分页)

26.1 分页协议规范

定义分页请求 / 响应的统一格式:

  • 请求参数:currentPage(当前页码)、pageSize(每页条数)
  • 响应数据:currentPagepageSizetotal(总条数)、data(分页数据)

26.2 分页实体类

复制代码
public class PageBean<T> {
    private Integer currentPage;
    private Integer pageSize;
    private Integer total;
    private List<T> data;
}

26.3 分页接口实现

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/page/{currentPage}/{pageSize}")
    public Result<PageBean<User>> page(@PathVariable Integer currentPage, @PathVariable Integer pageSize) {
        PageBean<User> pageBean = userService.page(currentPage, pageSize);
        return Result.success(pageBean);
    }
}

SpringMVC-27-SSM 整合 - 前后台协议联调(文件上传)

27.1 文件上传协议

  • 请求方法:POST

  • 请求头:Content-Type: multipart/form-data

  • SpringMVC 配置文件上传解析器:

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"/> <property name="defaultEncoding" value="UTF-8"/> </bean>

27.2 文件上传接口实现

复制代码
@RestController
@RequestMapping("/file")
public class FileController {
    @PostMapping("/upload")
    public Result<String> upload(@RequestParam("file") MultipartFile file) {
        try {
            // 保存文件
            String fileName = file.getOriginalFilename();
            file.transferTo(new File("D:/upload/" + fileName));
            return Result.success("上传成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("上传失败");
        }
    }
}

SpringMVC-28-SSM 整合 - 前后台协议联调(登录拦截)

28.1 登录拦截核心

通过拦截器实现登录状态校验,未登录用户无法访问敏感接口。

28.2 拦截器实现(见下一章详细说明)

复制代码
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 校验登录状态
        Object user = request.getSession().getAttribute("user");
        if (user == null) {
            // 未登录,返回401
            response.setStatus(401);
            return false;
        }
        return true;
    }
}

SpringMVC-29 - 拦截器简介

29.1 什么是拦截器

拦截器(Interceptor)是 SpringMVC 提供的AOP 实现,用于拦截请求的执行,在请求前后执行自定义逻辑,常用于登录校验、日志记录、权限控制等。

29.2 拦截器核心方法

方法 执行时机 说明
preHandle 请求处理前(Controller 方法执行前) 返回 true 继续执行,false 中断请求
postHandle 请求处理后(Controller 方法执行后,视图渲染前) 可修改 ModelAndView
afterCompletion 请求完成后(视图渲染后) 用于资源清理

29.3 拦截器与过滤器的区别

特性 拦截器(Interceptor) 过滤器(Filter)
所属框架 SpringMVC Servlet
拦截范围 仅拦截 Controller 请求 拦截所有请求(包括静态资源)
依赖容器 依赖 Spring 容器 依赖 Servlet 容器
适用场景 登录校验、权限控制、日志 编码过滤、权限过滤、跨域处理

SpringMVC-30 - 拦截器入门案例

30.1 案例需求

实现登录拦截器,未登录用户无法访问用户列表页面。

30.2 代码实现

1. 拦截器类
复制代码
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 排除登录接口
        String requestURI = request.getRequestURI();
        if (requestURI.contains("/login")) {
            return true;
        }

        // 校验登录状态
        Object user = request.getSession().getAttribute("user");
        if (user == null) {
            // 未登录,跳转到登录页
            response.sendRedirect("/login/page");
            return false;
        }
        return true;
    }
}
2. 配置拦截器
复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/login/**", "/static/**"); // 排除登录接口和静态资源
    }
}

SpringMVC-31 - 拦截器参数

31.1 拦截器核心参数

1. HttpServletRequest

获取请求信息(请求头、请求参数、Session 等)

2. HttpServletResponse

设置响应信息(状态码、响应头、重定向等)

3. Object handler

获取当前请求的处理器(Controller 方法),可用于判断请求类型

31.2 拦截器获取 Controller 方法信息

复制代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 获取方法名
        String methodName = handlerMethod.getMethod().getName();
        // 获取类名
        String className = handlerMethod.getBeanType().getName();
        System.out.println("请求方法:" + className + "." + methodName);
    }
    return true;
}

SpringMVC-32 - 拦截器 - 拦截器链配置

32.1 什么是拦截器链

多个拦截器按顺序组成拦截器链,请求依次经过所有拦截器的 preHandle 方法,响应依次经过所有拦截器的 postHandleafterCompletion 方法。

32.2 拦截器链执行顺序

  • preHandle:按配置顺序执行
  • postHandle:按配置逆序执行
  • afterCompletion:按配置逆序执行

32.3 拦截器链配置示例

复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 先执行日志拦截器,再执行登录拦截器
        registry.addInterceptor(logInterceptor).addPathPatterns("/**");
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
    }
}

Maven 分模块开发全体系(01-06 章 完整笔记)


Maven-01 - 分模块开发的意义

1.1 什么是分模块开发

将一个大型项目拆分为多个独立的模块,每个模块负责单一功能,模块之间通过依赖关系协作,是大型项目开发的标准方式。

1.2 分模块开发的核心优势

  • 解耦:模块之间低耦合,便于维护和迭代
  • 复用:模块可复用,避免重复开发
  • 并行开发:多个团队并行开发不同模块,提升开发效率
  • 便于测试:模块可独立测试,降低测试复杂度
  • 版本管理:模块可独立版本控制,灵活升级

1.3 常见模块拆分方式

  • 按层拆分:entitydaoservicecontrollerweb
  • 按功能拆分:userorderpay 等业务模块
  • 按工具拆分:commonutilsconfig 等公共模块

Maven-02 - 分模块开发与设计

2.1 分模块设计示例(SSM 项目)

模块名 职责 依赖关系
ssm-entity 实体类、工具类
ssm-dao DAO 层、Mapper 依赖 ssm-entity
ssm-service Service 层、业务逻辑 依赖 ssm-dao
ssm-web Controller 层、Web 接口 依赖 ssm-service
ssm-common 公共工具类、配置 被所有模块依赖

2.2 模块创建步骤

  1. 创建父工程(聚合工程),打包方式为 pom
  2. 创建子模块,继承父工程
  3. 配置模块依赖关系
  4. 编写模块代码
  5. 聚合模块,统一构建

2.3 父工程 pom.xml 配置

复制代码
<groupId>com.example</groupId>
<artifactId>ssm-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!-- 聚合子模块 -->
<modules>
    <module>ssm-entity</module>
    <module>ssm-dao</module>
    <module>ssm-service</module>
    <module>ssm-web</module>
</modules>

<!-- 统一依赖版本 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.20</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Maven-03 - 依赖传递

3.1 什么是依赖传递

Maven 支持依赖传递,即 A 依赖 B,B 依赖 C,那么 A 会自动依赖 C,无需手动引入。

3.2 依赖传递的范围

依赖范围(scope) 说明 是否传递
compile(默认) 编译、测试、运行都有效
test 仅测试有效
provided 编译、测试有效,运行时由容器提供
runtime 运行、测试有效,编译无效
system 本地依赖,需指定路径

3.3 依赖传递示例

  • ssm-web 依赖 ssm-service
  • ssm-service 依赖 ssm-dao
  • ssm-dao 依赖 ssm-entity
  • 最终 ssm-web 会自动依赖 ssm-daossm-entity

Maven-04 - 可选依赖与排除依赖

4.1 可选依赖(optional = true

  • 作用:将依赖标记为可选,不会传递给下游模块

  • 适用场景:依赖仅当前模块需要,下游模块不需要

  • 配置示例:

    <dependency> <groupId>com.example</groupId> <artifactId>ssm-entity</artifactId> <version>1.0-SNAPSHOT</version> <optional>true</optional> </dependency>

4.2 排除依赖(exclusions

  • 作用:排除不需要的传递依赖

  • 适用场景:传递依赖版本冲突、不需要的依赖

  • 配置示例:

    <dependency> <groupId>com.example</groupId> <artifactId>ssm-service</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>com.example</groupId> <artifactId>ssm-dao</artifactId> </exclusion> </exclusions> </dependency>

4.3 可选依赖 vs 排除依赖

特性 可选依赖 排除依赖
作用位置 依赖方 被依赖方
作用对象 当前依赖 传递依赖
目的 阻止依赖传递 移除不需要的传递依赖

Maven-05 - 聚合

5.1 什么是聚合

聚合是指将多个模块聚合到一个父工程中,通过父工程统一构建所有模块,简化构建流程。

5.2 聚合配置

在父工程的 pom.xml 中配置 <modules>

复制代码
<modules>
    <module>ssm-entity</module>
    <module>ssm-dao</module>
    <module>ssm-service</module>
    <module>ssm-web</module>
</modules>

5.3 聚合的优势

  • 统一构建:执行 mvn clean install 自动构建所有模块
  • 构建顺序:按模块依赖顺序构建(先构建被依赖模块)
  • 版本统一:父工程统一管理版本,避免版本冲突

Maven-06 - 继承

6.1 什么是继承

Maven 继承是指子工程继承父工程的配置(依赖、插件、属性等),统一管理项目配置,避免重复配置。

6.2 继承配置

子工程的 pom.xml 中配置父工程:

复制代码
<parent>
    <groupId>com.example</groupId>
    <artifactId>ssm-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

6.3 继承的核心作用

  • 统一依赖版本 :父工程 dependencyManagement 统一管理依赖版本,子工程无需指定版本
  • 统一插件配置:父工程统一配置编译、测试等插件
  • 统一属性配置:父工程统一配置项目属性(如 JDK 版本)
  • 简化配置:子工程无需重复配置相同内容

6.4 继承 vs 聚合

特性 继承 聚合
作用 子工程继承父工程配置 父工程聚合子工程,统一构建
关系 父子关系 聚合关系
目的 统一配置、简化配置 统一构建、简化构建流程
配置 <parent> <modules>

Maven-07 - 属性

7.1 Maven 属性的作用

Maven 属性用于统一配置、简化维护,避免在 pom.xml 中硬编码版本号、路径等信息,核心分为 5 类:

属性类型 说明 示例
内置属性 Maven 预定义的属性,直接使用 ${project.basedir}(项目根目录)、${version}(项目版本)
自定义属性 用户自定义的属性,用于统一版本 <properties><spring.version>5.3.20</spring.version></properties>
Setting 属性 来自 settings.xml 的属性 ${settings.localRepository}(本地仓库路径)
Java 系统属性 JVM 系统属性,可通过 mvn help:system 查看 ${java.version}(Java 版本)、${user.home}(用户目录)
环境变量属性 系统环境变量 ${env.JAVA_HOME}(JAVA_HOME 环境变量)

7.2 自定义属性使用示例

复制代码
<!-- 1. 定义属性 -->
<properties>
    <spring.version>5.3.20</spring.version>
    <mybatis.version>3.5.9</mybatis.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!-- 2. 使用属性 -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>
</dependencies>

7.3 常用内置属性

  • ${project.basedir}:项目根目录
  • ${project.version}:项目版本号
  • ${project.build.directory}:构建输出目录(target)
  • ${project.groupId}:项目 groupId
  • ${project.artifactId}:项目 artifactId

Maven-08 - 配置文件加载属性

8.1 配置文件加载原理

Maven 支持在 ** 资源文件(如 properties、xml)** 中引用 pom.xml 中定义的属性,通过 filtering 开启属性过滤,实现配置文件动态替换。

8.2 实现步骤

1. 开启资源过滤
复制代码
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering> <!-- 开启属性过滤 -->
        </resource>
    </resources>
</build>
2. 定义属性
复制代码
<properties>
    <jdbc.driver>com.mysql.cj.jdbc.Driver</jdbc.driver>
    <jdbc.url>jdbc:mysql://localhost:3306/test</jdbc.url>
    <jdbc.username>root</jdbc.username>
    <jdbc.password>root</jdbc.password>
</properties>
3. 资源文件中引用属性(jdbc.properties)
复制代码
jdbc.driver=${jdbc.driver}
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}
4. 构建后效果

Maven 会自动将 ${jdbc.driver} 等属性替换为实际值,生成最终的配置文件。


Maven-09 - 版本管理

9.1 版本号规范

Maven 遵循语义化版本规范 ,格式为:主版本号.次版本号.修订版本号-里程碑号

  • 主版本号:重大架构变更,不向下兼容(如 3.x → 4.x)
  • 次版本号:功能新增,向下兼容(如 3.1 → 3.2)
  • 修订版本号:Bug 修复,向下兼容(如 3.2.0 → 3.2.1)
  • 里程碑号:SNAPSHOT(快照版)、RELEASE(正式版)、Alpha/Beta/RC(测试版)

9.2 快照版 vs 正式版

版本类型 说明 特点
SNAPSHOT(快照版) 开发版本,用于开发阶段 每次构建会自动更新,Maven 会从仓库拉取最新版本
RELEASE(正式版) 发布版本,用于生产环境 版本固定,一旦发布不可修改

9.3 版本管理最佳实践

  • 统一版本管理 :在父工程 dependencyManagement 中统一管理依赖版本
  • 避免版本冲突 :使用 dependency:tree 查看依赖树,排除冲突版本
  • 快照版仅用于开发:正式环境使用 RELEASE 版本,避免不稳定
  • 语义化版本:遵循版本号规范,便于版本迭代

Maven-10 - 多环境开发

10.1 多环境开发的需求

项目在不同环境(开发、测试、生产)需要不同的配置(如数据库、端口、日志级别),Maven 支持通过 profiles 配置多环境,一键切换环境。

10.2 多环境配置示例

复制代码
<!-- 1. 定义多环境 -->
<profiles>
    <!-- 开发环境 -->
    <profile>
        <id>dev</id>
        <properties>
            <env>dev</env>
            <jdbc.url>jdbc:mysql://localhost:3306/dev_db</jdbc.url>
            <server.port>8080</server.port>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault> <!-- 默认激活 -->
        </activation>
    </profile>
    <!-- 测试环境 -->
    <profile>
        <id>test</id>
        <properties>
            <env>test</env>
            <jdbc.url>jdbc:mysql://test-db:3306/test_db</jdbc.url>
            <server.port>8081</server.port>
        </properties>
    </profile>
    <!-- 生产环境 -->
    <profile>
        <id>prod</id>
        <properties>
            <env>prod</env>
            <jdbc.url>jdbc:mysql://prod-db:3306/prod_db</jdbc.url>
            <server.port>80</server.port>
        </properties>
    </profile>
</profiles>

<!-- 2. 资源过滤,动态替换配置 -->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

10.3 切换环境命令

复制代码
# 激活开发环境(默认)
mvn clean install

# 激活测试环境
mvn clean install -P test

# 激活生产环境
mvn clean install -P prod

Maven-11 - 跳过测试

11.1 跳过测试的场景

  • 开发阶段,代码未完成,无需执行测试
  • 构建生产版本,跳过测试加快构建速度
  • 测试用例依赖外部环境,无法执行

11.2 跳过测试的方式

1. 命令行方式(推荐)
复制代码
# 跳过所有测试
mvn clean install -DskipTests

# 仅不执行测试,仍编译测试代码
mvn clean install -Dmaven.test.skip=true
2. pom.xml 配置方式
复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
            <configuration>
                <skipTests>true</skipTests> <!-- 跳过测试 -->
            </configuration>
        </plugin>
    </plugins>
</build>

11.3 两种方式的区别

方式 说明 编译测试代码 执行测试
-DskipTests 跳过测试执行
-Dmaven.test.skip=true 完全跳过测试

Maven-12 - 私服简介与安装

12.1 什么是 Maven 私服

Maven 私服是搭建在局域网内的 Maven 仓库服务器,用于统一管理项目依赖、加速构建、共享内部组件,是企业级开发的标准配置。

12.2 私服的核心作用

  • 加速构建:缓存中央仓库依赖,避免重复下载
  • 统一管理:统一管理项目依赖版本,避免版本冲突
  • 共享组件:内部开发的组件上传到私服,团队共享
  • 安全隔离:内网环境,避免外部依赖安全风险

12.3 主流私服产品

  • Nexus(Sonatype Nexus):最主流的 Maven 私服,开源免费,功能强大
  • Artifactory(JFrog Artifactory):企业级私服,功能丰富,收费
  • Archiva(Apache Archiva):Apache 开源私服,功能简单

12.4 Nexus 安装步骤

  1. 下载 Nexus:https://help.sonatype.com/repomanager3/download
  2. 解压安装包,进入 bin 目录
  3. 启动 Nexus:./nexus start(Linux)/ nexus.exe /run(Windows)
  4. 访问 Nexus:http://localhost:8081,默认账号 admin,密码在 admin.password 文件中

Maven-13 - 私服仓库分类

13.1 Nexus 仓库核心分类

仓库类型 说明 作用
宿主仓库(Hosted) 存储内部开发的组件 用于上传内部项目的 jar 包,团队共享
代理仓库(Proxy) 代理远程仓库(如 Maven 中央仓库) 缓存中央仓库依赖,加速构建
仓库组(Group) 聚合多个仓库,对外提供统一地址 简化配置,项目只需配置仓库组地址
虚拟仓库(Virtual) 兼容 Maven 1.x 版本 基本不用

13.2 Nexus 内置仓库

  • maven-central:代理 Maven 中央仓库
  • maven-public:仓库组,聚合了所有代理仓库和宿主仓库
  • maven-releases:宿主仓库,存储正式版组件
  • maven-snapshots:宿主仓库,存储快照版组件

Maven-14 - 本地仓库访问私服配置

14.1 配置私服地址

settings.xml 中配置私服服务器和认证信息:

复制代码
<!-- 1. 配置私服服务器 -->
<servers>
    <server>
        <id>nexus</id> <!-- 唯一标识,与pom.xml中id一致 -->
        <username>admin</username>
        <password>admin123</password>
    </server>
</servers>

<!-- 2. 配置镜像,将中央仓库请求转发到私服 -->
<mirrors>
    <mirror>
        <id>nexus</id>
        <mirrorOf>*</mirrorOf> <!-- 拦截所有仓库请求 -->
        <url>http://localhost:8081/repository/maven-public/</url>
    </mirror>
</mirrors>

14.2 pom.xml 配置上传私服

复制代码
<distributionManagement>
    <repository>
        <id>nexus</id> <!-- 与settings.xml中server的id一致 -->
        <url>http://localhost:8081/repository/maven-releases/</url>
    </repository>
    <snapshotRepository>
        <id>nexus</id>
        <url>http://localhost:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
</distributionManagement>

Maven-15 - 私服资源上传与下载

15.1 从私服下载依赖

配置私服后,Maven 会自动从私服拉取依赖,优先从私服获取,私服没有再从中央仓库拉取并缓存。

15.2 上传项目到私服

执行以下命令,将项目打包上传到私服:

复制代码
# 上传正式版
mvn clean deploy -P prod

# 上传快照版
mvn clean deploy -P dev

15.3 上传第三方 jar 到私服

复制代码
mvn deploy:deploy-file \
-DgroupId=com.example \
-DartifactId=third-party \
-Dversion=1.0.0 \
-Dpackaging=jar \
-Dfile=D:/third-party-1.0.0.jar \
-Durl=http://localhost:8081/repository/maven-releases/ \
-DrepositoryId=nexus

SpringBoot 入门全体系(01-11 章 完整笔记)


SpringBoot-01-SpringBoot 工程入门案例

1.1 SpringBoot 简介

SpringBoot 是 Spring 团队推出的快速开发框架 ,核心目标是简化 Spring 应用开发,解决 Spring 配置繁琐、搭建复杂的问题,实现 "约定大于配置"。

1.2 入门案例(IDEA 创建)

1. 环境准备
  • JDK 8+
  • Maven 3.6+
  • IDEA 开发工具
2. 创建 SpringBoot 项目
  • 选择 Spring Initializr,填写项目信息
  • 选择依赖:Spring Web
  • 生成项目,导入 IDEA
3. 启动类
复制代码
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
4. Controller 类
复制代码
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello SpringBoot!";
    }
}
5. 运行测试

启动项目,访问 http://localhost:8080/hello,返回 Hello SpringBoot!,入门案例成功。


SpringBoot-02-SpringBoot 工程官网创建

2.1 官网创建项目

访问 SpringBoot 官网:https://start.spring.io/,填写项目信息,选择依赖,下载项目压缩包,导入 IDEA 即可。

2.2 官网创建优势

  • 无需 IDEA,任何环境均可创建
  • 可选择最新的 SpringBoot 版本
  • 可自定义项目结构、依赖

2.3 核心配置选项

  • Project:Maven/Gradle 项目
  • Language:Java/Kotlin/Groovy
  • Spring Boot:版本选择
  • Dependencies:依赖选择(如 Spring Web、Spring Data JPA)

SpringBoot-03-SpringBoot 程序快速启动

3.1 快速启动方式

1. IDEA 直接启动

运行启动类 main 方法,IDEA 自动启动内嵌 Tomcat,无需额外配置。

2. 命令行启动
复制代码
# 打包项目
mvn clean package

# 启动项目
java -jar target/demo-0.0.1-SNAPSHOT.jar
3. 内嵌容器启动

SpringBoot 内嵌 Tomcat/Jetty/Undertow,无需额外安装 Tomcat,直接运行 jar 包即可启动。

3.2 启动原理

SpringBoot 启动时,自动扫描 @SpringBootApplication 标注的类,加载所有配置、Bean,启动内嵌容器,完成项目启动。


SpringBoot-04-SpringBoot 简介 (起步依赖)

4.1 起步依赖(Starter)

SpringBoot 提供的一站式依赖,将常用依赖整合在一起,无需手动配置版本,避免版本冲突。

4.2 核心起步依赖

起步依赖 说明
spring-boot-starter-web Web 开发依赖,包含 SpringMVC、Tomcat 等
spring-boot-starter-test 测试依赖,包含 JUnit、Mockito 等
spring-boot-starter-data-jpa JPA 数据库操作依赖
spring-boot-starter-mybatis MyBatis 数据库操作依赖
spring-boot-starter-aop AOP 面向切面依赖

4.3 起步依赖原理

起步依赖通过 spring-boot-dependencies 统一管理依赖版本,无需手动指定版本,实现 "约定大于配置"。


SpringBoot-05-SpringBoot 简介 (辅助功能之...)

5.1 SpringBoot 辅助功能

SpringBoot 提供了大量辅助功能,简化开发:

  • 自动配置@EnableAutoConfiguration 自动配置 Bean,无需手动配置
  • 内嵌容器:内嵌 Tomcat,无需额外安装
  • 命令行工具spring-boot-cli 命令行工具,快速开发
  • 监控管理spring-boot-starter-actuator 监控项目运行状态
  • 热部署spring-boot-devtools 热部署,修改代码自动重启
  • 配置文件application.properties/application.yml 统一配置

5.2 热部署配置

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

IDEA 开启自动编译,修改代码自动重启,无需手动重启项目。


SpringBoot-06 - 配置文件格式 (3 种)

6.1 配置文件格式

SpringBoot 支持 3 种配置文件格式:

  1. application.properties:键值对格式,传统格式
  2. application.yml:YAML 格式,缩进式,可读性强(推荐)
  3. application.yaml :YAML 格式,与 .yml 等价

6.2 三种格式对比

格式 语法 可读性 推荐度
.properties key=value 一般 一般
.yml 缩进式,层级结构 推荐
.yaml .yml 一致 一般

6.3 配置示例

properties 格式
复制代码
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
yml 格式
复制代码
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root

SpringBoot-07-yaml 格式

7.1 YAML 语法规则

  • 大小写敏感
  • 缩进表示层级,缩进使用空格,不允许使用 Tab
  • 冒号后必须加空格
  • 列表使用 - 表示
  • 键值对使用 key: value 表示

7.2 YAML 数据类型

1. 普通数据类型
复制代码
name: 张三
age: 20
flag: true
2. 对象 / Map
复制代码
user:
  name: 张三
  age: 20
# 行内写法
user: {name: 张三, age: 20}
3. 数组 / List
复制代码
hobby:
  - 篮球
  - 足球
# 行内写法
hobby: [篮球, 足球]
4. 参数引用
复制代码
name: 张三
info: "姓名:${name}"

SpringBoot-08-yaml 数据读取方式 (3 种)

8.1 3 种读取方式

1. @Value 注解(读取单个属性)
复制代码
@Component
public class User {
    @Value("${user.name}")
    private String name;
    @Value("${user.age}")
    private Integer age;
}
2. @ConfigurationProperties 注解(批量读取,推荐)
复制代码
@Component
@ConfigurationProperties(prefix = "user")
public class User {
    private String name;
    private Integer age;
    // getter/setter
}
3. Environment 接口(动态读取)
复制代码
@Component
public class User {
    @Autowired
    private Environment env;

    public void print() {
        String name = env.getProperty("user.name");
        Integer age = Integer.parseInt(env.getProperty("user.age"));
    }
}

8.2 三种方式对比

方式 适用场景 优点 缺点
@Value 单个属性 简单直观 批量读取繁琐
@ConfigurationProperties 批量读取 简洁高效,支持数据校验 需创建实体类
Environment 动态读取 灵活 代码繁琐,类型转换麻烦

SpringBoot-09 - 多环境开发配置

9.1 多环境配置文件

SpringBoot 支持多环境配置,通过 application-{env}.yml 区分不同环境:

  • application-dev.yml:开发环境
  • application-test.yml:测试环境
  • application-prod.yml:生产环境

9.2 激活环境

在主配置文件 application.yml 中激活环境:

复制代码
spring:
  profiles:
    active: dev # 激活开发环境

9.3 命令行激活环境

复制代码
java -jar demo.jar --spring.profiles.active=prod

SpringBoot-10 - 多环境命令行启动参数

10.1 命令行参数优先级

命令行参数优先级高于配置文件,可动态修改配置:

复制代码
# 修改端口号
java -jar demo.jar --server.port=8081

# 激活生产环境
java -jar demo.jar --spring.profiles.active=prod

# 修改数据库密码
java -jar demo.jar --spring.datasource.password=123456

10.2 常用命令行参数

  • --server.port:修改端口号
  • --spring.profiles.active:激活环境
  • --spring.datasource.url:修改数据库地址
  • --logging.level.root:修改日志级别

SpringBoot-11 - 多环境开发兼容问题

11.1 常见兼容问题

  • 配置文件优先级问题:命令行 > 外部配置文件 > 内部配置文件
  • 依赖版本冲突:不同环境依赖版本不一致
  • 数据库连接问题:不同环境数据库地址、账号不一致
  • 日志级别问题:开发环境日志级别 debug,生产环境 info

11.2 解决方案

  • 统一配置管理 :通过 application.yml 统一管理配置,多环境配置文件仅区分差异
  • 依赖版本统一 :在 pom.xml 中统一管理依赖版本,避免版本冲突
  • 配置文件加密:敏感信息(如数据库密码)使用加密配置,避免明文泄露
  • 日志级别区分:开发环境日志级别 debug,生产环境 info,通过多环境配置区分

SpringBoot-12 - 配置文件分类

12.1 配置文件分类与优先级

SpringBoot 配置文件按加载优先级分为 4 大类,优先级从高到低如下(高优先级覆盖低优先级):

分类 路径 / 说明 优先级 适用场景
命令行参数 java -jar xxx.jar --server.port=8081 最高 动态修改配置、生产环境调参
外部配置文件 jar 包同级目录的 application.yml 次高 生产环境独立配置,无需改 jar
内部配置文件 resources 目录下的 application.yml 开发环境默认配置
@ConfigurationProperties 注解配置 代码中绑定的配置类 最低 配置类默认值、兜底配置

12.2 内部配置文件细分

内部配置文件按加载顺序优先级:

  1. application-{profile}.yml(环境专属,如 application-prod.yml
  2. application.yml(主配置文件)
  3. application.properties(同目录下优先级低于 yml)

12.3 配置加载最佳实践

  • 开发环境:用 resources 下的 application-dev.yml
  • 测试环境:用外部配置文件覆盖,避免改代码
  • 生产环境:用命令行参数 + 外部配置文件,保证配置灵活
  • 敏感配置(如密码):用环境变量 / 外部配置,避免硬编码

SpringBoot-13-SpringBoot 整合 JUnit

13.1 整合核心依赖

SpringBoot 提供 spring-boot-starter-test 起步依赖,一键整合 JUnit 5、Mockito 等测试框架:

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

13.2 单元测试代码示例

复制代码
@SpringBootTest // 标记为SpringBoot测试类,自动加载容器
class UserServiceTest {

    @Autowired
    private UserService userService; // 直接注入Spring管理的Bean

    @Test
    void testFindAll() {
        List<User> list = userService.findAll();
        Assertions.assertFalse(list.isEmpty()); // 断言结果
    }

    @Test
    @Transactional // 测试事务,执行后自动回滚,不污染数据库
    void testAdd() {
        User user = new User("张三", 20);
        userService.add(user);
        Assertions.assertNotNull(user.getId());
    }
}

13.3 核心注解说明

注解 作用
@SpringBootTest 加载 SpringBoot 容器,注入 Bean
@Transactional 测试事务,自动回滚,保护测试数据
@Test JUnit 测试方法标记
@MockBean Mock 依赖,隔离外部服务

SpringBoot-14-SpringBoot 整合 MyBatis

14.1 整合核心依赖

复制代码
<!-- MyBatis SpringBoot整合起步依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Druid数据源(可选) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.16</version>
</dependency>

14.2 核心配置(application.yml)

复制代码
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml # Mapper映射文件路径
  type-aliases-package: com.example.entity # 实体类别名包
  configuration:
    map-underscore-to-camel-case: true # 下划线转驼峰

14.3 代码实现

1. 实体类
复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Integer age;
}
2. Mapper 接口
复制代码
@Mapper // 标记为MyBatis Mapper,Spring自动扫描
public interface UserMapper {
    List<User> findAll();
}
3. Mapper 映射文件(UserMapper.xml)
复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <select id="findAll" resultType="user">
        select * from user
    </select>
</mapper>
4. Service 层
复制代码
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }
}

SpringBoot-15 - 案例:基于 SpringBoot 实现 CRUD

15.1 案例需求

基于 SpringBoot + MyBatis 实现用户的 ** 增删改查(CRUD)** 完整功能,包含 Controller、Service、Mapper 全链路。

15.2 完整代码实现

1. Controller 层
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 查询所有
    @GetMapping
    public Result<List<User>> findAll() {
        return Result.success(userService.findAll());
    }

    // 新增
    @PostMapping
    public Result<Void> add(@RequestBody User user) {
        userService.add(user);
        return Result.success(null);
    }

    // 修改
    @PutMapping
    public Result<Void> update(@RequestBody User user) {
        userService.update(user);
        return Result.success(null);
    }

    // 删除
    @DeleteMapping("/{id}")
    public Result<Void> delete(@PathVariable Integer id) {
        userService.delete(id);
        return Result.success(null);
    }
}
2. Service 层
复制代码
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }

    @Override
    @Transactional
    public void add(User user) {
        userMapper.add(user);
    }

    @Override
    @Transactional
    public void update(User user) {
        userMapper.update(user);
    }

    @Override
    @Transactional
    public void delete(Integer id) {
        userMapper.delete(id);
    }
}
3. Mapper 层
复制代码
@Mapper
public interface UserMapper {
    List<User> findAll();
    void add(User user);
    void update(User user);
    void delete(Integer id);
}

MyBatis-Plus 全体系


MyBatis-Plus-01-MyBatis-Plus 入门案例

1.1 MyBatis-Plus 简介

MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 基础上只做增强不做改变,简化开发、提高效率,内置通用 Mapper、分页插件、条件构造器等功能,无需编写 XML 即可实现 CRUD。

1.2 入门案例步骤

1. 引入依赖
复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
2. 实体类
复制代码
@Data
@TableName("user") // 绑定数据库表
public class User {
    @TableId(type = IdType.AUTO) // 主键自增
    private Integer id;
    private String name;
    private Integer age;
}
3. Mapper 接口(继承 BaseMapper)
复制代码
public interface UserMapper extends BaseMapper<User> {
    // 无需编写XML,BaseMapper内置通用CRUD方法
}
4. 启动类添加 @MapperScan
复制代码
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描Mapper接口
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
5. 测试
复制代码
@SpringBootTest
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    void testSelectList() {
        List<User> list = userMapper.selectList(null); // null表示查询所有
        list.forEach(System.out::println);
    }
}

MyBatis-Plus-02-MyBatis-Plus 简介

2.1 核心特性

  • 无侵入:只做增强不做改变,不影响原有 MyBatis 功能
  • 强大的 CRUD:内置通用 Mapper,无需编写 XML
  • 条件构造器:Lambda 条件查询,避免硬编码
  • 分页插件:一键实现分页,支持多种数据库
  • 主键自动生成:支持多种主键策略(自增、UUID、雪花算法等)
  • 逻辑删除:无需手动删除,标记删除即可
  • 乐观锁:自动实现乐观锁,解决并发问题
  • 代码生成器:一键生成 Entity、Mapper、Service、Controller 代码

2.2 与 MyBatis 的对比

特性 MyBatis MyBatis-Plus
CRUD 开发 需手动编写 XML、Mapper 方法 继承 BaseMapper,零 XML 实现 CRUD
分页功能 需手动编写分页代码 分页插件一键实现
条件查询 需手动拼接 SQL Lambda 条件构造器,类型安全
主键生成 需手动配置 内置多种主键策略
开发效率 高(减少 80% 重复代码)

MyBatis-Plus-03 - 标准 CRUD 制作

3.1 BaseMapper 内置 CRUD 方法

方法 作用
selectById(Serializable id) 根据 ID 查询
selectBatchIds(Collection idList) 批量 ID 查询
selectList(Wrapper queryWrapper) 条件查询
insert(T entity) 新增
updateById(T entity) 根据 ID 修改
deleteById(Serializable id) 根据 ID 删除
deleteBatchIds(Collection idList) 批量删除

3.2 CRUD 代码示例

复制代码
@SpringBootTest
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    // 新增
    @Test
    void testInsert() {
        User user = new User(null, "张三", 20);
        int result = userMapper.insert(user);
        System.out.println("影响行数:" + result);
    }

    // 修改
    @Test
    void testUpdate() {
        User user = new User(1, "张三", 21);
        userMapper.updateById(user);
    }

    // 删除
    @Test
    void testDelete() {
        userMapper.deleteById(1);
    }

    // 查询所有
    @Test
    void testSelectAll() {
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}

MyBatis-Plus-04 - 标准分页功能制作

4.1 分页插件配置

复制代码
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {
    // 分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

4.2 分页代码示例

复制代码
@Test
void testPage() {
    // 分页参数:当前页(1),每页条数(5)
    Page<User> page = new Page<>(1, 5);
    // 执行分页查询
    Page<User> result = userMapper.selectPage(page, null);
    // 获取分页数据
    System.out.println("总条数:" + result.getTotal());
    System.out.println("总页数:" + result.getPages());
    System.out.println("当前页数据:" + result.getRecords());
}

MyBatis-Plus-05 - 条件查询的三种格式

5.1 三种条件查询格式

1. 硬编码格式(不推荐)
复制代码
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三").gt("age", 18);
List<User> list = userMapper.selectList(wrapper);
2. Lambda 格式(推荐,类型安全)
复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三").gt(User::getAge, 18);
List<User> list = userMapper.selectList(wrapper);
3. 链式调用格式(简化写法)
复制代码
List<User> list = userMapper.selectList(
    Wrappers.<User>lambdaQuery()
        .eq(User::getName, "张三")
        .gt(User::getAge, 18)
);

5.2 常用条件方法

方法 作用
eq 等于(=)
ne 不等于(!=)
gt 大于(>)
ge 大于等于(>=)
lt 小于(<)
le 小于等于(<=)
like 模糊查询(LIKE)
in 包含(IN)
between 区间查询(BETWEEN)
orderByAsc/orderByDesc 排序

MyBatis-Plus-06 - 条件查询 null 判定

6.1 问题背景

条件查询时,若参数为 null,MP 会自动忽略该条件,避免 SQL 语法错误。

6.2 手动控制 null 判定

1. 条件判断
复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
if (name != null) {
    wrapper.eq(User::getName, name);
}
if (age != null) {
    wrapper.gt(User::getAge, age);
}
2. condition 参数控制
复制代码
// condition为true时,条件生效;false时,条件忽略
wrapper.eq(StringUtils.isNotBlank(name), User::getName, name)
       .gt(age != null, User::getAge, age);

6.3 最佳实践

  • condition 参数控制条件,避免手动 if 判断
  • 结合 StringUtils 等工具类,统一处理 null / 空字符串
  • 复杂条件用 Lambda 格式,保证类型安全

MyBatis-Plus-07 - 查询投影

7.1 什么是查询投影

查询投影指只查询指定字段,而非查询所有字段,提升查询效率。

7.2 投影查询方式

1. select 方法指定字段
复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName) // 只查询id和name
       .eq(User::getAge, 20);
List<User> list = userMapper.selectList(wrapper);
2. 自定义映射(查询非实体类字段)
复制代码
@Data
public class UserVO {
    private Integer id;
    private String name;
}

@Test
void testSelectVo() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(User::getId, User::getName);
    List<UserVO> list = userMapper.selectList(wrapper).stream()
            .map(user -> {
                UserVO vo = new UserVO();
                vo.setId(user.getId());
                vo.setName(user.getName());
                return vo;
            })
            .collect(Collectors.toList());
}

MyBatis-Plus-08 - 查询条件设置

8.1 多条件组合查询

复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张") // 姓名包含"张"
       .between(User::getAge, 18, 30) // 年龄18-30
       .orderByDesc(User::getAge); // 按年龄降序
List<User> list = userMapper.selectList(wrapper);

8.2 空值处理与条件优先级

  • 条件优先级:后设置的条件覆盖先设置的条件

  • 空值自动忽略:参数为 null 时,条件不生效

  • 多条件逻辑:默认 AND,用 or() 切换 OR 逻辑

    wrapper.eq(User::getName, "张三").or().eq(User::getName, "李四"); // OR逻辑


MyBatis-Plus-09 - 映射匹配兼容性

9.1 字段映射规则

  • 表名映射@TableName("user") 绑定实体类与数据库表
  • 主键映射@TableId 标记主键字段
  • 普通字段映射@TableField("user_name") 绑定实体类属性与数据库字段
  • 下划线转驼峰 :MP 自动开启 map-underscore-to-camel-case,无需手动映射

9.2 兼容性处理

1. 字段不一致处理
复制代码
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField("user_name") // 数据库字段为user_name,实体类为name
    private String name;
    @TableField(exist = false) // 标记该字段不存在于数据库表
    private String temp;
}
2. 表名前缀统一处理
复制代码
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_ # 统一表名前缀,实体类无需加@TableName

MyBatis-Plus-10-id 生成策略

10.1 主键生成策略(@TableId(type = IdType.xxx)

策略 说明 适用场景
AUTO 数据库自增 MySQL 自增主键
ASSIGN_ID 雪花算法(默认) 分布式场景,全局唯一 ID
ASSIGN_UUID UUID 分布式场景,无序 ID
INPUT 手动输入 手动指定 ID
NONE 无策略 需手动配置

10.2 全局主键策略配置

复制代码
mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id # 全局主键策略为雪花算法

MyBatis-Plus-11 - 多数据操作 (删除与查询)

11.1 批量查询

复制代码
// 批量ID查询
List<Integer> idList = Arrays.asList(1, 2, 3);
List<User> list = userMapper.selectBatchIds(idList);

11.2 批量删除

复制代码
// 批量ID删除
List<Integer> idList = Arrays.asList(1, 2, 3);
userMapper.deleteBatchIds(idList);

11.3 条件批量删除

复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.lt(User::getAge, 18); // 删除年龄小于18的用户
userMapper.delete(wrapper);

MyBatis-Plus-12 - 逻辑删除

12.1 什么是逻辑删除

逻辑删除指不物理删除数据 ,而是标记删除状态(如 deleted=1),数据仍保留在数据库中,便于数据恢复。

12.2 逻辑删除配置

1. 数据库添加删除字段
复制代码
ALTER TABLE user ADD COLUMN deleted INT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除';
2. 实体类添加 @TableLogic
复制代码
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    private Integer age;
    @TableLogic // 标记逻辑删除字段
    private Integer deleted;
}
3. 全局配置(可选)
复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除字段
      logic-delete-value: 1 # 已删除值
      logic-not-delete-value: 0 # 未删除值

12.3 逻辑删除效果

  • 执行 deleteById 时,MP 自动执行 UPDATE user SET deleted=1 WHERE id=?
  • 查询时,MP 自动添加 WHERE deleted=0 条件,过滤已删除数据

MyBatis-Plus-13 - 乐观锁

13.1 什么是乐观锁

乐观锁是一种并发控制机制,假设数据不会冲突,在更新时检查数据是否被修改,若被修改则拒绝更新,避免并发问题。

13.2 乐观锁实现步骤

1. 数据库添加版本字段
复制代码
ALTER TABLE user ADD COLUMN version INT DEFAULT 1 COMMENT '乐观锁版本号';
2. 实体类添加 @Version
复制代码
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    @Version // 标记乐观锁版本字段
    private Integer version;
}
3. 配置乐观锁插件
复制代码
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    // 分页插件
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

13.3 乐观锁执行效果

执行 updateById 时,MP 自动执行:

复制代码
UPDATE user SET name=?, version=version+1 WHERE id=? AND version=?

若版本号不匹配,更新失败,避免并发冲突。


MyBatis-Plus-14 - 代码生成器

14.1 代码生成器作用

一键生成 Entity、Mapper、Service、Controller 代码,减少重复开发,提升开发效率。

14.2 代码生成器使用示例

复制代码
public class CodeGenerator {
    public static void main(String[] args) {
        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(
                "jdbc:mysql://localhost:3306/test",
                "root",
                "root"
        ).build();

        // 代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setDataSource(dataSourceConfig);

        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig.Builder()
                .outputDir(System.getProperty("user.dir") + "/src/main/java")
                .author("MyBatis-Plus")
                .fileOverride()
                .build();
        autoGenerator.setGlobalConfig(globalConfig);

        // 包配置
        PackageConfig packageConfig = new PackageConfig.Builder()
                .parent("com.example")
                .entity("entity")
                .mapper("mapper")
                .service("service")
                .controller("controller")
                .build();
        autoGenerator.setPackageInfo(packageConfig);

        // 策略配置
        StrategyConfig strategyConfig = new StrategyConfig.Builder()
                .addInclude("user") // 要生成的表名
                .entityBuilder()
                .enableLombok() // 开启Lombok
                .logicDeleteColumnName("deleted") // 逻辑删除字段
                .versionColumnName("version") // 乐观锁字段
                .build();
        autoGenerator.setStrategy(strategyConfig);

        // 执行生成
        autoGenerator.execute();
    }
}
相关推荐
武子康2 小时前
大数据-262 实时数仓 - Canal 同步数据实战指南 实时统计
大数据·hadoop·后端
難釋懷2 小时前
Nginx本地缓存API
nginx·spring·缓存
zzb15802 小时前
系统提示词-System Prompt 动态组装
人工智能·后端·python·prompt
RInk7oBjo2 小时前
spring boot3--自动配置与手动配置
java·spring boot·后端
最初的↘那颗心2 小时前
LangChain4j核心能力:AiService、Prompt注解与结构化输出实战
java·大模型·结构化输出·langchain4j·aiservice
lixia0417mul22 小时前
简单的RAG知识库问答
java
云烟成雨TD2 小时前
Spring AI 1.x 系列【25】结构化输出案例演示
java·人工智能·spring
鱼鳞_2 小时前
Java学习笔记_Day23(HashMap)
java·笔记·学习