目录
一、核心思想一:IOC(控制反转,Inversion of Control)
组件 3:依赖注入(DI,Dependency Injection)
依赖注入的 3 种方式(Spring 支持,推荐前 2 种):
步骤 2:解析 Bean 定义(生成 BeanDefinition)
步骤 3:BeanFactoryPostProcessor 增强(扩展点)
步骤 6:BeanPostProcessor 增强(扩展点)
二、核心思想二:AOP(面向切面编程,Aspect-Oriented Programming)
2.AOP 的核心概念(必须熟记,Spring AOP 落地的基础)
3.AOP 的底层实现:动态代理(Spring AOP 的核心技术)
2. 手动模拟动态代理(理解 Spring AOP 底层逻辑)
4.Spring AOP 的核心特性(与原生 AOP 框架的区别)
5.Spring AOP 实战:注解驱动实现切面(核心用法)
6.Spring AOP 切入点表达式(重点:精准匹配目标方法)
2. 次常用:@annotation 表达式(匹配注解标注的方法)
2. 引入增强(Introduction:给目标类添加新方法 / 接口)
Spring 是什么?
Spring 是 Java 生态中最主流的一站式企业级开发框架,核心定位是简化 Java 开发—— 它不是单一功能组件,而是一套覆盖核心容器、AOP、数据访问、Web 开发、事务管理等全链路的技术体系,同时提供了丰富的生态扩展(如 Spring Boot、Spring Cloud),是 Java 后端开发的基石。
简单说:Spring 就像一个「Java 开发工具箱 + 胶水框架」—— 它帮你解决开发中最繁琐的问题(如对象管理、依赖解耦、事务控制),同时能整合其他工具(如 MyBatis、Redis、RabbitMQ),让开发者聚焦核心业务逻辑,而非底层技术细节。

Spring 框架体系结构
Spring 的核心思想:IOC + AOP
一、核心思想一:IOC(控制反转,Inversion of Control)
IoC 的全称为 Inversion of Control,即控制反转,意为把对资源的控制权反转过来。IoC 不是一项开发技术,也不是具体功能,而是面向对象编程中的一种设计理念。
1.什么是IoC?
- 传统开发:开发者手动
new对象(如UserService userService = new UserService();),还要手动管理对象的依赖(如userService.setUserDao(new UserDao());),对象的创建、组装全由开发者控制; - IOC 思想:将对象的创建、依赖组装的控制权,交给 Spring 容器—— 开发者只需定义对象(如用
@Component注解),Spring 会自动创建对象、注入依赖(如用@Autowired注解),无需手动new。
IoC开发(Spring 控制)
// 1. 数据库连接类(交给 Spring 管理,无需手动 new)
@Component
class DBConnection {
public Connection getConn() { ... }
}
// 2. Dao 层(依赖由 Spring 注入,无需手动 new)
@Component
class UserDao {
@Autowired // 依赖注入:Spring 自动创建 DBConnection 并注入
private DBConnection dbConn;
public void query() { ... }
}
// 3. Service 层(依赖由 Spring 注入)
@Component
class UserService {
@Autowired
private UserDao userDao;
public void getUser() { ... }
}
// 4. 应用启动(Spring 容器统一管理)
public class App {
public static void main(String[] args) {
// 启动 Spring 容器:自动扫描、创建、组装所有 Bean
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
// 直接从容器获取对象(无需手动创建)
UserService service = context.getBean(UserService.class);
service.getUser();
}
}
IoC 的 “反转” 本质:
- 反转的 “控制”:指「对象的创建权、依赖组装权、生命周期管理权」从开发者手中,转移到了 Spring IoC 容器手中;
- 反转的核心:开发者从 “主动创建、组装对象” 变成 “被动接收容器创建好的对象”,只需声明 “我需要什么”,容器就会自动满足依赖;
- 通俗类比:传统开发是 “你亲自买菜、做饭、洗碗”(全程掌控);IoC 是 “你告诉餐厅(容器)想吃什么,餐厅做好后给你”(餐厅掌控食材采购、烹饪、餐具清洗)。
2.IoC 的三大核心组件:Bean、容器、依赖注入
IoC 要能工作,必须依赖三个核心组件,它们共同构成了 IoC 的基础体系:
组件 1:Bean(被管理的对象)
什么是 Bean?
Bean 是「被 Spring IoC 容器管理的 Java 对象」—— 它不是普通的 Java 对象(普通对象是 new 出来的),而是由容器创建、组装、销毁的对象。Spring 中的所有业务对象(Service、Dao、Controller)、工具对象(Template)本质上都是 Bean。
Bean 的核心属性(元数据):
Spring 管理 Bean 时,会通过「BeanDefinition」存储 Bean 的元数据(相当于 Bean 的 “身份证 + 说明书”),核心属性包括:
- 全类名(
className):Bean 的具体实现类(如com.example.UserService); - 作用域(
scope):Bean 的创建模式(单例 / 多例等); - 依赖关系(
dependencies):Bean 依赖的其他 Bean(如UserService依赖UserDao); - 生命周期回调(
initMethod/destroyMethod):Bean 初始化 / 销毁时执行的方法; - 懒加载(
lazyInit):是否延迟创建 Bean(默认单例 Bean 启动时创建); - 初始化参数(
properties):Bean 的属性值(如数据库连接池的 url、用户名)。
Spring 提供 6 种 Bean 作用域(常用前 2 种),决定了 Bean 的创建时机和实例数量:

注意:单例 Bean 是 “容器级单例” —— 一个 Spring 容器中只有一个实例,多个容器可以有多个实例;多例 Bean 每次获取都会新建,容器不管理其销毁(由 GC 回收)
注解方式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
@Configuration
public class BeanScopeConfig {
// 默认是Singleton作用域
@Bean
public SingletonService singletonService() {
return new SingletonService();
}
// 显式指定Singleton作用域
@Bean
@Scope("singleton")
public SingletonService explicitSingletonService() {
return new SingletonService();
}
// Prototype作用域
@Bean
@Scope("prototype")
public PrototypeService prototypeService() {
return new PrototypeService();
}
// Request作用域(需要Web环境)
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestScopedService requestScopedService() {
return new RequestScopedService();
}
// Session作用域(需要Web环境)
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public SessionScopedService sessionScopedService() {
return new SessionScopedService();
}
// Application作用域(ServletContext级别)
@Bean
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ApplicationScopedService applicationScopedService() {
return new ApplicationScopedService();
}
}
XML 配置方式
<bean id="myBean" class="com.example.MyBean" scope="prototype"/>
组件 2:IoC 容器(Bean 的 “管家”)
IoC 容器是 Spring 实现 IoC 的核心载体,本质是一个「Bean 工厂 + 依赖注入引擎」,负责 Bean 的全生命周期管理:
- 职责 1:扫描并解析 Bean 定义(从注解、XML 配置中获取 Bean 元数据);
- 职责 2:创建 Bean 实例(根据元数据反射创建对象);
- 职责 3:依赖注入(将 Bean 依赖的其他 Bean 自动组装);
- 职责 4:管理 Bean 生命周期(初始化、使用、销毁);
- 职责 5:提供 Bean 访问接口(
getBean()方法获取 Bean)。
Spring 容器的体系结构(核心接口):
Spring 容器有两个核心接口,体现了 “分层设计”:

ApplicationContext 的常见实现类:
AnnotationConfigApplicationContext:基于注解配置的容器(扫描@Component、@Configuration等注解);ClassPathXmlApplicationContext:基于 XML 配置的容器(加载 classpath 下的 XML 配置文件);FileSystemXmlApplicationContext:基于文件系统 XML 配置的容器(加载指定路径的 XML 文件);AnnotationConfigWebApplicationContext:Web 环境下基于注解的容器(Spring MVC 常用)。
组件 3:依赖注入(DI,Dependency Injection)
什么是 DI?
DI 是 IoC 的「具体实现方式」—— 容器在创建 Bean 时,自动将其依赖的其他 Bean 注入到当前 Bean 中,无需开发者手动 new 或 set。
注意:IoC 是「思想」(控制反转),DI 是「技术」(依赖注入);DI 是实现 IoC 的核心手段,没有 DI 就没有 IoC 的落地。
依赖注入的 3 种方式(Spring 支持,推荐前 2 种):
(1)构造器注入(推荐,Spring 4.3+ 推荐优先使用)
通过 Bean 的构造方法注入依赖,强制要求依赖必须存在(避免空指针),且注入顺序固定。
@Component
public class UserService {
private final UserDao userDao; // 用 final 修饰,确保依赖不可变(推荐)
// 构造器注入:Spring 自动找到 UserDao 实例注入
@Autowired // Spring 4.3+ 单构造器时可省略 @Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
优点:
- 依赖不可变(final 修饰),线程安全;
- 依赖强制注入(无参构造器不存在时,必须传入依赖,避免漏注入);
- 构造器执行时依赖已注入,避免 “部分初始化” 的 Bean。
缺点:
- 依赖过多时,构造器参数会很长(可通过拆分 Bean 解决)。
(2)Setter 方法注入(适合可选依赖)
通过 Bean 的 setXxx() 方法注入依赖,支持动态修改依赖(可选)。
@Component
public class UserService {
private UserDao userDao;
// Setter 注入:Spring 调用 setUserDao 注入依赖
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
优点:
- 支持可选依赖(可在 set 方法中设置默认值);
- 支持动态修改依赖(后续可通过 set 方法替换依赖实例)。
缺点:
- 依赖可被修改(线程不安全);
- 注入时机晚于构造器,可能出现 “Bean 已创建但依赖未注入” 的情况。
(3)字段注入(不推荐,仅适用于简单场景)
直接在字段上用 @Autowired 注入,代码最简洁,但存在隐患。
@Component
public class UserService {
// 字段注入:Spring 通过反射直接给字段赋值(跳过 set 方法)
@Autowired
private UserDao userDao;
}
优点:代码简洁,无多余构造器 /set 方法。
缺点:
- 字段无法用 final 修饰(依赖可变);
- 依赖注入通过反射实现,可读性差,调试困难;
- 无法避免 “空指针”(若依赖未被注入,直接使用会报错);
- 不利于单元测试(无法手动设置依赖,只能用 Spring 容器)。
依赖注入的匹配规则:
Spring 注入依赖时,按以下优先级匹配目标 Bean:
- 按「类型(Type)」匹配:容器中查找与依赖类型一致的 Bean;
- 按「名称(Name)」匹配:若存在多个同类型 Bean,按字段名 / 参数名匹配(可通过
@Qualifier("beanName")指定名称); - 按「@Primary」匹配:若多个同类型 Bean 中存在被
@Primary标注的 Bean,优先注入该 Bean。
// 两个 UserDao 实现类
@Component("userDaoMysql") // 指定 Bean 名称为 userDaoMysql
public class UserDaoMysql implements UserDao { ... }
@Component("userDaoRedis") // 指定 Bean 名称为 userDaoRedis
@Primary // 优先注入
public class UserDaoRedis implements UserDao { ... }
// 注入时指定名称
@Component
public class UserService {
// 按名称注入 userDaoMysql
@Autowired
@Qualifier("userDaoMysql")
private UserDao userDao;
}
3.IoC 容器的完整工作流程(从启动到销毁)

Spring IoC 容器的工作过程是「从配置解析到 Bean 销毁」的全链路,每一步都有明确的职责,我们以 AnnotationConfigApplicationContext(注解驱动)为例,拆解核心步骤:
步骤 1:容器初始化(启动容器)
当执行 new AnnotationConfigApplicationContext("com.example") 时,容器开始初始化:
- 初始化核心组件:
BeanDefinitionReader(解析 Bean 定义)、BeanDefinitionScanner(扫描 Bean)、BeanFactory(Bean 工厂); - 扫描指定包(
com.example)下的所有类,识别带有@Component、@Service、@Dao、@Controller等注解的类,将其标记为 “待管理的 Bean”。
步骤 2:解析 Bean 定义(生成 BeanDefinition)
容器对扫描到的类进行解析,生成 BeanDefinition 对象(存储 Bean 元数据):
- 解析类名、包名,确定 Bean 的全类名;
- 解析注解中的属性(如
@Scope指定作用域、@Autowired指定依赖); - 解析类的构造方法、set 方法,确定依赖关系;
- 将所有
BeanDefinition存储到BeanDefinitionRegistry(Bean 定义注册表)中,供后续创建 Bean 使用。
步骤 3:BeanFactoryPostProcessor 增强(扩展点)
容器启动时,会先执行所有 BeanFactoryPostProcessor 接口的实现类(Spring 提供的扩展点),对 BeanDefinition 进行修改:
- 例如:
PropertySourcesPlaceholderConfigurer替换配置文件中的占位符(如${db.url}替换为实际配置值); - 开发者可自定义
BeanFactoryPostProcessor,动态修改 Bean 的元数据(如修改 Bean 的作用域、添加依赖)。
步骤 4:创建 Bean 实例(实例化)
容器根据 BeanDefinition 创建 Bean 实例,默认通过「反射」实现:
- 单例 Bean:容器启动时(步骤 4)创建(除非设置
lazyInit=true,使用时创建); - 多例 Bean:每次调用
getBean()时创建; - 创建方式:
- 无参构造器:默认方式(需确保类有默认无参构造器);
- 有参构造器:构造器注入时,根据依赖找到对应的 Bean,传入构造器创建实例;
- 静态工厂方法:通过
@Bean注解指定静态方法创建(如@Bean public static UserDao userDao() { return new UserDao(); }); - 实例工厂方法:通过实例方法创建(如
@Bean public UserDao userDao(DBConnection dbConn) { return new UserDao(dbConn); })。
步骤 5:依赖注入(装配)
Bean 实例创建后,容器会执行依赖注入:
- 构造器注入:创建实例时已完成(步骤 4 中传入依赖);
- Setter 注入 / 字段注入:通过反射调用 set 方法或直接给字段赋值,将依赖的 Bean 注入到当前 Bean 中;
- 注入时会递归创建依赖的 Bean:若
UserService依赖UserDao,UserDao依赖DBConnection,容器会先创建DBConnection,再创建UserDao,最后创建UserService。
步骤 6:BeanPostProcessor 增强(扩展点)
依赖注入完成后,容器会执行所有 BeanPostProcessor 接口的实现类,对 Bean 进行增强:
- 前置处理(
postProcessBeforeInitialization):在 Bean 初始化前执行(如给 Bean 动态添加属性、AOP 代理生成); - 后置处理(
postProcessAfterInitialization):在 Bean 初始化后执行(如 Spring AOP 就是通过该方法生成代理对象); - 示例:
AutowiredAnnotationBeanPostProcessor就是 Spring 内置的BeanPostProcessor,负责解析@Autowired注解,完成依赖注入。
步骤 7:Bean 初始化(初始化回调)
Bean 增强后,容器会执行 Bean 的初始化方法(按以下顺序):
- 执行
@PostConstruct注解标注的方法(JSR-250 标准注解); - 执行 Bean 定义中指定的
initMethod(如 XML 中的init-method或@Bean(initMethod="init")); - 执行
InitializingBean接口的afterPropertiesSet()方法(Spring 内置接口)。
初始化方法用于 Bean 的初始化逻辑(如初始化连接池、加载配置文件)。
步骤 8:Bean 就绪(可供使用)
初始化完成后,Bean 被存储到容器的缓存中(单例 Bean 存储在 singletonObjects 缓存),此时可以通过 context.getBean() 获取 Bean 并使用。
步骤 9:Bean 销毁(容器关闭时)
当容器调用 close() 方法(或应用停止)时,容器会销毁 Bean(仅单例 Bean,多例 Bean 由 GC 回收):
- 执行
@PreDestroy注解标注的方法(JSR-250 标准注解); - 执行 Bean 定义中指定的
destroyMethod(如 XML 中的destroy-method或@Bean(destroyMethod="destroy")); - 执行
DisposableBean接口的destroy()方法(Spring 内置接口)。
销毁方法用于释放资源(如关闭连接池、释放文件句柄)。
总结流程:容器启动 → 扫描 Bean → 解析 Bean 定义 → 增强 Bean 定义 → 创建 Bean 实例 → 依赖注入 → 增强 Bean → 初始化 → 就绪 → 容器关闭 → 销毁。
4.IoC 底层实现技术揭秘
Spring IoC 容器的底层依赖 3 个核心技术,正是这些技术让 “控制反转” 成为可能:
1. 反射(Reflection)
反射是 Java 提供的动态访问类成员(构造器、方法、字段)的能力,Spring 通过反射实现:
- Bean 实例化:通过
Class.newInstance()或Constructor.newInstance()创建 Bean 实例; - 依赖注入:通过
Field.set()给字段赋值,或Method.invoke()调用 set 方法; - 生命周期回调:通过
Method.invoke()执行@PostConstruct、initMethod等方法。
示例(反射创建 Bean 并注入依赖):
// 模拟 Spring 反射创建 Bean
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 1. 获取 UserService 类对象
Class<UserService> userServiceClass = UserService.class;
// 2. 获取 UserDao 类对象
Class<UserDao> userDaoClass = UserDao.class;
// 3. 反射创建 UserDao 实例
UserDao userDao = userDaoClass.getConstructor().newInstance();
// 4. 反射创建 UserService 实例(通过构造器注入 UserDao)
Constructor<UserService> constructor = userServiceClass.getConstructor(UserDao.class);
UserService userService = constructor.newInstance(userDao);
// 5. 调用方法
userService.getUser();
}
}
2. 工厂模式(Factory Pattern)
Spring IoC 容器本质是一个「超级工厂」,通过 BeanFactory 接口封装了 Bean 的创建逻辑:
- 工厂接口:
BeanFactory定义了 Bean 创建的标准(getBean()方法); - 工厂实现:
DefaultListableBeanFactory是核心实现类,负责 Bean 定义的注册、实例化、依赖注入; - 工厂模式的优势:将 Bean 的创建逻辑与使用逻辑分离,开发者无需关心 Bean 如何创建,只需通过工厂获取。
3. 注册中心模式(Registry Pattern)
Spring 用 BeanDefinitionRegistry 存储所有 Bean 的元数据(BeanDefinition),相当于一个 “Bean 注册中心”:
- 注册:容器扫描 Bean 时,将解析后的
BeanDefinition注册到BeanDefinitionRegistry; - 查找:创建 Bean 或注入依赖时,从注册中心查找对应的
BeanDefinition; - 优势:集中管理 Bean 元数据,支持动态添加、修改、删除 Bean 定义,扩展性强。
5.IoC 的高级特性与实际应用
1. 循环依赖的解决(Spring 的核心能力)
什么是循环依赖?
两个或多个 Bean 互相依赖,形成闭环(如 A→B→A 或 A→B→C→A)。
示例(循环依赖):
@Component
public class A {
@Autowired
private B b; // A 依赖 B
}
@Component
public class B {
@Autowired
private A a; // B 依赖 A
}
传统开发的问题:
new A() 时需要 B,new B() 时需要 A,导致无限循环,无法创建实例。
Spring 如何解决循环依赖?
Spring 仅支持「单例 Bean + 非构造器注入」的循环依赖,核心是「三级缓存」机制:
- 一级缓存(
singletonObjects):存储完全初始化完成的单例 Bean(可供使用); - 二级缓存(
earlySingletonObjects):存储提前暴露的单例 Bean 实例(已实例化,但未完成依赖注入和初始化); - 三级缓存(
singletonFactories):存储 Bean 的工厂对象(用于生成提前暴露的实例)。
解决流程(以 A→B→A 为例):
- 容器创建 A:实例化 A(未注入依赖),将 A 的工厂对象存入三级缓存;
- A 需要注入 B:容器开始创建 B;
- 容器创建 B:实例化 B(未注入依赖),将 B 的工厂对象存入三级缓存;
- B 需要注入 A:容器从三级缓存中获取 A 的工厂对象,生成 A 的提前暴露实例(未初始化),存入二级缓存,删除三级缓存中的 A;
- B 注入 A 后,完成依赖注入和初始化,存入一级缓存(B 就绪);
- A 注入 B 后,完成依赖注入和初始化,存入一级缓存(A 就绪)。
关键:提前暴露未完全初始化的 Bean 实例,打破循环依赖的死锁。注意:构造器注入的循环依赖无法解决(会抛出
BeanCurrentlyInCreationException),因为构造器执行时 Bean 还未实例化,无法提前暴露。
2. 延迟加载(Lazy Initialization)
默认情况下,单例 Bean 会在容器启动时创建(饿汉式),延迟加载可让 Bean 在「第一次使用时」创建(懒汉式)。
开启方式:
- 单个 Bean 延迟加载:在 Bean 类上添加
@Lazy注解; - 全局延迟加载:在配置类上添加
@Lazy注解(所有单例 Bean 都延迟加载)。
@Component
@Lazy // 延迟加载:容器启动时不创建,第一次 getBean() 时创建
public class UserService { ... }
适用场景:
- Bean 初始化成本高(如连接远程服务);
- Bean 不常用(如后台定时任务);
- 减少容器启动时间(避免启动时创建大量 Bean)。
3. 条件注解(@Conditional)
根据特定条件决定是否创建 Bean,实现 “按需创建”。
示例:根据系统环境创建不同的 DBConnection:
// 自定义条件:Windows 系统
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
// 自定义条件:Linux 系统
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
// 按条件创建 Bean
@Component
@Conditional(WindowsCondition.class) // Windows 系统时创建
public class WindowsDBConnection implements DBConnection { ... }
@Component
@Conditional(LinuxCondition.class) // Linux 系统时创建
public class LinuxDBConnection implements DBConnection { ... }
常用内置条件注解:
@ConditionalOnClass:类路径下存在指定类时创建;@ConditionalOnMissingClass:类路径下不存在指定类时创建;@ConditionalOnBean:容器中存在指定 Bean 时创建;@ConditionalOnMissingBean:容器中不存在指定 Bean 时创建;@ConditionalOnProperty:配置文件中存在指定属性时创建。
二、核心思想二:AOP(面向切面编程,Aspect-Oriented Programming)
1.AOP 的核心思想是什么?
AOP 的本质是「在不修改原有业务代码的前提下,对方法进行统一增强」—— 把日志记录、事务控制、权限校验等「通用功能」(横切逻辑)抽离成独立的「切面」,动态植入到目标方法的执行流程中(如方法执行前、执行后、异常时)。
为什么需要AOP?(OOP 的痛点)
OOP(面向对象编程)是「垂直分层」思想(如 Service、Dao、Controller 层),但面对「横切逻辑」(横跨多个层的通用功能)时会陷入困境:
- 代码冗余:日志、事务等代码需要在每个业务方法中重复编写(如每个 Service 方法都要写
try-catch控制事务); - 耦合严重:通用功能与业务逻辑紧耦合(如想关闭日志,需要修改所有业务方法);
- 维护成本高:修改通用功能时(如日志格式变更),需要修改所有关联的业务代码。
AOP 的核心解决思路:「分离关注点」
AOP 将软件系统的功能分为两类,实现分离管理:
- 核心关注点:业务逻辑(如下单、查询用户、支付),是开发者聚焦的核心;
- 横切关注点:通用功能(如日志、事务、权限、异常处理),横跨多个核心关注点。
AOP 的目标是:让核心关注点和横切关注点完全分离,横切逻辑统一维护在切面中,无需侵入业务代码。
2.AOP 的核心概念(必须熟记,Spring AOP 落地的基础)
AOP 的所有操作都围绕以下 6 个核心概念展开,结合示例理解更清晰:
假设场景:给 OrderService 的 placeOrder()(下单)和 cancelOrder()(取消订单)方法添加「日志记录」和「事务控制」功能。

关键关联:
- 连接点是「候选者」(所有可能植入的时机),切入点是「筛选后的目标」(实际植入的时机);
- 切面是「规则 + 逻辑」的组合,织入是「执行缝合」的过程,代理对象是「缝合后的产物」;
- 开发者只需定义「切面」(切入点 + 通知),Spring 自动完成「织入」并生成「代理对象」。
3.AOP 的底层实现:动态代理(Spring AOP 的核心技术)
AOP 的织入过程依赖「动态代理」技术 —— 在程序运行时动态生成代理对象,代理对象包装目标对象,并重写目标方法,将横切逻辑植入其中。
Spring AOP 支持两种动态代理方式,自动选择:
- JDK 动态代理:基于 Java 原生
java.lang.reflect.Proxy类,要求目标对象必须实现接口; - CGLIB 动态代理:基于第三方库 CGLIB(Code Generation Library),通过生成目标对象的子类实现代理,无需目标对象实现接口。
1. 两种代理方式的对比(重点)

2. 手动模拟动态代理(理解 Spring AOP 底层逻辑)
通过手动实现 JDK 动态代理,感受 AOP 的核心原理:
步骤 1:定义目标接口和目标对象(业务逻辑)
// 目标接口
public interface OrderService {
void placeOrder(String orderId); // 下单方法(核心业务)
}
// 目标对象(实现接口)
public class OrderServiceImpl implements OrderService {
@Override
public void placeOrder(String orderId) {
System.out.println("核心业务:执行下单逻辑,订单ID=" + orderId);
}
}
步骤 2:定义横切逻辑(日志增强)
// 横切逻辑:日志记录
public class LogAdvice {
// 方法执行前增强
public void before(String orderId) {
System.out.println("日志增强:下单方法开始执行,订单ID=" + orderId);
}
// 方法执行后增强
public void after(String orderId) {
System.out.println("日志增强:下单方法执行结束,订单ID=" + orderId);
}
}
步骤 3:通过 JDK 动态代理生成代理对象,织入横切逻辑
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 代理处理器:负责织入横切逻辑
public class OrderServiceProxyHandler implements InvocationHandler {
private final Object target; // 目标对象(OrderServiceImpl)
private final LogAdvice logAdvice; // 横切逻辑(日志)
// 构造器注入目标对象和横切逻辑
public OrderServiceProxyHandler(Object target, LogAdvice logAdvice) {
this.target = target;
this.logAdvice = logAdvice;
}
// 核心方法:代理对象的所有方法调用都会触发此方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 执行前置增强(日志记录)
logAdvice.before((String) args[0]);
// 2. 执行目标对象的核心业务方法
Object result = method.invoke(target, args);
// 3. 执行后置增强(日志记录)
logAdvice.after((String) args[0]);
return result;
}
// 生成代理对象的工厂方法
public static OrderService createProxy(OrderService target, LogAdvice logAdvice) {
return (OrderService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口
new OrderServiceProxyHandler(target, logAdvice) // 代理处理器
);
}
}
步骤 4:测试代理对象(触发增强逻辑)
public class ProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象和横切逻辑对象
OrderService target = new OrderServiceImpl();
LogAdvice logAdvice = new LogAdvice();
// 2. 生成代理对象
OrderService proxy = OrderServiceProxyHandler.createProxy(target, logAdvice);
// 3. 调用代理对象的方法(触发增强)
proxy.placeOrder("ORDER_123456");
}
}
输出结果(横切逻辑自动植入,无侵入业务代码):
日志增强:下单方法开始执行,订单ID=ORDER_123456
核心业务:执行下单逻辑,订单ID=ORDER_123456
日志增强:下单方法执行结束,订单ID=ORDER_123456
4.Spring AOP 的核心特性(与原生 AOP 框架的区别)
Spring AOP 是「基于动态代理的 AOP 框架」,并非完整的 AOP 实现(完整 AOP 如 AspectJ),但贴合 Spring 生态,足够满足绝大多数业务场景。其核心特性:
- 基于代理:仅支持「方法级别的增强」(连接点是方法执行前后、异常时),不支持字段、构造器级别的增强(如需更细粒度增强,需结合 AspectJ);
- 动态织入:织入过程在程序运行时完成(通过动态代理生成代理对象),无需编译期或类加载期修改字节码;
- 与 IoC 深度集成:切面、目标对象都是 Spring 管理的 Bean,依赖注入自动完成(如切面中可通过
@Autowired注入其他 Bean); - 注解驱动:通过
@Aspect、@Pointcut、@Before等注解快速定义切面,无需复杂 XML 配置。
5.Spring AOP 实战:注解驱动实现切面(核心用法)
Spring AOP 最常用的方式是「注解驱动」,步骤简单:定义切面 → 配置切入点 → 编写通知 → 启动容器。
1. 环境准备(Maven 依赖)
Spring AOP 依赖 spring-context 和 spring-aspects(AOP 注解支持):
<dependencies>
<!-- Spring 核心容器 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<!-- Spring AOP 注解支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.20</version>
</dependency>
</dependencies>
步骤 1:定义目标对象(业务 Bean)
import org.springframework.stereotype.Service;
// 目标对象:订单服务(核心业务)
@Service // 交给 Spring 管理为 Bean
public class OrderService {
// 核心业务方法1:下单
public void placeOrder(String orderId) {
System.out.println("核心业务:下单成功,订单ID=" + orderId);
// 模拟异常(测试异常通知)
// if (orderId.length() < 10) throw new RuntimeException("订单ID格式错误");
}
// 核心业务方法2:取消订单
public String cancelOrder(String orderId) {
System.out.println("核心业务:取消订单,订单ID=" + orderId);
return "取消成功";
}
}
步骤 2:定义切面(@Aspect + 通知注解)
Spring 提供 5 种通知类型(覆盖方法执行的全流程),结合切入点表达式使用:
5 种通知类型详解:

切面类完整实现(包含 5 种通知):
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 切面类 = @Aspect(标识切面) + @Component(交给 Spring 管理)
@Aspect
@Component
public class OrderAspect {
// ################### 步骤1:定义切入点(Pointcut) ###################
// @Pointcut:定义切入点规则,value 是切入点表达式
// 规则:匹配 com.example.service 包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void orderPointcut() {} // 切入点签名(无实际逻辑,仅作为规则引用)
// ################### 步骤2:编写通知(Advice) ###################
// 1. 前置通知:目标方法执行前执行
@Before("orderPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
// JoinPoint:连接点对象,可获取目标方法信息(方法名、参数)
String methodName = joinPoint.getSignature().getName(); // 获取方法名
Object[] args = joinPoint.getArgs(); // 获取方法参数
System.out.println("[前置通知] 方法" + methodName + "开始执行,参数:" + args[0]);
}
// 2. 后置返回通知:目标方法正常执行完成后执行(可获取返回值)
@AfterReturning(value = "orderPointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[后置返回通知] 方法" + methodName + "正常执行,返回值:" + result);
}
// 3. 后置异常通知:目标方法抛出异常后执行(可获取异常)
@AfterThrowing(value = "orderPointcut()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[后置异常通知] 方法" + methodName + "执行异常,异常信息:" + ex.getMessage());
}
// 4. 最终通知:目标方法执行结束后执行(无论正常/异常)
@After("orderPointcut()")
public void afterAdvice(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[最终通知] 方法" + methodName + "执行结束,释放资源");
}
// 5. 环绕通知:环绕目标方法执行(功能最强,需手动调用 proceed() 执行目标方法)
@Around("orderPointcut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String methodName = proceedingJoinPoint.getSignature().getName();
Object[] args = proceedingJoinPoint.getArgs();
// ① 前置增强:目标方法执行前
System.out.println("[环绕通知-前] 方法" + methodName + "准备执行,参数:" + args[0]);
Object result = null;
try {
// ② 执行目标方法(必须调用 proceed(),否则目标方法不会执行)
result = proceedingJoinPoint.proceed(); // 执行目标方法,获取返回值
// 可修改返回值(如对返回值进行包装)
if (result != null) {
result = "【环绕通知包装】" + result;
}
} catch (Exception e) {
// ③ 异常增强:目标方法抛出异常时
System.out.println("[环绕通知-异常] 方法" + methodName + "执行异常:" + e.getMessage());
throw e; // 可选择抛出异常或处理异常
}
// ④ 后置增强:目标方法执行后
System.out.println("[环绕通知-后] 方法" + methodName + "执行完成,包装后返回值:" + result);
return result; // 返回修改后的结果
}
}
步骤 3:启动 Spring 容器,测试增强效果
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// 配置类:开启组件扫描和 AOP 自动代理
@Configuration
@ComponentScan("com.example") // 扫描 com.example 包下的 Bean
@EnableAspectJAutoProxy // 开启 Spring AOP 自动代理(必须添加,否则 AOP 不生效)
public class AppConfig {
public static void main(String[] args) {
// 启动 Spring 容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取目标对象的代理对象(Spring 自动生成代理,注入的是代理对象)
OrderService orderService = context.getBean(OrderService.class);
// 调用代理对象的方法(触发 AOP 增强)
System.out.println("=== 测试下单方法 ===");
orderService.placeOrder("ORDER_1234567890");
System.out.println("\n=== 测试取消订单方法 ===");
String cancelResult = orderService.cancelOrder("ORDER_9876543210");
System.out.println("最终返回结果:" + cancelResult);
// 关闭容器
context.close();
}
}
6.Spring AOP 切入点表达式(重点:精准匹配目标方法)
切入点表达式是 @Pointcut 的核心,用于精准匹配需要增强的目标方法。Spring AOP 支持多种切入点表达式,最常用的是 execution 和 @annotation。
1. 最常用:execution 表达式(匹配方法签名)
语法格式(6 部分组成):
execution( [访问修饰符] 返回值类型 包名.类名.方法名(参数类型) [throws 异常类型] )
- 可省略部分:访问修饰符(如 public)、throws 异常类型;
- 通配符:
*:匹配任意字符(如*表示任意返回值类型、任意类、任意方法);..:匹配任意层级的包或任意个数的参数(如com.example..*表示 com.example 下所有子包的类)。
常用 execution 表达式示例:

2. 次常用:@annotation 表达式(匹配注解标注的方法)
于匹配被指定注解标注的方法,适合「按注解增强」场景(如事务注解 @Transactional)。
示例:
- 自定义注解
@Log(标记需要添加日志的方法import java.lang.annotation.*; @Target(ElementType.METHOD) // 仅作用于方法 @Retention(RetentionPolicy.RUNTIME) // 运行时生效 public @interface Log { String value() default ""; // 注解属性(可传入日志描述) } - 切入点表达式匹配
@Log注解:@Aspect @Component public class LogAspect { // 切入点:匹配所有被 @Log 注解标注的方法 @Pointcut("@annotation(com.example.annotation.Log)") public void logPointcut() {} // 前置通知:获取注解属性 @Before("logPointcut() && @annotation(log)") // 通过 @annotation(log) 注入注解实例 public void beforeLog(JoinPoint joinPoint, Log log) { String methodName = joinPoint.getSignature().getName(); String logDesc = log.value(); // 获取注解的属性值 System.out.println("[日志通知] 方法" + methodName + ",描述:" + logDesc); } } - 目标方法添加
@Log注解(触发增强):@Service public class OrderService { @Log("用户下单操作") // 添加注解,触发日志增强 public void placeOrder(String orderId) { System.out.println("下单成功,订单ID=" + orderId); } }
7.Spring AOP 的高级特性(实际开发高频使用)
1. 切面优先级控制(@Order 注解)
当多个切面增强同一个目标方法时,需要控制切面的执行顺序:
@Order(value = 数字):数字越小,切面优先级越高(先执行);- 默认优先级:数字越大,优先级越低(未标注
@Order的切面优先级最低)。
// 日志切面(优先级 1,先执行)
@Aspect
@Component
@Order(1)
public class LogAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("日志切面:前置增强");
}
}
// 事务切面(优先级 2,后执行)
@Aspect
@Component
@Order(2)
public class TransactionAspect {
@Before("execution(* com.example.service.*.*(..))")
public void transactionBefore() {
System.out.println("事务切面:前置增强");
}
}
2. 引入增强(Introduction:给目标类添加新方法 / 接口)
引入增强是 Spring AOP 的高级功能,允许给目标类「动态添加新的接口和方法实现」,无需修改目标类代码。
示例:给 OrderService 添加 Loggable 接口的实现(动态添加日志方法):
- 定义新接口
Loggable:public interface Loggable { void log(String message); // 新方法:记录日志 } - 切面中通过
@DeclareParents引入接口实现:@Aspect @Component public class IntroductionAspect { // @DeclareParents:给目标类添加新接口 // value:目标类(OrderService),defaultImpl:接口的默认实现类 @DeclareParents(value = "com.example.service.OrderService", defaultImpl = LoggableImpl.class) public static Loggable loggable; // 要添加的接口 } // 接口实现类 public class LoggableImpl implements Loggable { @Override public void log(String message) { System.out.println("引入增强:" + message); } }
Spring 框架基础搭建
环境准备
- JDK 8+(推荐JDK 11或17)
- Maven 3.6+或Gradle 7.x
- IDE(IntelliJ IDEA/Eclipse)
创建项目 通过Spring Initializr生成项目模板:
curl https://start.spring.io/starter.zip -d dependencies=web \
-d type=maven-project -d javaVersion=17 -o demo.zip
或使用IDE内置的Spring Initializr向导选择Web、JDBC等模块。
核心配置文件
application.properties或application.yml:
server.port=8080
spring.datasource.url=jdbc:h2:mem:testdb
spring.h2.console.enabled=true
Spring MVC 核心流程
请求处理链路
- DispatcherServlet接收HTTP请求
- HandlerMapping解析请求URL到Controller方法
- 参数解析器处理@RequestParam/@RequestBody等注解
- 方法执行后通过ViewResolver或HttpMessageConverter返回响应
示例控制器
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return "Hello " + name;
}
}
IoC 容器实现原理
Bean生命周期
- 实例化 → 属性填充 → 初始化前(@PostConstruct) → 初始化(InitializingBean) → 初始化后(AOP代理) → 销毁
配置方式对比
// 注解配置
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
// Java显式配置
@Configuration
public class AppConfig {
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
AOP 底层机制
代理模式选择
- JDK动态代理:基于接口(默认)
- CGLIB:代理类(需添加依赖)
切面定义示例
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example..*(..))")
public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
System.out.println("Method executed in " + (System.currentTimeMillis()-start) + "ms");
return result;
}
}
事务管理实现
传播行为配置
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Account from, Account to, BigDecimal amount) {
// 业务逻辑
}
底层实现
- PlatformTransactionManager抽象
- 事务同步管理器(TransactionSynchronizationManager)
- 基于AOP的拦截器(TransactionInterceptor)
Spring Boot 自动配置
条件化Bean加载
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
// 自动配置逻辑
}
}
自定义Starter步骤
- 创建autoconfigure模块
- 编写
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports - 定义配置属性类(@ConfigurationProperties)
响应式编程支持
WebFlux核心组件
- RouterFunction方式定义路由:
@Bean
public RouterFunction<ServerResponse> routes() {
return route(GET("/hello"),
req -> ok().bodyValue("Hello WebFlux"));
}
Reactive仓库示例
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByAgeGreaterThan(int age);
}
源码调试技巧
关键断点位置
- AbstractApplicationContext.refresh()
- DefaultListableBeanFactory.preInstantiateSingletons()
- AbstractAutowireCapableBeanFactory.createBean()
- ProxyFactory.getProxy()
日志配置
logging.level.org.springframework=DEBUG
logging.level.org.hibernate.SQL=TRACE
1633

被折叠的 条评论
为什么被折叠?



