本文是 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 大模块:
- Core Container(核心容器) :
spring-core、spring-beans、spring-context、spring-expression,IoC/DI 核心 - AOP(面向切面) :
spring-aop,AOP 编程支持 - Data Access/Integration(数据访问) :
spring-jdbc、spring-tx、spring-orm、spring-jms,数据库、消息队列支持 - Web(Web 开发) :
spring-web、spring-webmvc、spring-websocket,Web 开发支持 - Test(测试) :
spring-test,单元测试、集成测试支持 - Instrumentation( instrumentation) :
spring-instrument,JVM 代理支持 - Messaging(消息) :
spring-messaging,消息编程支持
2.2 核心模块详解
1. Core Container(核心容器)
spring-core:核心工具类,Spring 基础spring-beans:Bean 管理,IoC 核心spring-context:容器上下文,加载配置、管理 Beanspring-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(常用实现ClassPathXmlApplicationContext、AnnotationConfigApplicationContext) - 容器职责:加载配置、创建 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 注入方式:
- setter 注入:通过 setter 方法注入依赖(最常用)
- 构造器注入:通过构造方法注入依赖(Spring 4.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 种方式:
- 构造方法实例化(默认):通过无参构造器创建对象(最常用)
- 静态工厂实例化:通过静态工厂方法创建对象
- 实例工厂实例化:通过实例工厂方法创建对象
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 个阶段)
- 实例化:容器通过构造方法 / 工厂创建 Bean 实例
- 属性赋值:DI 注入依赖,给 Bean 的属性赋值
- Aware 接口回调 :调用
BeanNameAware、BeanFactoryAware、ApplicationContextAware等接口方法 - BeanPostProcessor 前置处理 :调用
postProcessBeforeInitialization()方法 - 初始化 :调用
init-method方法、InitializingBean的afterPropertiesSet()方法 - BeanPostProcessor 后置处理 :调用
postProcessAfterInitialization()方法 - Bean 就绪:Bean 可以使用
- 运行:Bean 正常工作
- 销毁 :容器关闭时,调用
destroy-method方法、DisposableBean的destroy()方法 - 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 种自动装配模式:
no(默认):不自动装配,需手动配置byName:按属性名自动装配,属性名与 Bean id 一致byType:按类型自动装配,容器中类型匹配的 Bean 自动注入constructor:按构造器参数类型自动装配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 容器启动流程
- 加载配置:加载 XML / 注解配置,解析 Bean 定义
- 实例化 Bean:创建 Bean 实例(单例 Bean 预加载)
- DI 注入:为 Bean 注入依赖
- 初始化 Bean:调用初始化方法、BeanPostProcessor
- 容器就绪:Bean 可以使用
- 容器关闭:销毁 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
四、容器与扩展
- 容器接口:
BeanFactory、ApplicationContext - 配置加载:XML 配置、properties 文件加载
- 案例:数据源管理、容器启动流程
18.2 核心最佳实践
- 依赖注入:优先使用构造器注入,保证依赖完整性
- Bean 作用域 :默认单例,多例场景使用
prototype - 自动装配 :优先使用
byType,避免byName的命名依赖 - 配置分离:使用 properties 文件管理配置,避免硬编码
- 容器管理 :使用
ApplicationContext作为容器,预加载 Bean - 生命周期 :合理使用
init-method、destroy-method管理 Bean 生命周期
18.3 学习路线建议
- 掌握 IoC/DI 核心思想,理解容器作用
- 熟练 Bean 配置、依赖注入、Bean 生命周期
- 掌握集合注入、自动装配、properties 加载等实用技能
- 实战案例:数据源管理、Spring 整合 MyBatis
- 进阶学习: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. 核心配置步骤
- 开启组件扫描:
<context:component-scan base-package="com.example"/> - 在类上添加注解:
@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 场景
第三方类(如 DruidDataSource、JdbcTemplate)无法添加 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 注入方式
- 方法参数注入 :
@Bean方法直接传参 - 成员变量注入 :在配置类中使用
@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 全流程总结
- 配置 :
@Configuration+@ComponentScan - 定义 Bean :
@Component家族 /@Bean - 依赖注入 :
@Autowired/@Resource/@Value - 生命周期 :
@PostConstruct/@PreDestroy - 作用域 :
@Scope
25.2 最佳实践
- 类级别的 Bean 用
@Component系列 - 第三方类用
@Bean - 构造器注入优先(
@Autowired构造器)
Spring-26-Spring 整合 MyBatis 思路分析
26.1 整合核心思想
- MyBatis 负责创建
SqlSessionFactory和Mapper - Spring 负责管理这些对象,完成依赖注入,控制事务
26.2 整合步骤(思路)
- 引入依赖(MyBatis + Spring + 整合包)
- 配置
SqlSessionFactoryBean(核心) - 配置
MapperScannerConfigurer扫描 Mapper - 注入 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 版)
- 定义业务类
- 定义切面类(加
@Aspect) - 配置切入点、通知
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 核心流程(四步走)
- 织入(Weaving) :Spring 容器启动时,扫描
@Aspect切面,根据切入点匹配目标类。 - 创建代理(Proxy):对目标类创建动态代理(JDK 动态代理 / CGLIB 代理)。
- 调用:业务代码调用代理对象,代理对象拦截方法。
- 执行通知:代理对象按照通知类型(前置、环绕等)执行切面逻辑,再调用目标方法。
31.2 代理创建规则
- 目标类实现了接口 → JDK 动态代理(基于接口)
- 目标类未实现接口 → CGLIB 代理(基于继承)
31.3 关键流程图示
业务代码 → 代理对象 → 拦截切入点 → 执行通知 → 执行目标方法 → 返回结果
Spring-32-AOP 切入点表达式
32.1 核心语法
execution(访问修饰符 返回值 包名.类名.方法名(参数))
32.2 通配符
*:匹配任意字符..:匹配任意参数(方法参数)或任意子包+:匹配类及其子类(通常用于类名后)
32.3 常用示例
- 匹配任意方法 :
execution(* *.*(..)) - 匹配某包下所有类方法 :
execution(* com.example.service.*.*(..)) - 匹配特定方法 :
execution(* com.example.service.UserService.add*(..)) - 匹配特定参数 :
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 注解开发核心步骤
- 导入
spring-aspects依赖 - 开启 AOP 自动代理:
@EnableAspectJAutoProxy - 编写切面类并加
@Aspect+@Component - 定义切入点
@Pointcut - 配置 5 种通知:
@Before/@After/@AfterReturning/@AfterThrowing/@Around
35.2 XML 配置 vs 注解配置
- XML:配置集中,适合老项目、复杂 AOP 规则
- 注解:简洁直观、开发快,SpringBoot 主流方式
35.3 AOP 使用注意事项
- 同一个切面内多个通知执行顺序:Around → Before → 方法 → After → AfterReturning/AfterThrowing
- 环绕通知必须手动调用
proceed(),否则目标方法不执行 - 切入点表达式尽量精准,避免拦截范围过大影响性能
- 同类内部方法调用(this.method ())不会走 AOP 代理
Spring-36-Spring 事务简介
36.1 什么是事务
一组操作要么全部成功 ,要么全部失败,保证数据一致性。
36.2 事务四大特性 ACID
- 原子性:不可分割
- 一致性:执行前后数据合法
- 隔离性:多事务互不干扰
- 持久性:提交后永久生效
36.3 Spring 事务管理方式
- 编程式事务:代码手动控制(极少用)
- 声明式事务 :XML / 注解控制(主流)底层基于 AOP 动态代理 实现
36.4 核心接口
PlatformTransactionManager:事务管理器(真正干活)TransactionDefinition:事务定义(传播、隔离、超时等)TransactionStatus:事务运行状态
Spring-37 - 声明式事务注解开发
37.1 核心注解
@Transactional:加在方法 / 类上表示开启事务
37.2 常用属性
| 属性 | 说明 |
|---|---|
| propagation | 事务传播行为 |
| isolation | 事务隔离级别 |
| timeout | 超时时间(秒) |
| readOnly | 是否只读事务 |
| rollbackFor | 哪些异常回滚 |
| noRollbackFor | 哪些异常不回滚 |
37.3 实现步骤
- 配置数据源、SqlSessionFactory
- 配置事务管理器
DataSourceTransactionManager - 开启事务注解:
@EnableTransactionManagement - 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 种)
控制多个带事务方法互相调用时事务如何传递:
- **REQUIRED(默认)**有事务就加入,没有就新建
- REQUIRES_NEW新建独立事务,挂起当前事务
- SUPPORTS有事务就用,没有就非事务运行
- MANDATORY必须在事务内运行,否则抛异常
- NOT_SUPPORTED非事务运行,挂起当前事务
- NEVER必须非事务运行,否则抛异常
- NESTED嵌套事务,有保存点
38.2 事务隔离级别
解决并发问题:脏读、不可重复读、幻读
- DEFAULT:使用数据库默认
- READ_UNCOMMITTED:读未提交(问题最多)
- READ_COMMITTED:读已提交(Oracle 默认)
- REPEATABLE_READ:可重复读(MySQL 默认)
- SERIALIZABLE:串行化(最安全、性能低)
38.3 事务使用要点
@Transactional只对 public 方法 生效- 默认只对 RuntimeException 回滚
- 想要所有异常都回滚:
rollbackFor = Exception.class - 同类内部调用不生效(不走代理)
- 读操作设
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):
REQUIRED、REQUIRES_NEW等 - 隔离级别(Isolation):
READ_COMMITTED、REPEATABLE_READ等 - 超时时间(Timeout):事务最大执行时间
- 只读(ReadOnly):是否为只读事务
- 回滚规则(Rollback Rules):哪些异常回滚、哪些不回滚
- 传播行为(Propagation):
3. 事务状态(TransactionStatus)
- 用于跟踪事务的运行状态,是事务管理器操作事务的依据。
- 核心方法:
isNewTransaction():是否为新事务hasSavepoint():是否有保存点isCompleted():事务是否已完成(提交 / 回滚)setRollbackOnly():标记事务为回滚状态
4. 事务切面(TransactionInterceptor)
- 是 Spring 事务的 AOP 实现载体 ,继承了
MethodInterceptor,用于拦截目标方法。 - 执行流程:
- 拦截目标方法,获取事务定义
- 调用事务管理器开启事务
- 执行目标方法
- 方法正常执行 → 提交事务
- 方法抛出异常 → 回滚事务
39.3 Spring 事务执行完整流程
- 调用目标方法 → 被
TransactionInterceptor拦截 - 拦截器读取
@Transactional注解,封装为TransactionDefinition - 调用
PlatformTransactionManager.getTransaction()开启事务 - 执行目标业务方法
- 方法正常返回 → 调用
commit()提交事务 - 方法抛出异常 → 调用
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 :指定哪些异常触发事务回滚(默认仅
RuntimeException和Error回滚) - noRollbackFor:指定哪些异常不触发事务回滚
- 生产环境推荐:
@Transactional(rollbackFor = Exception.class)→ 所有异常都回滚,避免漏回滚。
40.3 事务属性使用最佳实践
- 传播行为 :默认
REQUIRED即可,特殊场景用REQUIRES_NEW - 隔离级别 :默认
DEFAULT,用数据库默认级别,无需手动修改 - 回滚规则 :必须加
rollbackFor = Exception.class,避免受检异常不回滚 - 只读 :查询方法加
readOnly = true,优化性能 - 超时时间:核心业务方法设置合理超时,避免死锁
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 步)
- 用户发送请求 → 被
DispatcherServlet(前端控制器)拦截 DispatcherServlet调用HandlerMapping(处理器映射器)HandlerMapping根据请求路径匹配Controller,返回HandlerExecutionChainDispatcherServlet调用HandlerAdapter(处理器适配器)HandlerAdapter执行Controller中的目标方法Controller执行完成,返回ModelAndView(模型数据 + 视图名)HandlerAdapter将ModelAndView返回给DispatcherServletDispatcherServlet调用ViewResolver(视图解析器),解析视图名,返回真实视图DispatcherServlet渲染视图(将模型数据填充到视图)- 响应结果给用户
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.service、com.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 加载流程
- Tomcat 启动 → 加载
web.xml - 加载 Spring 根容器(
ContextLoaderListener) - 加载 SpringMVC 子容器(
DispatcherServlet) - 子容器继承父容器,可访问父容器 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-Type为application/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 功能模块开发流程
- 创建实体类 :
User实体,对应数据库表 - 创建 DAO 层 :
UserDao接口 +UserMapper.xml映射文件 - 创建 Service 层 :
UserService接口 +UserServiceImpl实现类 - 创建 Controller 层 :
UserController,调用 Service,处理请求 - 测试接口: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. 测试流程
- 测试 DAO 层:直接调用 Mapper,验证数据库操作
- 测试 Service 层:调用 Service,验证业务逻辑与事务
- 测试 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 异常处理流程
- Controller 抛出异常 → 被
@ControllerAdvice拦截 - 匹配对应的
@ExceptionHandler方法 - 执行异常处理逻辑,返回统一结果给前端
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(每页条数) - 响应数据:
currentPage、pageSize、total(总条数)、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 方法,响应依次经过所有拦截器的 postHandle 和 afterCompletion 方法。
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 常见模块拆分方式
- 按层拆分:
entity、dao、service、controller、web - 按功能拆分:
user、order、pay等业务模块 - 按工具拆分:
common、utils、config等公共模块
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 模块创建步骤
- 创建父工程(聚合工程),打包方式为
pom - 创建子模块,继承父工程
- 配置模块依赖关系
- 编写模块代码
- 聚合模块,统一构建
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-servicessm-service依赖ssm-daossm-dao依赖ssm-entity- 最终
ssm-web会自动依赖ssm-dao和ssm-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 安装步骤
- 下载 Nexus:https://help.sonatype.com/repomanager3/download
- 解压安装包,进入
bin目录 - 启动 Nexus:
./nexus start(Linux)/nexus.exe /run(Windows) - 访问 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 种配置文件格式:
application.properties:键值对格式,传统格式application.yml:YAML 格式,缩进式,可读性强(推荐)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 内部配置文件细分
内部配置文件按加载顺序优先级:
application-{profile}.yml(环境专属,如application-prod.yml)application.yml(主配置文件)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();
}
}