学习笔记 - Spring

什么是Spring?

Spring 是于 2003 年兴起的一个开源的轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的, 主要目的是简化java开发。

Spring 的核心思想是控制反转(IOC)和面向切面编程(AOP), 实现对象的解耦与增强, 从而构建松耦合, 已维护, 可拓展的系统

Spring体系结构

image-20250720174620465

  • Spring Core: 包含IOC容器的基础功能, 使整个框架的核心
    • 核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开
    • BeanFactory 与 ApplicationContext 区别
    • Bean 的生命周期(初始化、销毁、自定义扩展点)
    • Bean 作用域(singleton、prototype、request、session)
    • 依赖注入方式:构造器、Setter、字段注入、@Value
    • 自动装配(@Autowired、@Qualifier、@Primary、@Resource)
  • Spring AOP: 提供AOP支持, 允许在运行时将切面织入业务逻辑中
    • 通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中
    • AOP 通知类型(前置、后置、返回、异常、环绕)
    • 切入点表达式(execution、@annotation 等)
    • AspectJ 与 Spring AOP 区别
    • 动态代理(JDK vs CGLIB)
    • AOP 实现原理(ProxyFactoryBean、Advisor、Interceptor)
  • Spring Beans: 管理bean的创建, 配置, 生命周期等
    • BeanPostProcessor / InstantiationAwareBeanPostProcessor
    • BeanFactoryPostProcessor / BeanDefinitionRegistryPostProcessor
    • ApplicationListener / ApplicationEvent
    • InitializingBean / DisposableBean / SmartInitializingSingleton
  • Spring Context: 更高级的IOC容器, 支持国际化, 事件传播, 资源加载等
    • Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能
  • Spring Expression Language(SpEL): 动态计算表达式的语言, 常用语配置文件和注解中
    • 字符串拼接、数学运算、集合操作、条件判断
    • 与注解结合使用(如 @Value("#{config.port + 1}")
  • Spring JDBC/ORM: 简化数据库访问, 整合Mybatis, Hibernate, JPA等框架
    • JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构
    • Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构
    • JdbcTemplate 使用
    • 编程式事务 vs 声明式事务
    • PlatformTransactionManager 实现类
    • @Transactional 注解原理与传播行为(Propagation)、隔离级别(Isolation)、回滚机制
    • MyBatis 整合(SqlSessionFactory、MapperScannerConfigurer)
  • Spring Transaction: 支持声明式事务管理, 兼容JDBC, JTA等事务机制
  • Spring Web: 支持构建Web应用, 包括Servlet API封装, 请求映射等
    • Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作
    • JSR-303 参数校验(@Valid、@Validated)
    • 自定义 Converter、Formatter
    • 全局参数处理(WebDataBinder)
  • Spring MVC: 基于请求-响应模型的Web MVC框架, 核心是DispatcherServlet
    • MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
    • DispatcherServlet 工作流程
    • HandlerMapping、HandlerAdapter、ViewResolver 等组件
    • @Controller、@RestController、@RequestMapping、@ResponseBody
    • 请求参数绑定(@RequestParam、@PathVariable、@RequestBody、@ModelAttribute)
    • 返回值处理机制(ResponseBodyAdvice、HttpMessageConverter)
    • 拦截器(HandlerInterceptor)与过滤器(Filter)区别
    • 异常处理(@ControllerAdvice、@ExceptionHandler)
  • Spring Boot: 基于Spring的快速开发脚手架, 简化配置和部署
    • Spring Boot 启动原理(SpringApplication)
    • @SpringBootApplication、@EnableAutoConfiguration 原理
    • META-INF/spring.factories
    • 条件注解(@ConditionalOnMissingBean、@ConditionalOnClass 等)
    • application.yml / application.properties
    • @ConfigurationProperties 与 @Value 区别
    • 多环境配置(@Profile)
    • 自定义 Starter 开发

IOC控制反转

IoC(Inversion of Control, 控制反转)是一种对象创建和依赖管理的思想, 它将对象之间的控制权从程序代码中"反转"给容器, 由容器负责管理对象的创建, 生命周期和依赖注入

简单的来说: 原来是你自己new, 现在容器帮你new

容器

容器指的是Spring提供的IoC容器, 也叫Spring容器, 本质上是ApplicationContext的实现类, 用来代替程序员手动new对象, 管理依赖关系和对象的生命周期

IoC, AOP都可以看做是一个容器技术, 都是用来管理操作bean的

容器的核心接口

  • BeanFactory: 最基本的IoC容器, 只提供最小功能(延迟加载)
  • ApplicationContext: 更高级的容器, 提供了BeanFactory的所有功能, 并支持国际化, 事件发布, AOP, 资源加载等

容器常见的实现类

  • ClassPathXmlApplicationContext: 读取 XML 配置文件
  • AnnotationConfigApplicationContext: 读取 Java 配置类(最常用)
  • WebApplicationContext: Spring MVC 中的 Web 容器

Spring 容器初始化过程

  • new ApplicationContext(); 这个方法是创建Spring容器

  • refresh(); 这个方法是加载IoC容器的过程, 也就是真正创建bean的过程

    refresh() 中核心流程:
    
    1. prepareRefresh() // 容器准备工作(设置启动时间、初始化属性源等)
    2. obtainFreshBeanFactory() // 获取 BeanFactory(如 DefaultListableBeanFactory)
    3. prepareBeanFactory(beanFactory) // 配置 BeanFactory 的标准类加载器、注册后处理器等
    4. postProcessBeanFactory(beanFactory) // 子类重写扩展点(空实现)
    5. invokeBeanFactoryPostProcessors(beanFactory) // 执行 BeanFactoryPostProcessor
    6. registerBeanPostProcessors(beanFactory) // 注册 BeanPostProcessor
    7. initMessageSource() // 国际化
    8. initApplicationEventMulticaster() // 初始化事件广播器
    9. onRefresh() // 留给子类扩展,如 Spring Boot 会重写这里
    10. registerListeners() // 注册事件监听器
    11. finishBeanFactoryInitialization(beanFactory) // 实例化剩余的非懒加载 bean
    12. finishRefresh() // 清理、发布事件等
    

具体流程

  1. 配置Bean

    • xml配置文件:
    • JavaConfig: @Bean
    • 此时这个Bean只是概念形态的bean, 只是配置了bean(可以看做是一台概念车)
  2. 读取配置, 也就是new ApplicationContext() 的时候

    • 将读取到的配置信息都存入beanDefinitionMap中, 此时Bean为定义态(可以看做是车的图纸)

      BeanDefinition 是 Spring 中对 bean 的“蓝图”抽象,包含类名、构造器参数、作用域、是否懒加载等元信息。BeanDefinition 并不是具体的 Bean,而是 Bean 的描述对象

    • 读取的配置信息都有: Class, 是否为懒加载lazy-init, 作用域scope, 初始化方法init-method

    • 读取流程

      • 不同的配置方式用来读取的类肯定也不一样
        • xml: ClassPathXmlApplicationContext
        • JavaConfig: AnnotationConfigApplicationContext
      • 公共处理流程
        1. 读取xml或者JavaConfig配置类: BeanDefinitionReader(实现类: AbstractBeanDefinitionReader, XmlBeanDefinitionReader)
        2. 解析配置类中不同的Java注解或者配置文件中不同的元素标签: ConfigurationClassParser, DefaultBeanDefinitionDocumentReader
        3. 然后通过BeanDefinitionRegistry注册成为Bean
  3. BeanFactory通过getBean()生产bean

    • 首先会判断是否已经生成了bean的实例(实例化)

      • 已经生成会直接返回bean的实例, 此时getBean()是获取bean

      • 没有生成, 会对bean进行实例化, 此时getBean()是生产bean

      • 实例化的核心机制就是利用反射

      • 此时会把bean放入到二级缓存中, 此时bean的属性还没有复制

        Bean 实例化过程细化:
        
        1. 判断是否已存在于单例缓存(三级缓存机制:singletonObjects,earlySingletonObjects,singletonFactories)
        2. 创建实例(构造器或工厂方法) → 反射生成裸对象
        3. 暂存早期对象到二级缓存(earlySingletonObjects)
        4. 属性注入(依赖注入,填充字段、调用 set 方法)
        5. 执行 Aware 接口(如 BeanNameAware、ApplicationContextAware)
        6. 执行 BeanPostProcessor 的 postProcessBeforeInitialization()
        7. 调用初始化方法(@PostConstruct、InitializingBean.afterPropertiesSet、init-method)
        8. 执行 BeanPostProcessor 的 postProcessAfterInitialization()
        9. 放入一级缓存(singletonObjects)
        
    • 属性赋值(DI), 解析自动注入

    • 初始化, 会调用生命周期的初始化回调方法(可以在这一步进行一些初始化操作: @PostConstruct)

    • 创建bean之后, 会把bean放到一个static Map<beanName, bean>中, 这个map也就是一级缓存:单例 缓存池

  4. 创建bean的每一步都会调用一些处理器, 在bean创建的每一步的前后进行处理

    1. AOP是在初始化之后的一个后置处理器中进行创建的: BeanPostProcess

      Spring AOP 是通过一个特殊的 BeanPostProcessor —— AnnotationAwareAspectJAutoProxyCreator 实现的,
      它会在 postProcessAfterInitialization() 中为目标类创建代理对象(JDK 或 CGLIB),
      从而实现方法拦截。

    Bean后置处理器:

    后置处理器接口作用时机说明
    BeanFactoryPostProcessorrefresh() 早期,Bean 实例化前修改 BeanDefinition(如注入配置属性)
    BeanPostProcessorBean 实例化后,初始化前后如 AOP、@Autowired、@Resource、@PostConstruct 都依赖它
    InstantiationAwareBeanPostProcessor实例化前(最早拦截)可用来自定义依赖注入逻辑,比如 Spring AOP 的核心接口之一
    DestructionAwareBeanPostProcessor销毁前可以自定义销毁逻辑
    SmartInitializingSingleton所有单例 bean 创建完成后调用执行容器就绪之后的动作

依赖注入

依赖注入(Dependency injection, DI), DI是IoC的具体实现方式

  • Dependency: 表示一个对象所依赖的其他对象
  • injection: 表示这些依赖不是对象自己创建的, 而是由外部"注入"进去的

也就是说: DI是指由外部容器将依赖对象动态的注入到当前对象中, 而不是当前对象主动创建依赖

依赖注入的三种方式

  1. 通过构造器方式注入

    @Component
    public class OrderService {
        private final UserService userService;
    
        @Autowired
        public OrderService(UserService userService) {
            this.userService = userService;
        }
    }
    
  2. 通过set方法注入

    @Component
    public class OrderService {
        private UserService userService;
    
        @Autowired
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    }
    
  3. 通过字段注入

    @Component
    public class OrderService {
        @Autowired
        private UserService userService;
    }
    

Bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .

类别说明
singleton在Spring IoC容器中仅存在一个Bean实例, Bean以单例模式存在, 也是默认值
prototype每次从容器中调用Bean时, 都返回一个新的实例, 即每次调用getBean()时, 相当于执行new XxxBean()
request每次Http请求都会创建一个新的Bean, 该作用域仅适用于WebApplicationContext环境
session同一个Http Session共享一个Bean, 不同Session使用不同的Bean, 仅适用于WebApplicationContext环境

几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境

Singleton(单例模式)

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。

Prototype(原型模式)

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。当处理请求结束,request作用域的bean实例将被销毁。

Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

Bean的自动装配

自动装配(Autowiring) 是 Spring IoC 容器的一项功能,它能够自动将匹配的 Bean 注入到目标 Bean 中的依赖字段、方法或构造器中,省去了手动在 XML 或配置类中显式声明依赖关系的工作

自动装配方式

  • 基于注解的自动装配

    注解说明
    @AutowiredSpring 提供的主力自动装配注解,默认按类型装配
    @Autowired(required = false), 默认 required = true:如果找不到依赖会抛异常;设置为 false 则找不到也不会报错(字段为 null)
    @Qualifier配合 @Autowired 使用,按名称装配
    @ResourceJSR-250 标准注解,默认按名称装配, 如果没有再按照类型装配
    @InjectJSR-330 标准注解,功能类似 @Autowired
    @Value注入配置值或常量
  • 基于XML配置的自动装配

基于Java类进行配置

JavaConfig 是一种使用 Java 类代替 XML 来进行 Spring 容器配置的方式。

通过 @Configuration 注解声明配置类,使用 @Bean 方法定义 Bean,并通过 @ComponentScan 扫描组件,让整个 Spring IoC 容器通过 Java 代码完成初始化和装配。

  1. 创建配置类

    @Configuration //标识该类为配置类,相当于 <beans> 标签
    @ComponentScan(basePackages = "com.example") // 告诉 Spring 去扫描哪些包中的类(相当于 <context:component-scan>)
    public class AppConfig {
    }
    
  2. 声明Bean(可选)

    // 也可以通过 @Bean 手动定义 Bean(相当于 XML 的 <bean> 标签)
    @Configuration
    public class AppConfig {
    
        @Bean
        public UserService userService() {
            return new UserService();
        }
    }
    // 如果这个类本身没有使用 @Component 注解,Spring 就不会自动扫描它,这时通过 @Bean 可以手动注册
    
  3. 创建 ApplicationContext 容器

    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(AppConfig.class);
    
    UserService userService = context.getBean(UserService.class);
    userService.sayHello();
    // AnnotationConfigApplicationContext 是 Spring 提供的基于注解配置的容器
    
  4. JavaConfig中常见的注解

    注解作用
    @Configuration声明配置类
    @Bean定义 Bean 方法
    @ComponentScan扫描组件类
    @Import导入其他配置类
    @PropertySource加载配置文件
    @Conditional条件注册 Bean(结合 @Profile)

代理模式

代理模式的本质就是: 由一个代理对象代表真实对象对外提供服务, 控制对目标对象的访问或增强其功能

通俗点说: 你想买房,不直接找房东,而是通过中介(代理);中介可以在你和房东之间增加服务,如:带看、谈价、合同审核等

静态代理(Static Proxy)

代理类在编译时期就已经确定,即提前写死了一个代理类来代理目标类

特点

  • 代理类和目标类实现相同的接口
  • 手动编写代理类代码
  • 编译期就生成,类结构固定,灵活性差

示例

接口和目标类

public interface UserService {
    void login(String username);
}

public class UserServiceImpl implements UserService {
    public void login(String username) {
        System.out.println(username + " 登录系统");
    }
}

静态代理类

public class UserServiceProxy implements UserService {
    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    public void login(String username) {
        System.out.println("日志记录:用户尝试登录");
        target.login(username);
        System.out.println("日志记录:用户登录成功");
    }
}

调用方式

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        proxy.login("Alice");
    }
}

输出结果

日志记录:用户尝试登录
Alice 登录系统
日志记录:用户登录成功

动态代理

在运行时通过反射/字节码技术动态生成代理类,无需手动编写代理类

特点

  • 更加灵活,适合大规模代理处理
  • 实现成本低,维护简单
  • 使用频率高,Spring AOP、MyBatis Mapper 全靠它

动态代理方式

技术方式底层依赖要求
JDK 动态代理接口 + Proxy目标类必须实现接口
CGLIB 动态代理ASM 字节码增强代理目标类本身,不需要接口
JDK动态代理方式

接口和目标类

public interface UserService {
    void login(String username);
}

public class UserServiceImpl implements UserService {
    public void login(String username) {
        System.out.println(username + " 登录系统");
    }
}

创建 InvocationHandler(调用处理器)

import java.lang.reflect.*;

public class LogInvocationHandler implements InvocationHandler {
    private Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志前置...");
        Object result = method.invoke(target, args);
        System.out.println("日志后置...");
        return result;
    }
}

创建代理对象并使用

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        InvocationHandler handler = new LogInvocationHandler(target);

        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );

        proxy.login("Bob");
    }
}

输出结果

日志前置...
Bob 登录系统
日志后置...	
CGLIB 动态代理(无需接口)
public class UserService {
    public void login(String username) {
        System.out.println(username + " 登录系统");
    }
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置增强...");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("后置增强...");
        return result;
    }
});

UserService proxy = (UserService) enhancer.create();
proxy.login("Tom");

静态代理是手写代理类,结构固定、不灵活;动态代理则是运行时自动生成代理类,更加灵活,是现代框架开发的核心能力之一。Spring AOP 就是基于动态代理实现的横切增强机制。

JDK动态代理的核心类

InvocationHandler

调用处理程序

InvocationHandler是由代理实例的调用处理程序实现的接口

每个代理实例都有一个关联的调用处理程序. 它实现了接口InvocationHandler, 当在代理实例上调用方法时, 方法调用将被编码并分派到其调用处理程序的invoke方法

Object invoke(Object proxy, Method method, Object[] args)//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
Proxy

代理类

Proxy提供了创建动态代理类和实例的静态方法, 他也是由这些方法创建的所有动态代理类的超类

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)throws IllegalArgumentException{
//loader - 类加载器来定义代理类
//interfaces - 代理类实现的接口列表
//h - 调度方法调用的调用处理函数

AOP切面编程

AOP(Aspect-Oriented Programming),全称“面向切面编程”。

本质:在不改变原有业务逻辑代码的前提下,将公共功能(如日志、权限、事务、监控)以切面方式进行模块化封装和织入,从而实现逻辑解耦、职责分离、可复用性强。

通俗点说:
AOP 就是给你的核心业务逻辑“悄悄加料”,比如加事务、打印日志、限流、记录耗时等,而不用改动原始代码

image-20250720174653395

AOP核心术语

名称含义
切面(Aspect)横切逻辑的模块,例如事务、日志切面, 切面是一个类
连接点(JoinPoint)程序执行的某个“点”,如方法调用、异常抛出
切入点(Pointcut)规则,决定哪些连接点会被织入横切逻辑
通知(Advice)切面要干的事(增强逻辑),如执行前打印日志, 它是类中的一个方法
目标对象(Target)被代理、被增强的对象(你的业务类)
代理对象(Proxy)增强后的对象,实际交由容器管理
织入(Weaving)将切面逻辑织入目标对象的过程
引入(Introduction)动态添加方法或属性到目标对象中(较少使用)

image-20250720174710899

Spring AOP的常见增强类型

类型注解实现接口执行时机
前置增强@Beforeorg.springframework.aop.MethodBeforeAdvice方法执行之前
后置增强@Afterorg.springframework.aop.AfterAdvice方法执行之后(无论成功/失败)
返回增强@AfterReturningorg.springframework.aop.AfterReturningAdvice方法正常返回后
异常增强@AfterThrowingorg.springframework.aop.ThrowsAdvice方法抛出异常后
环绕增强@Aroundorg.aopalliance.intercept.MethodInterceptor方法执行前后都拦截,可控制是否执行原方法

Maven

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

execution表达式

excution表达式通常用于切点表达式中

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
  
execution([修饰符模式]? 返回类型模式 [类路径模式].[方法名模式](参数模式) [异常模式]?)

各个部分的含义如下:

  • modifiers-pattern(修饰符模式): 指定方法的修饰符,如 public、protected、private 等,默认匹配任意修饰符。
  • ret-type-pattern(返回类型模式): 指定方法的返回类型,如 void、String 等,默认匹配任意返回类型。
  • declaring-type-pattern(类路径模式): 指定方法所属的类,可以使用类的全限定名,如 com.example.service.* 表示匹配 com.example.service 包下的所有类,默认匹配任意类。
  • name-pattern(方法名模式): 指定方法的名称,可以使用通配符 ,如 add 表示以 add 开头的方法,默认匹配任意方法名。
  • param-pattern(参数模式): 指定方法的参数列表,可以使用 … 表示任意个参数,如 *(…) 表示任意参数列表的方法,默认匹配无参数方法。
  • throws-pattern(异常模式): 指定方法可能抛出的异常类型,如 java.lang.Exception、java.lang.RuntimeException 等,默认匹配任意异常类型

示例:

  1. 匹配指定包下的所有类的所有方法

    execution(* com.example.service..*.*(..))
    
  2. 匹配指定包下的所有类的指定方法名

    execution(* com.example.service..*.do*(..))
    
  3. 匹配指定包下的指定类的所有方法

    execution(* com.example.service.UserService.*(..))
    
  4. 匹配指定包下的指定类的指定方法名

    execution(* com.example.service.UserService.add*(..))
    
  5. 匹配指定包下的所有类的指定返回类型的方法

    execution(public String com.example.service..*.*(..))
    
  6. 匹配指定包下的所有类的指定参数类型的方法

    execution(* com.example.service..*.*(String, int))
    
  7. 匹配指定包下的所有类的指定参数数量的方法

    execution(* com.example.service..*.*(String, *))
    
  8. 匹配指定包下的所有类的无参数方法

    execution(* com.example.service..*.*())
    
  9. 匹配指定包下的所有类的所有 public 方法

    execution(public * com.example.service..*.*(..))
    
  10. 匹配指定包下的所有类的所有 protected 方法

    execution(protected * com.example.service..*.*(..))
    
  11. 匹配指定包下的所有类的所有 private 方法

    execution(private * com.example.service..*.*(..))
    
  12. 匹配指定包下的所有类的所有静态方法

    execution(* com.example.service..*.*(..))
    

Spring中事务实现

Spring 的事务本质是通过 AOP + 动态代理,在方法执行前后插入事务控制逻辑,如开启事务、提交事务、回滚事务

事务的两种方式

声明式事务(最常用)

通过 @Transactional 注解声明即可

@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        // 插入订单表
        // 减库存
        // 写日志
    }
}
@Transactional详解
属性说明
propagation事务传播行为(如 REQUIRED、REQUIRES_NEW)
isolation事务隔离级别(如 READ_COMMITTED)
rollbackFor指定哪些异常回滚(如 RuntimeException.class)
noRollbackFor指定哪些异常不回滚
timeout超时时间
readOnly是否只读,优化性能
@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    timeout = 30,
    rollbackFor = Exception.class
)

编程式事务(使用事务模板)

@Transactional
public void save() {
    transactionTemplate.execute(status -> {
        // do something
        return null;
    });
}

事务底层实现流程(核心原理)

基于 AOP 实现(动态代理 + 环绕通知)

核心流程如下:

  1. Spring 在容器初始化时通过 TransactionInterceptor 拦截带 @Transactional 的方法
  2. 方法执行前:开启事务(通过事务管理器)
  3. 方法执行后:
    • 如果正常:提交事务
    • 如果异常:根据异常类型回滚事务
  4. 最终释放资源(如连接)

关键类组件

类名作用
TransactionInterceptorAOP 增强器,事务控制逻辑核心
PlatformTransactionManager事务管理器接口
DataSourceTransactionManagerJDBC 实现
TransactionAspectSupport真正执行事务的辅助类

事务传播行为

传播行为说明
REQUIRED默认,当前有事务就加入,没有就新建一个
REQUIRES_NEW始终新建一个事务,暂停当前事务
SUPPORTS有事务就加入,没有就非事务执行
NOT_SUPPORTED不支持事务,暂停当前事务
NEVER当前不能有事务,有就抛异常
MANDATORY必须有事务,没有就抛异常
NESTED嵌套事务,独立提交回滚,依赖 savepoint 支持(如 JDBC)

Spring生命周期

Spring容器掌管着bean的生命周期, 从创建到销毁的过程, 我们就叫他Spring的生命周期

  1. 实例化
  2. 属性赋值
  3. 初始化
  4. 销毁, 调用容器的close()

即文档后面Spring面试题中Bean的生命周期

Spring中常用注解 放到SSM框架学完统一总结

Spring面试题

BeanFactory 与 ApplicationContext 区别

  • 相同点:
    • 都是bean的容器, 都提供了getBean()用来访问容器中的bean
    • 之间是继承关系, ApplicationContext继承了BeanFactory
    • 获取bean的时候, ApplicationContext是交由BeanFactory进行执行的
  • 不同点:
    • ApplicationContext
      • 功能更齐全, 应用通常选择它作为容器
      • 读取配置的工作是由它来完成, 然后交由BeanFactory进行创建
    • BeanFactory
      • 职责比较单一, 主要用来生产bean, 内存小, 适合在嵌入式设备中
      • 不会直接绕过ApplicationContext进行创建bean
      • 也可以直接通过扫描器告诉它需要生产哪个bean (也可以自己定义图纸进行生产)

Bean 的生命周期(初始化、销毁、自定义扩展点)

Spring Bean的生命周期大致可以分为以下几个主要阶段: 实例化(Instantiation), 属性赋值(Populate properties), 初始化(Initialization), 使用(In Use), 以及销毁(Destruction)

在整个生命周期中, Spring容器提供了多个扩展点, 允许开发人员通过实现特定的接口或者使用注解的方式, 在不同阶段插入自定义的逻辑来满足应用的各种需求

具体生命周期阶段

  1. 实例化阶段(Instantiation)

    • 这是Bean生命周期的起始阶段

    • 当Spring容器启动并开始解析配置(可以是基于XML配置, Java配置或者注解配置等方式), 发现需要创建某个Bean实例时, 就会通过相应的机制来实例化该Bean

    • 例如: 如果使用默认的无参构造器来创建Bean, Spring就会利用Java的反射机制调用其对应的无参构造器方法来生成一个Bean的实例对象

    • @Configuration
      public class AppConfig{
        @Bean
        public MyBean myBean(){
          return new MyBean();
        }
      }
      // 在这个代码中, 当Spring容器加载AppConfig这个配置类时, 会调用myBean()方法来实例化MyBean这个类, 创建出它的对象实例, 这就进入了Bean生命周期的实例化阶段
      
  2. 属性赋值(Populate properties)

    • 在Bean实例创建完成后, Spring会根据配置信息(比如在XML中配置的元素或者通过Java配置/注解配置中设置的属性值等)来对Bean实例中的属性进行赋值操作

    • Spring会查找相应的依赖注入源, 将相应的值注入到Bean的属性中

    • 常见的依赖注入方式有构造器注入, Setter方法注入, 字段注入, @Value注入

    • public class MyBean{
        private AnotherBean anotherBean;
        
        public void setAnotherBean(AnotherBean anotherBean) {
          this.anotherBean = anotherBean;
        }
      }
      // 假设AnotherBean已经在Spring容器中被管理, Spring会在这个阶段找到对应的AnotherBean实例, 并通过调用setAnotherBean方法将其注入到MyBean实例中, 完成属性的赋值
      
  3. 初始化(Initialization)

    • 多种实现方式

      • 实现InitializingBean接口: Bean可以实现InitializingBean接口, 并实现afterPropertiesSet()方法. 在属性赋值完成后, Spring会自动调用该方法, 开发人员可以在这个方法中编写一些初始化相关的逻辑, 比如初始化一些资源, 启动一些后台线程等

        • public class MyBean implements InitializingBean{
            @Override
            public void afterPropertiesSet() throws Exception {
              // 可以在这里添加具体的初始化逻辑
            }
          }
          
      • 使用@PostConstruct注解: 通过在Bean的方法上添加@PostConstruct注解, 标注的这个方法会在属性赋值后, Bean正式可用之前被调用. 这也是一种常用的在初始化阶段添加自定义逻辑的方法, 相比实现接口更加简洁直观

        • import javax.annotation.PostConstruct;
          
          public class MyBean {
          
              @PostConstruct
              public void init() {
                  System.out.println("MyBean is being initialized with @PostConstruct.");
                  // 初始化逻辑
              }
          }
          
      • Spring容器内部执行的初始化操作: 除了开发人员自定义的初始化逻辑外, Spring自身也会在这个阶段执行一些内部的初始化操作, 例如对一些AOP代理的创建等(如果Bean配置了AOP相关增强的话), 确保Bean以完整的, 符合配置要求的状态进入到可以被使用的阶段

  4. 使用阶段(In Use)

    • 经过前面的实例化, 属性赋值和初始化阶段后, Bean就处于可以正常使用的状态了
    • 在应用运行过程中, 其他的组件或者业务逻辑可以通过依赖注入获取到该Bean实例, 并调用其相应的方法来实现具体的业务功能
    • 这个阶段会一直持续, 知道Bean所在的应用上下文被关闭或者Bean被销毁
    • 例如: 在一个Web应用中, Controller层的Bean可能会被调用去处理HTTP请求, 调用其业务方法来返回相应的数据等
  5. 销毁阶段(Destruction)

    • 多种实现方式

      • 实现DisposableBean接口: Bean可以实现DisposableBean接口, 并实现其destroy()方法. 当Spring容器关闭时, 会自动遍历所有实现了该接口的Bean, 并调用他们的destroy(), 以便释放Bean占用的资源, 比如关闭数据库连接, 停止一些后台线程等

        • public class MyBean implements DisposableBean {
            @Override
            public void destroy() throws Exception {
              // 释放资源的逻辑
            }
          }
          
      • 使用@PreDestroy注解: 和初始化阶段的@PostConstruct类似, 在Bean的方法上添加@PreDestroy注解, 标注的这个方法会在Bean被销毁之前被调用, 可以在这个方法中编写清理资源的相关逻辑

        • import javax.annotation.PreDestroy;
          
          public class MyBean {
          
              @PreDestroy
              public void cleanup() {
                  System.out.println("MyBean is cleaning up resources before destruction with @PreDestroy.");
                  // 资源清理逻辑
              }
          }
          

image-20250719125050327

image-20250719124929452

Bean 作用域(singleton、prototype、request、session)

当你创建一个Bean定义的时候, 实际上就是创建了一个配方, 用来创建该Bean定义的所定义的类的实际实例,

一个Bean定义可以用来创建多个对象实例

作用域描述
singleton(单例)(默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例
prototype(原型)将单个 bean 定义范围限定为任意数量的对象实例
request(一次请求)将单个 bean 定义的生命周期限定于单个 HTTP 请求。也就是说,每个 HTTP 请求都有一个基于该 bean 定义创建的 bean 实例。仅在支持 Web 的 Spring 上下文中有效ApplicationContext
session(一个会话)将单个 bean 定义的作用域限定为 HTTP 的生命周期Session。仅在支持 Web 的 Spring 上下文中有效ApplicationContext
application()将单个 bean 定义的作用域限定于 的生命周期ServletContext。仅在支持 Web 的 Spring 上下文中有效ApplicationContext
websocket将单个 bean 定义的作用域限定于 的生命周期WebSocket。仅在支持 Web 的 Spring 上下文中有效ApplicationContext

单例作用域

  • 仅管理单例 bean 的一个共享实例,并且对具有与该 bean 定义匹配的一个或多个 ID 的 bean 的所有请求都会导致 Spring 容器返回该特定的 bean 实例。
  • 换句话说,当你定义一个 bean 定义并将其作用域设置为单例时,Spring IoC 容器会创建该 bean 定义所定义的对象的一个实例。这个单例实例存储在此类单例 bean 的缓存中,并且所有后续对该指定 bean 的请求和引用都会返回这个缓存对象
  • image-20250719170417880

原型作用域

非单例原型范围的 Bean 部署会导致每次请求该特定 Bean 时都会创建一个新的 Bean 实例。也就是说,该 Bean 会被注入到另一个 Bean 中,或者您通过getBean()容器中的方法调用来请求它。通常,您应该对所有有状态 Bean 使用原型范围,对无状态 Bean 使用单例范围

image-20250719170501619

数据访问对象 (DAO) 通常不配置为原型,因为典型的 DAO 不包含任何对话状态。对于我们来说,重用单例图的核心会更容易

请求、会话、应用程序和 WebSocket 范围

仅当您使用支持 Web 的 Spring实现(例如 )时requestsessionapplication和作用域才可用

依赖注入方式:构造器、Setter、字段注入、@Value

注解注入在外部属性注入之前执行。因此,当通过混合方式注入时,外部配置(例如,XML 指定的 Bean 属性)会有效地覆盖属性的注解

基于字段注入: @Autowired

该方式使用@Autowired方法就可以实现类的注入

@Component
public class ClassB {
 
    @Autowired
    private ClassA classA;
 
    public void testMethod1() {
        classA.method1();
    }
}

优点: 实现简单, 通过一个注解就可以完成注入

缺点:

  1. 当ClassA没有被Spring管理时,则通过@Autowrite注解注入的ClassA则是null,此时调用A的方法会出现空指针异常。而且脱离了容器环境就不能够使用独立的使用ClassB中的ClassA对象, 比如:

    ClassB classB = new ClassB();
    classB.classA.method1();
    

    调用该代码就会出现空指针异常,这样就使得跟Spring框架强行绑定了,脱离框架无法使得代码正确运行

  2. 无法注入一个final类型的属性,这是由于final类型的字段必须在类实例化的时候进行实例化,而Spring的属性注入是在实例化完成之后通过AbstractAutowireCapableBeanFactory#populateBean方法进行属性填充的

  3. 第三点也是最容易忽略的一点,在存在循环依赖问题时,通过@Autowrite方式进行注入时,项目编译不会报错,但是调用循环依赖的方法时则会报错。所以@Autowrite方式不是解决了循环依赖的问题而是将问题隐藏了,这样更致命

基于构造器注入

@Component
public class ClassB {
 
    private final ClassA classA;
 
    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }
 
    public void testMethod1() {
        classA.method1();
    }
}

优点:

  1. 通过构造方法注入可以脱离Spring框架使用注入的类,下面那段代码不会报空指针异常

    ClassB classB = new ClassB(new ClassA());
    classB.testMethod1();
    
  2. 可以注入final类型的属性,并且能够保证注入的属性都不为空

  3. 如果出现循环依赖的情况,通过该构造方法注入的方式注入的时候编译期间就会报异常

缺点: 如果注入的类比较多,或者有的类不是强制需要的时候就会增加构造方法的复杂度

基于setter方法注入

@Component
public class ClassB {
 
    private ClassA classA;
 
    @Autowired
    public void setClassA(ClassA classA) {
        this.classA = classA;
    }
 
    public void testMethod1() {
        classA.method1();
    }
}
  1. 通过set方法进行注入的时候可以对依赖进行按需注入,避免了构造方法注入的时候许多依赖注入时候的问题
  2. set方法注入可以多次重复注入
  3. set方法注入不会发生循环依赖问题

@Value

  • getAutowireCandidateResolver().getSuggestedValue(descriptor)从@Value注解上拿到配置的原始value值。此时类型就是String
  • resolveEmbeddedValue解析placeholder,即从配置文件中读取相应配置替换placeholder
  • evaluateBeanDefinitionString再次解析SpEL表达式
  • 最后通过TypeConverter转换成相应的类型
基本语法
@Value("${属性名}")
private String 属性;
使用场景
  1. 注入配置文件中的值: application.yml或者application.properties

    my:
    	name: 张三
    	age: 23
    
    @Component
    public class MyBean {
    
        @Value("${my.name}")
        private String name;
    
        @Value("${my.age}")
        private int age;
    }
    
  2. 注入常量值

    @Value("Hello World")
    private String message;
    
  3. 注入表达式(SpEL:Spring Expression Language)

    @Value("#{5 * 10}")
    private int result; // 注入 50
    
    @Value("#{T(java.lang.Math).random() * 100}")
    private double randomNumber; // 注入随机数
    
  4. 注入系统属性或环境变量

    @Value("${JAVA_HOME}")
    private String javaHome;
    
  5. 使用默认值(避免 key 不存在时报错)

    @Value("${my.timeout:30}") // 如果 my.timeout 不存在,则默认注入 30
    private int timeout;
    

自动装配(@Autowired、@Qualifier、@Primary、@Resource)

在 Spring 中,自动装配(Auto-Wiring) 是指由 Spring 自动将所需的 Bean 注入到类中,不需要手动创建或传递对象

@Autowired - 按类型自动注入(byType)

  • 自动根据类型从 Spring 容器中找到匹配的 Bean 注入。

  • 默认是 必须注入,否则会报错(可通过 required = false 设置为非必须)

  • 可用于: 字段, 构造函数, setter方法

  • @Component
    public class OrderService {
    
        @Autowired
        private UserService userService; // 自动查找 UserService 类型的 Bean 注入
    }
    

@Qualifier - 指定 Bean 名称注入(解决多实现冲突)

当容器中有多个相同类型的 Bean 时,Spring 不知道注入哪个,会抛出异常,这时需要用 @Qualifier 指定 Bean 的名称

@Component
public class OrderService {

    @Autowired
    @Qualifier("vipUserService") // 指定注入 Bean 名称为 "vipUserService"
    private UserService userService;
}

@Primary - 默认首选注入的 Bean(解决多 Bean 冲突)

在多个同类型的 Bean 中,指定某个 Bean 为默认注入对象(被 @Autowired 优先选中)。

@Component
@Primary
public class DefaultUserService implements UserService {
    // 会作为默认注入的实现
}

配合 @Autowired 使用时,如果没加 @Qualifier,就注入这个 @Primary 的 Bean。

@Resource - JSR-250 标准注解(按名称优先,其次按类型)

  • @Autowired 类似,但 按名称注入优先,其次按类型,是 Java 标准注解,Spring 也支持。
  • 对@Primary标注的bean,没有支持
  • 必须注入bean,如果没有注入,会报错(没有@Autowired(reqiured=false)功能)
@Component
public class OrderService {

    @Resource(name = "vipUserService")
    private UserService userService;
}

// 如果省略 name,Spring 默认按字段名查找:

@Resource // 会查找名称为 userService 的 Bean
private UserService userService;

AOP 通知类型(前置、后置、返回、异常、环绕)

类型描述
前置通知@Before, 在目标方法执行之前执行
常用于:权限校验、日志记录、参数校验等
后置通知@After, 在目标方法执行之后执行(无论是否异常都会执行)
常用于:资源清理、日志记录等
返回通知@AfterReturning, 在目标方法正常返回后执行(无异常)
常用于:对返回结果进行处理、日志记录
异常通知@AfterThrowing, 在目标方法抛出异常时执行
常用于:统一异常处理、异常日志记录、事务回滚
环绕通知@Around, 可以在目标方法的前后执行自定义逻辑,甚至阻止目标方法执行, 类似于拦截器
常用于:性能监控、事务控制、自定义注解处理

切入点表达式(execution、@annotation 等)

@annotation 和 @execution 都是切点表达式中常用的两种方式,用于定义 AOP 切面中的切点。它们的区别和联系如下

  • 区别:

    • @annotation: 这个切点表达式使用 @annotation(注解类型) 的形式,用于匹配带有特定注解的方法。它关注的是方法上的注解,并根据注解类型来进行匹配。

    • @execution: 这个切点表达式使用 @execution(修饰符? 返回类型 方法名 参数列表 异常模式?) 的形式,更加灵活,可以按照方法的修饰符、返回类型、方法名、参数列表等进行匹配。它关注的是方法的签名和修饰符,并根据这些信息来进行匹配

  • 联系

    • @annotation 和 @execution 都是用于定义切点的表达式,都可以用于匹配要拦截的方法
    • 可以一起使用:在某些场景下,你可能需要同时匹配方法上的注解和方法的其他特征,这时可以将 @annotation 和 @execution 结合起来使用,以实现更精确的切点匹配

AspectJ 与 Spring AOP 区别

Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现方式

  • 依赖性
    • Spring AOP是Spring框架的一部分,因此它集成在Spring应用程序中,并依赖于Spring容器。Spring AOP不需要特殊的编译器或工具,因为它是基于Java代理的运行时代理实现
    • AspectJ AOP是一个独立的AOP框架,不依赖于Spring或任何其他框架。它有自己的编译器(ajc)和语法,可以在编译时或运行时织入切面
  • 织入方式
    • Spring AOP使用代理模式在目标对象和切面之间创建代理对象。这意味着Spring AOP只支持方法级别的切面,而且只能拦截Spring管理的bean
    • AspectJ AOP支持更广泛的织入方式,包括方法级别、字段级别和构造函数级别的切面。它可以在编译时或运行时织入切面,因此更加灵活
  • 性能
    • 由于使用代理模式,Spring AOP的性能通常比较高效,但对于复杂的切面和大规模的应用程序,性能可能会有所下降
    • AspectJ AOP在性能上通常更加高效,因为它可以在编译时进行织入,减少了运行时的开销。这使得它适用于大型和高性能的应用程序
  • 语法和表达能力
    • Spring AOP使用基于注解或 XML 配置的方式来定义切面,语法相对简单,适用于一般的切面需求。但它的表达能力有限,不支持复杂的切点表达式
    • AspectJ AOP使用更为丰富和复杂的切面表达式语言,支持更多的切点表达式,可以处理复杂的切面需求。它还提供了更多的切面类型,如引入和多个切面的组合
  • 使用场景
    • 适用于轻量级应用程序,或者对AOP要求不是特别复杂的场景。如果应用程序已经使用了 Spring框架,Spring AOP可能是更好的选择
    • 适用于需要更高级、更复杂的切面需求的大型应用程序。它可以处理更复杂的织入需求,并提供更多的灵活性

动态代理(JDK vs CGLIB)

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术

  1. 什么是代理模式?

    代理模式(Proxy Pattern)给某一个对象提供一个代理,并由代理对象控制原对象的引用。代理对象在客户端和目标对象之间起到中介作用

    1. 代理模式包含以下角色:
      • Subject (抽象主题角色) 抽象主题角色声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题 的地方都可以使用代理主题。客户端需要针对抽象主题角色进行编程
      • Proxy (代理主题角色) 代理主题角色内部包含对真实主题的引用,从而可以在任何时候操作真实主题对象。 在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实 主体。代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主 题对象,并对真实主题对象的使用加以约束。代理角色通常在客户端调用所引用的真实主 题操作之前或之后还需要执行其他操作,而不仅仅是单纯的调用真实主题对象中的操作
      • RealSubject (真实主题 角色) 真实主题角色定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的方法
    2. 代理模式的优点
      • 代理模式能将代理对象与真实被调用的目标对象分离。
      • 一定程度上降低了系统的耦合度,扩展性好。
      • 可以起到保护目标对象的作用。
      • 可以对目标对象的功能增强。
    3. 代理模式的缺点
      • 代理模式会造成系统设计中类的数量增加。
      • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
  2. JDK动态代理

    1. 两个重要的类或接口: InvocationHandler(Interface), Proxy(class)

      • InvocationHandler

        • 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用

        • InvocationHandler这个接口的唯一一个方法 invoke 方法:

          Object invoke(Object proxy, Method method, Object[] args) throws Throwable
            
            //这个方法一共接受三个参数,那么这三个参数分别代表如下:
          	// proxy:指代JDK动态生成的最终代理对象
          	// method:指代的是我们所要调用真实对象的某个方法的Method对象
          	// args:指代的是调用真实对象某个方法时接受的参数
          
      • Proxy

        • Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是newProxyInstance 这个方法:

          public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler handler)  throws IllegalArgumentException
          
          // 这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
          	// loader:ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载,即代理类的类加载器。
          	// interfaces:Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。
          	// Handler:InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
          
    2. 所以我们所说的DynamicProxy(动态代理类)是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。这个DynamicProxy其实就是一个Proxy,它不会做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作

    3. JDK动态代理步骤

      JDK动态代理分为以下几步:

      1. 拿到被代理对象的引用,并且通过反射获取到它的所有的接口。
      2. 通过JDK Proxy类重新生成一个新的类,同时新的类要实现被代理类所实现的所有的接口。
      3. 动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用。
      4. 编译新生成的 Java 代码.class。
      5. 将新生成的Class文件重新加载到 JVM 中运行
  3. CGLIB动态代理

    1. JDK动态代理是通过重写被代理对象实现的接口中的方法来实现,而CGLIB是通过继承被代理对象来实现,和JDK动态代理需要实现指定接口一样,CGLIB也要求代理对象必须要实现MethodInterceptor接口,并重写其唯一的方法intercept

    2. CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。(利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理)

    3. 因为CGLIB是通过继承目标类来重写其方法来实现的,故而如果是final和private方法则无法被重写,也就是无法被代理

    4. CGLIB核心类

      1. net.sf.cglib.proxy.Enhancer:主要增强类,通过字节码技术动态创建委托类的子类实例;

        Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。

      2. net.sf.cglib.proxy.MethodInterceptor:常用的方法拦截器接口,需要实现intercept方法,实现具体拦截处理;

        public java.lang.Object intercept(java.lang.Object obj,
                                           java.lang.reflect.Method method,
                                           java.lang.Object[] args,
                                           MethodProxy proxy) throws java.lang.Throwable{}
        
        // obj:动态生成的代理对象
        // method:实际调用的方法
        // args:调用方法入参
        // net.sf.cglib.proxy.MethodProxy:java Method类的代理类,可以实现委托类对象的方法的调用;常用方法:methodProxy.invokeSuper(proxy, args);在拦截方法内可以调用多次。
        
        
    5. cglib动态代理实现分析

      1. CGLib动态代理采用了FastClass机制,其分别为代理类和被代理类各生成一个FastClass,这个FastClass类会为代理类或被代理类的方法分配一个 index(int类型)。这个index当做一个入参,FastClass 就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用更高
JDK和CGLib动态代理对比
  • JDK 动态代理是实现了被代理对象所实现的接口,CGLib是继承了被代理对象。
  • JDK和CGLib 都是在运行期生成字节码, JDK是直接写Class字节码, CGLib 使用 ASM 框架写Class字节码,Cglib代理实现更复杂,生成代理类的效率比JDK代理低。
  • JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass机制直接调用方法,CGLib 执行效率更高
  • 原理区别
    • java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知
    • 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知
    • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
    • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
    • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
  • 性能区别
    • CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类
    • 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理
  • 各自局限
    • JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。
    • cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理
类型机制回调方式适用场景效率
JDK动态代理委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法反射目标类是接口类效率瓶颈在反射调用稍慢
CGLIB动态代理继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑通过FastClass方法索引调用非接口类、非final类,非final方法第一次调用因为要生成多个Class对象,比JDK方式慢。多次调用因为有方法索引比反射快,如果方法过多,switch case过多其效率还需测试

AOP 实现原理(ProxyFactoryBean、Advisor、Interceptor)

Spring AOP 本质上就是把一组增强逻辑(Advice)组合成责任链(Interceptor Chain),并通过代理类(JDK/CGLIB)在方法调用时“织入”这些逻辑。

1.Spring内部创建代理对象的过程

在Spring的底层, 如果我们配置了代理模式, Spring会为每一个Bean创建一个对应的ProxyFactoryBean的FactoryBean来创建某个对象的代理对象

假定我们现在有一个接口****TicketService*及其实现类*RailwayStation*,我们打算创建一个代理类,在执行*TicketService****的方法时的各个阶段,插入对应的业务代码

TicketService
package org.luanlouis.meditations.thinkinginspring.aop;
 
/**
 * 售票服务
 * Created by louis on 2016/4/14.
 */
public interface TicketService {
 
    //售票
    public void sellTicket();
 
    //问询
    public void inquire();
 
    //退票
    public void withdraw();
}
impl - RailwayStation
package org.luanlouis.meditations.thinkinginspring.aop;
 
/**
 * RailwayStation 实现 TicketService
 * Created by louis on 2016/4/14.
 */
public class RailwayStation implements TicketService {
 
    public void sellTicket(){
        System.out.println("售票............");
    }
 
    public void inquire() {
        System.out.println("问询.............");
    }
 
    public void withdraw() {
        System.out.println("退票.............");
    }
}
BeforeAdvice
package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.springframework.aop.MethodBeforeAdvice;
 
import java.lang.reflect.Method;
 
/**
 * 执行RealSubject对象的方法之前的处理意见
 * Created by louis on 2016/4/14.
 */
public class TicketServiceBeforeAdvice implements MethodBeforeAdvice {
 
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("BEFORE_ADVICE: 欢迎光临代售点....");
    }
}
AfterReturningAdvice
package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.springframework.aop.AfterReturningAdvice;
 
import java.lang.reflect.Method;
 
/**
 * 返回结果时后的处理意见
 * Created by louis on 2016/4/14.
 */
public class TicketServiceAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("AFTER_RETURNING:本次服务已结束....");
    }
}
ThrowsAdvice
package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.springframework.aop.ThrowsAdvice;
 
import java.lang.reflect.Method;
 
/**
 * 抛出异常时的处理意见
 * Created by louis on 2016/4/14.
 */
public class TicketServiceThrowsAdvice implements ThrowsAdvice {
 
    public void afterThrowing(Exception ex){
        System.out.println("AFTER_THROWING....");
    }
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
        System.out.println("调用过程出错啦!!!!!");
    }
 
} 
AroundAdvice
package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
 
/**
 *
 * AroundAdvice
 * Created by louis on 2016/4/15.
 */
public class TicketServiceAroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("AROUND_ADVICE:BEGIN....");
        Object returnValue = invocation.proceed();
        System.out.println("AROUND_ADVICE:END.....");
        return returnValue;
    }
}

image-20250720153827539

创建代理类

现在,我们来手动使用****ProxyFactoryBean*来创建*Proxy*对象,并将相应的几种不同的*Advice*加入这个*proxy****对应的各个执行阶段中

package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactoryBean;
 
/**
 * 通过ProxyFactoryBean 手动创建 代理对象
 * Created by louis on 2016/4/14.
 */
public class App {
 
    public static void main(String[] args) throws Exception {
 
        //1.针对不同的时期类型,提供不同的Advice
        Advice beforeAdvice = new TicketServiceBeforeAdvice();
        Advice afterReturningAdvice = new TicketServiceAfterReturningAdvice();
        Advice aroundAdvice = new TicketServiceAroundAdvice();
        Advice throwsAdvice = new TicketServiceThrowsAdvice();
 
        RailwayStation railwayStation = new RailwayStation();
 
        //2.创建ProxyFactoryBean,用以创建指定对象的Proxy对象
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
       //3.设置Proxy的接口
        proxyFactoryBean.setInterfaces(TicketService.class);
        //4. 设置RealSubject
        proxyFactoryBean.setTarget(railwayStation);
        //5.使用JDK基于接口实现机制的动态代理生成Proxy代理对象,如果想使用CGLIB,需要将这个flag设置成true
        proxyFactoryBean.setProxyTargetClass(true);
 
        //6. 添加不同的Advice
 
        proxyFactoryBean.addAdvice(afterReturningAdvice);
        proxyFactoryBean.addAdvice(aroundAdvice);
        proxyFactoryBean.addAdvice(throwsAdvice);
        proxyFactoryBean.addAdvice(beforeAdvice);
        proxyFactoryBean.setProxyTargetClass(false);
        //7通过ProxyFactoryBean生成Proxy对象
        TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
        ticketService.sellTicket();
 
    }
 
 
}
执行结果:

image-20250720154113548

你会看到,我们成功地创建了一个通过一个ProxyFactoryBean和 真实的实例对象创建出了对应的代理对象,并将各个Advice加入到proxy代理对象中。

你会发现,在调用RailwayStation的sellticket()之前,成功插入了BeforeAdivce逻辑,而调用RailwayStation的sellticket()之后,AfterReturning逻辑也成功插入了。

AroundAdvice也成功包裹了sellTicket()方法,只不过这个AroundAdvice发生的时机有点让人感到迷惑。实际上,这个背后的执行逻辑隐藏了Spring AOP关于AOP的关于Advice调度最为核心的算法机制,这个将在本文后面详细阐述。

另外,本例中ProxyFactoryBean是通过JDK的针对接口的动态代理模式生成代理对象的,具体机制,请看下面关于ProxyFactoryBean的介绍。

2.Spring AOP的核心 - ProxyFactoryBean

上面我们通过了纯手动使用****ProxyFactoryBean*实现了AOP的功能。现在来分析一下上面的代码:我们为*ProxyFactoryBean****提供了如下信息

  1. Proxy应该感兴趣的Adivce列表;

  2. 真正的实例对象引用ticketService;

  3. 告诉ProxyFactoryBean使用基于接口实现的JDK动态代理机制实现proxy:

  4. Proxy应该具备的Interface接口:TicketService;

根据这些信息,ProxyFactoryBean就能给我们提供我们想要的Proxy对象了!那么,ProxyFactoryBean帮我们做了什么?

image-20250720154404240

Spring 使用工厂Bean模式创建每一个Proxy,对应每一个不同的Class类型,在Spring中都会有一个相对应的****ProxyFactoryBean*. 以下是*ProxyFactoryBean****的类图

image-20250720154429856

如上所示,对于生成Proxy的工厂Bean而言,它要知道对其感兴趣的Advice信息,而这类的信息,被维护到Advised中。Advised可以根据特定的类名和方法名返回对应的AdviceChain,用以表示需要执行的Advice串

3.基于JDK面向接口的动态代理JdkDynamicAopProxy生成代理对象

JdkDynamicAopProxy类实现了AopProxy,能够返回Proxy,并且,其自身也实现了InvocationHandler角色。也就是说,当我们使用proxy时,我们对proxy对象调用的方法,都最终被转到这个类的invoke()方法中

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
        //省略若干...
	/** Proxy的配置信息,这里主要提供Advisor列表,并用于返回AdviceChain */
	private final AdvisedSupport advised;
 
	/**
	 * Construct a new JdkDynamicAopProxy for the given AOP configuration.
	 * @param config the AOP configuration as AdvisedSupport object
	 * @throws AopConfigException if the config is invalid. We try to throw an informative
	 * exception in this case, rather than let a mysterious failure happen later.
	 */
	public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}
 
 
	@Override
	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}
  
  //返回代理实例对象
	@Override
	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    //这里的InvocationHandler设置成了当前实例对象,即对这个proxy调用的任何方法,都会调用这个类的invoke()方法
    //这里的invoke方法被调用,动态查找Advice列表,组成ReflectMethodInvocation
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
	/**
	 * 对当前proxy调用其上的任何方法,都将转到这个方法上
   * Implementation of {@code InvocationHandler.invoke}.
	 * <p>Callers will see exactly the exception thrown by the target,
	 * unless a hook method throws an exception.
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;
 
		TargetSource targetSource = this.advised.targetSource;
		Class<?> targetClass = null;
		Object target = null;
 
		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}
 
			Object retVal;
 
			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
 
			// May be null. Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}
 
			// Get the interception chain for this method.获取当前调用方法的拦截链
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 
			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
      //如果没有拦截链,则直接调用Joinpoint连接点的方法。
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
        //根据给定的拦截链和方法调用信息,创建新的MethodInvocation对象,整个拦截链的工作逻辑都在这个ReflectiveMethodInvocation里 
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}
 
			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

4.基于Cglib子类继承方式的动态代理CglibAopProxy生成代理对象

package org.springframework.aop.framework;
/**
 * CGLIB-based {@link AopProxy} implementation for the Spring AOP framework.
 *
 * <p>Formerly named {@code Cglib2AopProxy}, as of Spring 3.2, this class depends on
 * Spring's own internally repackaged version of CGLIB 3.</i>.
 */
@SuppressWarnings("serial")
class CglibAopProxy implements AopProxy, Serializable {
 
	// Constants for CGLIB callback array indices
	private static final int AOP_PROXY = 0;
	private static final int INVOKE_TARGET = 1;
	private static final int NO_OVERRIDE = 2;
	private static final int DISPATCH_TARGET = 3;
	private static final int DISPATCH_ADVISED = 4;
	private static final int INVOKE_EQUALS = 5;
	private static final int INVOKE_HASHCODE = 6;
 
 
	/** Logger available to subclasses; static to optimize serialization */
	protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
 
	/** Keeps track of the Classes that we have validated for final methods */
	private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<Class<?>, Boolean>();
 
 
	/** The configuration used to configure this proxy */
	protected final AdvisedSupport advised;
 
	protected Object[] constructorArgs;
 
	protected Class<?>[] constructorArgTypes;
 
	/** Dispatcher used for methods on Advised */
	private final transient AdvisedDispatcher advisedDispatcher;
 
	private transient Map<String, Integer> fixedInterceptorMap;
 
	private transient int fixedInterceptorOffset;
 
 
	/**
	 * Create a new CglibAopProxy for the given AOP configuration.
	 * @param config the AOP configuration as AdvisedSupport object
	 * @throws AopConfigException if the config is invalid. We try to throw an informative
	 * exception in this case, rather than let a mysterious failure happen later.
	 */
	public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
		this.advisedDispatcher = new AdvisedDispatcher(this.advised);
	}
 
	/**
	 * Set constructor arguments to use for creating the proxy.
	 * @param constructorArgs the constructor argument values
	 * @param constructorArgTypes the constructor argument types
	 */
	public void setConstructorArguments(Object[] constructorArgs, Class<?>[] constructorArgTypes) {
		if (constructorArgs == null || constructorArgTypes == null) {
			throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
		}
		if (constructorArgs.length != constructorArgTypes.length) {
			throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length +
					") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
		}
		this.constructorArgs = constructorArgs;
		this.constructorArgTypes = constructorArgTypes;
	}
 
 
	@Override
	public Object getProxy() {
		return getProxy(null);
	}
 
	@Override
	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
		}
 
		try {
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
 
			Class<?> proxySuperClass = rootClass;
			if (ClassUtils.isCglibProxyClass(rootClass)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}
 
			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);
 
			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
 
			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);
 
			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		catch (CodeGenerationException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of class [" +
					this.advised.getTargetClass() + "]: " +
					"Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of class [" +
					this.advised.getTargetClass() + "]: " +
					"Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Exception ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
 
	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}
 
	/**
	 * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
	 * {@link Enhancer} implementation.
	 */
	protected Enhancer createEnhancer() {
		return new Enhancer();
	}
 
 
 
	private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		// Parameters used for optimisation choices...
		boolean exposeProxy = this.advised.isExposeProxy();
		boolean isFrozen = this.advised.isFrozen();
		boolean isStatic = this.advised.getTargetSource().isStatic();
 
		// Choose an "aop" interceptor (used for AOP calls).
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
 
		// Choose a "straight to target" interceptor. (used for calls that are
		// unadvised but can return this). May be required to expose the proxy.
		Callback targetInterceptor;
		if (exposeProxy) {
			targetInterceptor = isStatic ?
					new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
		}
		else {
			targetInterceptor = isStatic ?
					new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
		}
 
		// Choose a "direct to target" dispatcher (used for
		// unadvised calls to static targets that cannot return this).
		Callback targetDispatcher = isStatic ?
				new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
 
		Callback[] mainCallbacks = new Callback[] {
				aopInterceptor,  // for normal advice
				targetInterceptor,  // invoke target without considering advice, if optimized
				new SerializableNoOp(),  // no override for methods mapped to this
				targetDispatcher, this.advisedDispatcher,
				new EqualsInterceptor(this.advised),
				new HashCodeInterceptor(this.advised)
		};
 
		Callback[] callbacks;
 
		// If the target is a static one and the advice chain is frozen,
		// then we can make some optimisations by sending the AOP calls
		// direct to the target using the fixed chain for that method.
		if (isStatic && isFrozen) {
			Method[] methods = rootClass.getMethods();
			Callback[] fixedCallbacks = new Callback[methods.length];
			this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);
 
			// TODO: small memory optimisation here (can skip creation for methods with no advice)
			for (int x = 0; x < methods.length; x++) {
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
				fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
						chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
				this.fixedInterceptorMap.put(methods[x].toString(), x);
			}
 
			// Now copy both the callbacks from mainCallbacks
			// and fixedCallbacks into the callbacks array.
			callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
			System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
			System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
			this.fixedInterceptorOffset = mainCallbacks.length;
		}
		else {
			callbacks = mainCallbacks;
		}
		return callbacks;
	}
 
 
	/**
	 * General purpose AOP callback. Used when the target is dynamic or when the
	 * proxy is not frozen.
	 */
	private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
 
		private final AdvisedSupport advised;
 
		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}
 
		@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Class<?> targetClass = null;
			Object target = null;
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// May be null. Get as late as possible to minimize the time we
				// "own" the target, in case it comes from a pool...
				target = getTarget();
				if (target != null) {
					targetClass = target.getClass();
				}
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// We can skip creating a MethodInvocation: just invoke the target directly.
					// Note that the final invoker must be an InvokerInterceptor, so we know
					// it does nothing but a reflective operation on the target, and no hot
					// swapping or fancy proxying.
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null) {
					releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}
		//省略...
	}
 
 
	/**
	 * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
	 */
	private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
 
		private final MethodProxy methodProxy;
 
		private final boolean publicMethod;
 
		public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
				Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
 
			super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
			this.methodProxy = methodProxy;
			this.publicMethod = Modifier.isPublic(method.getModifiers());
		}
 
		/**
		 * Gives a marginal performance improvement versus using reflection to
		 * invoke the target when invoking public methods.
		 */
		@Override
		protected Object invokeJoinpoint() throws Throwable {
			if (this.publicMethod) {
				return this.methodProxy.invoke(this.target, this.arguments);
			}
			else {
				return super.invokeJoinpoint();
			}
		}
	}
 
}

5.各种Advice的执行顺序是如何和方法调用进行结合的

JdkDynamicAopProxy 和CglibAopProxy只是创建代理方式的两种方式而已,实际上我们为方法调用添加的各种Advice的执行逻辑都是统一的。在Spring的底层,会把我们定义的各个Adivce分别 包裹成一个 MethodInterceptor,这些Advice按照加入Advised顺序,构成一个AdivseChain.

比如我们上述的代码:

//5. 添加不同的Advice

proxyFactoryBean.addAdvice(afterReturningAdvice);
proxyFactoryBean.addAdvice(aroundAdvice);
proxyFactoryBean.addAdvice(throwsAdvice);
proxyFactoryBean.addAdvice(beforeAdvice);
proxyFactoryBean.setProxyTargetClass(false);
//通过ProxyFactoryBean生成
TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
ticketService.sellTicket();

当我们调用 ticketService.sellTicket()时,Spring会把这个方法调用转换成一个MethodInvocation对象,然后结合上述的我们添加的各种Advice,组成一个ReflectiveMethodInvocation:

image-20250720154756093

各种Advice本质而言是一个方法调用拦截器,现在让我们看看各个Advice拦截器都干了什么?

image-20250720154824312

包裹MethodBeforeAdvice的方法拦截器
/**
 * 包裹MethodBeforeAdvice的方法拦截器
 * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
 * Used internally by the AOP framework; application developers should not need
 * to use this class directly.
 *
 * @author Rod Johnson
 */
@SuppressWarnings("serial")
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
 
	private MethodBeforeAdvice advice;
 
 
	/**
	 * Create a new MethodBeforeAdviceInterceptor for the given advice.
	 * @param advice the MethodBeforeAdvice to wrap
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
 
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		//在调用方法之前,先执行BeforeAdvice
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		return mi.proceed();
	}
 
}
包裹AfterReturningAdvice的方法拦截器
/**
 * 包裹AfterReturningAdvice的方法拦截器
 * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
 * Used internally by the AOP framework; application developers should not need
 * to use this class directly.
 *
 * @author Rod Johnson
 */
@SuppressWarnings("serial")
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
 
	private final AfterReturningAdvice advice;
 
 
	/**
	 * Create a new AfterReturningAdviceInterceptor for the given advice.
	 * @param advice the AfterReturningAdvice to wrap
	 */
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
 
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		//先调用invocation
		Object retVal = mi.proceed();
		//调用成功后,调用AfterReturningAdvice
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
 
}
包裹ThrowsAdvice的方法拦截器
/**
 * Interceptor to wrap an after-throwing advice.
 *
 * <p>The signatures on handler methods on the {@code ThrowsAdvice}
 * implementation method argument must be of the form:<br>
 *
 * {@code void afterThrowing([Method, args, target], ThrowableSubclass);}
 *
 * <p>Only the last argument is required.
 *
 * <p>Some examples of valid methods would be:
 *
 * <pre class="code">public void afterThrowing(Exception ex)</pre>
 * <pre class="code">public void afterThrowing(RemoteException)</pre>
 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
 *
 * <p>This is a framework class that need not be used directly by Spring users.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
 
	private static final String AFTER_THROWING = "afterThrowing";
 
	private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);
 
 
	private final Object throwsAdvice;
 
	/** Methods on throws advice, keyed by exception class */
	private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<Class<?>, Method>();
 
 
	/**
	 * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
	 * @param throwsAdvice the advice object that defines the exception
	 * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
	 * implementation)
	 */
	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;
 
		Method[] methods = throwsAdvice.getClass().getMethods();
		for (Method method : methods) {
			//ThrowsAdvice定义的afterThrowing方法是Handler方法
			if (method.getName().equals(AFTER_THROWING) &&
					(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
					Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
				) {
				// Have an exception handler
				this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
				if (logger.isDebugEnabled()) {
					logger.debug("Found exception handler method: " + method);
				}
			}
		}
 
		if (this.exceptionHandlerMap.isEmpty()) {
			throw new IllegalArgumentException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}
 
	public int getHandlerMethodCount() {
		return this.exceptionHandlerMap.size();
	}
 
	/**
	 * Determine the exception handle method. Can return null if not found.
	 * @param exception the exception thrown
	 * @return a handler for the given exception type
	 */
	private Method getExceptionHandler(Throwable exception) {
		Class<?> exceptionClass = exception.getClass();
		if (logger.isTraceEnabled()) {
			logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
		}
		Method handler = this.exceptionHandlerMap.get(exceptionClass);
		while (handler == null && exceptionClass != Throwable.class) {
			exceptionClass = exceptionClass.getSuperclass();
			handler = this.exceptionHandlerMap.get(exceptionClass);
		}
		if (handler != null && logger.isDebugEnabled()) {
			logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
		}
		return handler;
	}
 
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		//使用大的try,先执行代码,捕获异常
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			//获取异常处理方法
			Method handlerMethod = getExceptionHandler(ex);
			//调用异常处理方法
			if (handlerMethod != null) {
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
 
	private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
		Object[] handlerArgs;
		if (method.getParameterTypes().length == 1) {
			handlerArgs = new Object[] { ex };
		}
		else {
			handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
		}
		try {
			method.invoke(this.throwsAdvice, handlerArgs);
		}
		catch (InvocationTargetException targetEx) {
			throw targetEx.getTargetException();
		}
	}
 
}
关于AroundAdivce,其本身就是一个MethodInterceptor,所以不需要额外做转换了。

在拦截器串中,每个拦截器最后都会调用MethodInvocation的proceed()方法。如果按照简单的拦截器的执行串来执行的话,MethodInvocation的proceed()方法至少要执行N次(N表示拦截器Interceptor的个数),因为每个拦截器都会调用一次proceed()方法。
真实的Spring的方法调用过程能够控制这个逻辑按照我们的思路执行,Spring将这个整个方法调用过程连同若干个Advice组成的拦截器链组合成ReflectiveMethodInvocation对象

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
 
	protected final Object proxy;
 
	protected final Object target;
 
	protected final Method method;
 
	protected Object[] arguments;
 
	private final Class<?> targetClass;
 
	/**
	 * Lazily initialized map of user-specific attributes for this invocation.
	 */
	private Map<String, Object> userAttributes;
 
	/**
	 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
	 * that need dynamic checks.
	 */
	protected final List<?> interceptorsAndDynamicMethodMatchers;
 
	/**
	 * Index from 0 of the current interceptor we're invoking.
	 * -1 until we invoke: then the current interceptor.
	 */
	private int currentInterceptorIndex = -1;
 
 
	/**
	 * Construct a new ReflectiveMethodInvocation with the given arguments.
	 * @param proxy the proxy object that the invocation was made on
	 * @param target the target object to invoke
	 * @param method the method to invoke
	 * @param arguments the arguments to invoke the method with
	 * @param targetClass the target class, for MethodMatcher invocations
	 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
	 * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
	 * MethodMatchers included in this struct must already have been found to have matched
	 * as far as was possibly statically. Passing an array might be about 10% faster,
	 * but would complicate the code. And it would work only for static pointcuts.
	 */
	protected ReflectiveMethodInvocation(
			Object proxy, Object target, Method method, Object[] arguments,
			Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
 
		this.proxy = proxy;//proxy对象
		this.target = target;//真实的realSubject对象
		this.targetClass = targetClass;//被代理的类类型
		this.method = BridgeMethodResolver.findBridgedMethod(method);//方法引用
		this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);//调用参数
		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;//Advice拦截器链
	}
 
 
	@Override
	public final Object getProxy() {
		return this.proxy;
	}
 
	@Override
	public final Object getThis() {
		return this.target;
	}
 
	@Override
	public final AccessibleObject getStaticPart() {
		return this.method;
	}
 
	/**
	 * Return the method invoked on the proxied interface.
	 * May or may not correspond with a method invoked on an underlying
	 * implementation of that interface.
	 */
	@Override
	public final Method getMethod() {
		return this.method;
	}
 
	@Override
	public final Object[] getArguments() {
		return (this.arguments != null ? this.arguments : new Object[0]);
	}
 
	@Override
	public void setArguments(Object... arguments) {
		this.arguments = arguments;
	}
 
 
	@Override
	public Object proceed() throws Throwable {
		//	没有拦截器,则直接调用Joinpoint上的method,即直接调用MethodInvocation We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
    // 取得第拦截器链上第N个拦截器 
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		//PointcutInterceptor会走这个逻辑
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			//当前拦截器是符合拦截规则,每个拦截器可以定义是否特定的类和方法名是否符合拦截规则
      //实际上PointCut定义的方法签名最后会转换成这个MethodMatcher,并置于拦截器中
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
			     //符合拦截规则,调用拦截器invoke()	
           return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
        // 当前方法不需要拦截器操作,则直接往前推进
        return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
      //直接调用拦截器,
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
 
	/**
	 * Invoke the joinpoint using reflection.
	 * Subclasses can override this to use custom invocation.
	 * @return the return value of the joinpoint
	 * @throws Throwable if invoking the joinpoint resulted in an exception
	 */
	protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

*上述的代码比较冗杂,解释起来比较繁琐,请看下面一张图,你就知道这段代码的思路了:*

img

实例分析

根据上面的执行链上的逻辑,我们将我们上面举的例子的输出结果在整理一下:

Advice拦截器的添加顺序:

proxyFactoryBean.addAdvice(afterReturningAdvice);
proxyFactoryBean.addAdvice(aroundAdvice);
proxyFactoryBean.addAdvice(throwsAdvice);
proxyFactoryBean.addAdvice(beforeAdvice);

*第一个拦截器:AfterReturningAdvice*

第一个添加的是afterReturningAdivce,它所处的位置是第一个拦截器,执行的操作就是:

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

也就是说,先完成MethodInvocation的proceed()方法再执行相应的advice;而调用了mi.proceed()方法,导致了当前的调用链后移,进行和后续的操作,也就是说,AfterReturningAdvice只能等到整个拦截器链上所有执行完毕后才会生效,所以: AFTER_RETURNING:本次服务已结束… 这句话排在了最后
第二个拦截器:AroundAdvice

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
  System.out.println("AROUND_ADVICE:BEGIN....");
  Object returnValue = invocation.proceed();
  System.out.println("AROUND_ADVICE:END.....");
  return returnValue;
}

现在执行到了第二个拦截器,首先输出了"AROUND_ADVICE:BEGIN…“,接着调用Invocation.proceed(),等到剩余的执行完后,再输出"AROUND_ADVICE:END…”

第三个拦截器:ThrowsAdvice:

ThrowsAdvice拦截器的处理模式是:

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	try {
	       //先执行invocation.proceed();	
                   return mi.proceed();
	}
	catch (Throwable ex) {
                   //捕捉错误,调用afterThrowing()方法
                    Method handlerMethod = getExceptionHandler(ex);
		if (handlerMethod != null) {
			invokeHandlerMethod(mi, ex, handlerMethod);
		}
		throw ex;
	}
}

上述的逻辑是,先执行Invocation.proceed();如果这个过程中抛出异常,则调用ThrowsAdvice

第四个拦截器:BeforeAdvice:

这个拦截器的工作逻辑如下:

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );//先执行Advice
		return mi.proceed();//后执行Invocation
	}

综上所有的拦截器过程,我们就能理解,为什么我们刚开始的输出为什么是下面这样了:

6.PointCut与Advice的结合 - Advice的条件执行

上述这些Advice是无条件地加入了我们创建的对象中。无论调用Target的任何方法,这些Advice都会被触发到。

那么,我们如果想让Advice只让它对特定的方法或特定类起作用, 这个实际上是需要我们添加一个过滤器,如果满足条件,则Advice生效,否则无效。Spring将这个过滤器抽象成如下的接口:

public interface MethodMatcher {
 
	/**
	 * 提供方法签名和所属的Class类型,判断是否支持 
   * Perform static checking whether the given method matches. If this
	 * returns {@code false} or if the {@link #isRuntime()} method
	 * returns {@code false}, no runtime check (i.e. no.
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
	 * @param method the candidate method
	 * @param targetClass the target class (may be {@code null}, in which case
	 * the candidate class must be taken to be the method's declaring class)
	 * @return whether or not this method matches statically
	 */
	boolean matches(Method method, Class<?> targetClass);
 
	/**
	 * Is this MethodMatcher dynamic, that is, must a final call be made on the
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
	 * runtime even if the 2-arg matches method returns {@code true}?
	 * <p>Can be invoked when an AOP proxy is created, and need not be invoked
	 * again before each method invocation,
	 * @return whether or not a runtime match via the 3-arg
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} method
	 * is required if static matching passed
	 */
	boolean isRuntime();
 
	/**
	 * Check whether there a runtime (dynamic) match for this method,
	 * which must have matched statically.
	 * <p>This method is invoked only if the 2-arg matches method returns
	 * {@code true} for the given method and target class, and if the
	 * {@link #isRuntime()} method returns {@code true}. Invoked
	 * immediately before potential running of the advice, after any
	 * advice earlier in the advice chain has run.
	 * @param method the candidate method
	 * @param targetClass the target class (may be {@code null}, in which case
	 * the candidate class must be taken to be the method's declaring class)
	 * @param args arguments to the method
	 * @return whether there's a runtime match
	 * @see MethodMatcher#matches(Method, Class)
	 */
	boolean matches(Method method, Class<?> targetClass, Object... args);
 
 
	/**
	 * Canonical instance that matches all methods.
	 */
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
 
}

将这个匹配器MethodMatcher和拦截器Interceptor 结合到一起,就构成了一个新的类InterceptorAndDynamicMethodMatcher :

/**
 * Internal framework class, combining a MethodInterceptor instance
 * with a MethodMatcher for use as an element in the advisor chain.
 *
 * @author Rod Johnson
 */
class InterceptorAndDynamicMethodMatcher {
 
	final MethodInterceptor interceptor;
 
	final MethodMatcher methodMatcher;
 
	public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
		this.interceptor = interceptor;
		this.methodMatcher = methodMatcher;
	}
 
}

我们再将上述的包含整个拦截器执行链逻辑的ReflectiveMethodInvocation实现的核心代码在过一遍:

	@Override
	public Object proceed() throws Throwable {
		//	We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
 
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		//起到一定的过滤作用,如果不匹配,则直接skip
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			//满足匹配规则,则拦截器Advice生效
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
        //拦截器尚未生效,直接skip
        return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
实战

我们现在实现一个PointcutAdisor,PointcutAdvisor表示拥有某个Pointcut的Advisor。

package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
 
/**
 * 实现一个PointcutAdvisor,通过提供的Pointcut,对Advice的执行进行过滤
 * Created by louis on 2016/4/16.
 */
public class FilteredAdvisor implements PointcutAdvisor {
 
    private Pointcut pointcut;
    private Advice advice;
 
    public FilteredAdvisor(Pointcut pointcut, Advice advice) {
        this.pointcut = pointcut;
        this.advice = advice;
    }
 
    /**
     * Get the Pointcut that drives this advisor.
     */
    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }
 
    @Override
    public Advice getAdvice() {
        return advice;
    }
 
    @Override
    public boolean isPerInstance() {
        return false;
    }
}

创建代理对象

package org.luanlouis.meditations.thinkinginspring.aop;
 
import org.aopalliance.aop.Advice;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactoryBean;
 
/**
 * 通过ProxyFactoryBean 手动创建 代理对象
 * Created by louis on 2016/4/14.
 */
public class App {
 
    public static void main(String[] args) throws Exception {
 
        //1.针对不同的时期类型,提供不同的Advice
        Advice beforeAdvice = new TicketServiceBeforeAdvice();
        Advice afterReturningAdvice = new TicketServiceAfterReturningAdvice();
        Advice aroundAdvice = new TicketServiceAroundAdvice();
        Advice throwsAdvice = new TicketServiceThrowsAdvice();
 
        RailwayStation railwayStation = new RailwayStation();
 
        //2.创建ProxyFactoryBean,用以创建指定对象的Proxy对象
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
       //3.设置Proxy的接口
        proxyFactoryBean.setInterfaces(TicketService.class);
        //4. 设置RealSubject
        proxyFactoryBean.setTarget(railwayStation);
        //5.使用JDK基于接口实现机制的动态代理生成Proxy代理对象,如果想使用CGLIB,需要将这个flag设置成true
        proxyFactoryBean.setProxyTargetClass(true);
 
        //5. 添加不同的Advice
 
        proxyFactoryBean.addAdvice(afterReturningAdvice);
        proxyFactoryBean.addAdvice(aroundAdvice);
        proxyFactoryBean.addAdvice(throwsAdvice);
        //proxyFactoryBean.addAdvice(beforeAdvice);
        proxyFactoryBean.setProxyTargetClass(false);
 
        //手动创建一个pointcut,专门拦截sellTicket方法
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution( * sellTicket(..))");
        //传入创建的beforeAdvice和pointcut
        FilteredAdvisor sellBeforeAdvior = new FilteredAdvisor(pointcut,beforeAdvice);
        //添加到FactoryBean中
        proxyFactoryBean.addAdvisor(sellBeforeAdvior);
        
        //通过ProxyFactoryBean生成
        TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
        ticketService.sellTicket();
        System.out.println("---------------------------");
        ticketService.inquire();
 
    }
 
 
}

image-20250720160037767

从结果中你可以清晰地看到,我们可以对某一个Advisor(即Advice)添加一个pointcut限制,这样就可以针对指定的方法执行Advice了!本例中使用了PointcutAdvisor,实际上,带底层代码中,Spring会将PointcutAdvisor转换成InterceptorAndDynamicMethodMatcher 参与ReflectiveMethodInvocation关于拦截器链的执行逻辑:

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
 
	@Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, Class<?> targetClass) {
 
		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
                
		for (Advisor advisor : config.getAdvisors()) {
                                //PointcutAdvisor向 InterceptorAndDynamicMethodMatcher 的转换  
                               if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
 
		return interceptorList;
	}
 
	/**
	 * Determine whether the Advisors contain matching introductions.
	 */
	private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
		for (int i = 0; i < config.getAdvisors().length; i++) {
			Advisor advisor = config.getAdvisors()[i];
			if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (ia.getClassFilter().matches(actualClass)) {
					return true;
				}
			}
		}
		return false;
	}
 
}

BeanPostProcessor / InstantiationAwareBeanPostProcessor

BeanPostProcessor

Spring提供了一个BeanPostProcessor接口,俗称Bean后置通知处理器,它提供了两个方法postProcessBeforeInitializationpostProcessAfterInitialization。其中postProcessBeforeInitialization在组件的初始化方法调用之前执行,postProcessAfterInitialization在组件的初始化方法调用之后执行。它们都包含两个入参:

  1. bean:当前组件对象;
  2. beanName:当前组件在容器中的名称。

两个方法都返回一个Object类型,我们可以直接返回当前组件对象,或者包装后返回

二者区别

Initialization为初始化的意思,Instantiation为实例化的意思

在Spring Bean生命周期中,实例化指的是创建Bean的过程,初始化指的是Bean创建后,对其属性进行赋值(populate bean)、后置处理等操作的过程,所以Instantiation执行时机先于Initialization。

二者类关系

BeanPostProcessor的类结构

image-20250720160831907

方法解析

  1. BeanPostProcessor
    • postProcessBeforeInitialization(Object bean, String beanName):bean:Bean实例;beanName:Bean名称。方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用前调用,此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例,如果返回null,则后续的后置处理方法不再被调用。
    • postProcessAfterInitialization(Object bean, String beanName):bean:Bean实例;beanName:Bean名称。方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用后调用,此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例,如果返回null,则后续的后置处理方法不再被调用。
  2. InstantiationAwareBeanPostProcessor
    • postProcessBeforeInstantiation(Class<?> beanClass, String beanName):beanClass:待实例化的Bean类型;beanName:待实例化的Bean名称。方法作用为:在Bean实例化前调用该方法,返回值可以为代理后的Bean,以此代替Bean默认的实例化过程。返回值不为null时,后续只会调用BeanPostProcessor的 postProcessAfterInitialization方法,而不会调用别的后续后置处理方法(如postProcessAfterInitialization、postProcessBeforeInstantiation等方法);返回值也可以为null,这时候Bean将按默认方式初始化。
    • postProcessAfterInstantiation(Object bean, String beanName):bean:实例化后的Bean,此时属性还没有被赋值;beanName:Bean名称。方法作用为:当Bean通过构造器或者工厂方法被实例化后,当属性还未被赋值前,该方法会被调用,一般用于自定义属性赋值。方法返回值为布尔类型,返回true时,表示Bean属性需要被赋值;返回false表示跳过Bean属性赋值,并且InstantiationAwareBeanPostProcessor的postProcessProperties方法不会被调用。

执行时机对比

新建MyBeanPostProcessor实现BeanPostProcessor接口

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("demoApplication".equals(beanName)) {
            System.out.println("post processor before " + beanName + " initialization");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("demoApplication".equals(beanName)) {
            System.out.println("post processor after " + beanName + " initialization");
        }
        return bean;
    }

}

接着新建MyBeanInstantiationPostProcessor实现InstantiationAwareBeanPostProcessor接口:

@Component
public class MyBeanInstantiationPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if ("demoApplication".equals(beanName)) {
            System.out.println("post process before " + beanName + " instantiation");
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("demoApplication".equals(beanName)) {
            System.out.println("post process after " + beanName + " instantiation");
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if ("demoApplication".equals(beanName)) {
            System.out.println("post process " + beanName + " properties");
        }
        return pvs;
    }
}

执行结果:

先执行InstantiationAwareBeanPostProcessor(before, after, postProcessProperties)

再执行BeanPostProcessor(before, after)

post process before demoApplication instantiation
post process after demoApplication instantiation
post process demoApplication properties
post processor before demoApplication initialization
post processor after demoApplication initialization
  
//如果将MyBeanInstantiationPostProcessor的postProcessAfterInstantiation方法返回值改为false,程序输出如下:
post process before demoApplication instantiation
post process after demoApplication instantiation
post processor before demoApplication initialization
post processor after demoApplication initialization

总结

image-20250720161743630

BeanFactoryPostProcessor / BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor

image-20250720162221281

postProcessBeanFactory方法的执行时机为:BeanFactory标准初始化之后,所有的Bean定义已经被加载,但Bean的实例还没被创建(不包括BeanFactoryPostProcessor类型)。该方法通常用于修改bean的定义,Bean的属性值等,甚至可以在此快速初始化Bean。

BeanDefinitionRegistryPostProcessor

image-20250720162322119

postProcessBeanDefinitionRegistry方法的执行时机为:所有的Bean定义即将被加载,但Bean的实例还没被创建时。

也就是说,BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法执行时机先于BeanFactoryPostProcessor的postProcessBeanFactory方法。这个方法通常用于给IOC容器添加额外的组件。

ApplicationListener / ApplicationEvent

ApplicationListenerApplicationEvent 是 Spring 提供的事件监听机制的核心组件,适用于应用内组件解耦广播通知异步处理等场景

ApplicationEvent消息对象ApplicationListener消息订阅者ApplicationEventPublisher消息发布者。三者配合构建出 Spring 的应用内事件驱动架构

Spring事件机制使用观察者模式来传递时间和消息.

我们可以只用ApplicationEvent类发布事件, 然后使用ApplicationListener接口来监听事件.

当事件发生时, 所有注册的ApplicationListener都会得到通知.

事件用于在松耦合的组件之间交换消息, 由于发布者和订阅者之间没有直接耦合, 因此可以在不影响发布者的情况下修改订阅者, 反之亦然

核心概念

  1. ApplicationEvent: 事件

    • 是所有自定义事件的基类

    • 实际上是一个普通的pojo事件类, 表示"某事发生了"

    • // 自定义事件类
      public class MyEvent extends ApplicationEvent {
       
        private String message;
       
        public MyEvent(Object source, String message) {
          super(source);
          this.message = message;
        }
       
        public String getMessage() {
          return message;
        }
      }
      
  2. ApplicationListener: 事件监听器

    • 实现此接口的 Bean,会监听指定类型的事件

    • 定义一个事件监听器类 MyEventListener,该类实现 ApplicationListener 接口,并注册为 Spring 的组件。只要监听器对象在 Spring 应用程序上下文中注册,它就会接收事件。当 Spring 路由一个事件时,它使用监听器的签名来确定它是否与事件匹配

    • // 事件监听器
      @Component
      public class MyEventListener implements ApplicationListener<MyEvent> {
        // 事件发生时执行
        @Override
        public void onApplicationEvent(MyEvent event) {
          System.out.println("接收到事件: " + event.getMessage());
          // 模拟事件处理
          try {
            Thread.sleep(3000);
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          }
        }
      }
      
  3. ApplicationEventPublisher: 事件发布器

    • 用于在任意位置发布事件

    • @RestController
      public class HelloController {
        @Autowired
        private ApplicationContext context;
       
        @GetMapping("/hello")
        public void hello() {
          System.out.println("准备发送事件");
          context.publishEvent(new MyEvent(this, "welcome to hangge.com"));
          System.out.println("事件发送完毕");
        }
      }
      
    • spring 事件是同步的,这意味着发布者线程将阻塞,直到所有监听都完成对事件的处理为止

@EventListener 简化监听

Spring 4.2+ 支持使用 @EventListener 注解监听事件,更简洁

@Component
public class MyEventHandler {
    @EventListener
    public void handleEvent(MyEvent event) {
        System.out.println("注解监听到事件:" + event.getMessage());
    }
}

可选地也可以指定条件、异步等:

@EventListener(condition = "#event.message.contains('关键')")
public void handle(MyEvent event) {}

使用 @Order 指定优先级

当 Spring 发布一个事件时,会调用所有能处理这个事件的事件监听器方法。如果你有多个事件监听器方法,那么 Spring 会依次调用这些方法

我们可以使用 @Order 注解来指定事件监听器方法的优先级。@Order 注解可以标注在类上或方法上,表示这个类或方法的优先级。数值越小,优先级越高

使用 @Async 实现异步事件监听

如果监听器方法较慢,建议异步处理, 我们可以使用 @Async 注解来标注一个事件监听器方法,表示这个方法是一个异步方法,应该在独立的线程中执行

同时我们还需要在配置类(@Configuration 类之一或 @SpringBootApplication 类)中启用异步处理,才能使用 @Async 注解,需开启 @EnableAsync

// 事件监听器
@Component
public class MyEventListener{
  // 使用注解实现事件监听
  @Async
  @EventListener
  public void onApplicationEvent(MyEvent event) {
    System.out.println("接收到事件: " + event.getMessage());
    // 模拟事件处理
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }
}

Spring Boot 内置的 Application Event

  1. Spring Boot 中包含了一些与 SpringApplication 生命周期相关的内置 Application Event,包括:

    • ApplicationStartingEvent:在 Spring Boot 应用程序启动之前发布。

    • ApplicationEnvironmentPreparedEvent:在 Spring Boot 应用程序的环境已经准备好,但正在创建 Application Context 上下文之前发布。

    • ApplicationContextInitializedEvent:当 Spring Boot 应用程序 Application Context 上下文准备就绪并且调用

    • ApplicationContextInitializers,但尚未加载 bean 定义时发布。

    • ApplicationPreparedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经创建,但尚未刷新之前发布。

    • ApplicationStartedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经刷新,但尚未启动之前发布。

    • ApplicationReadyEvent:在 Spring Boot 应用程序已经启动并准备接受请求之后发布。

    • ApplicationFailedEvent:在 Spring Boot 应用程序启动失败时发布。

InitializingBean / DisposableBean / SmartInitializingSingleton

接口名方法调用时机用途替代方案
InitializingBeanafterPropertiesSet()Bean 初始化后初始化逻辑@PostConstruct
DisposableBeandestroy()Bean 销毁前清理资源@PreDestroy
SmartInitializingSingletonafterSingletonsInstantiated()所有单例 Bean 初始化完毕后通知全局、统一初始化无明确替代,适合高级用法
  1. 如果只是单个 Bean 初始化或清理逻辑,推荐用 @PostConstruct / @PreDestroy,代码更解耦。
  2. 如果需要在所有单例加载完成后统一处理,比如自动收集注册表、全局回调,使用 SmartInitializingSingleton 更合适。
  3. 不推荐直接依赖 InitializingBean / DisposableBean,除非做基础框架封装

Spring Expression Language(SpEL): 动态计算表达式的语言, 常用语配置文件和注解中

字符串拼接、数学运算、集合操作、条件判断

// 字符串拼接
@Value("#{ 'server.port=' + 8080 }")
private String configLine;  // server.port=8080

// 数学运算
@Value("#{1 + 2 * 3}")      // 7
private int result;

@Value("#{T(java.lang.Math).random() * 100}")  // 使用静态方法
private double random;

// 条件判断, 三元表达式
@Value("#{systemProperties['os.name'].contains('Windows') ? 'win' : 'unix'}")
private String osType;

// 集合操作
@Value("#{myList[0]}")  // 获取第一个元素
private String firstItem;

@Value("#{myMap['key1']}")  // 获取 Map 中 key 为 key1 的值
private String mapValue;

@Value("#{myList.size()}")  // 调用对象方法
private int listSize;

与注解结合使用

// @Value
@Value("#{config.port + 1}")
private int nextPort;

// @Scheduled
@Scheduled(cron = "#{@cronConfig.dailyTask}")
public void runDaily() {}

// PreAuthorize
@PreAuthorize("hasRole('ADMIN') and #user.name == authentication.name")
public void secureMethod(User user) {}

访问 Bean / 属性 / 方法

@Value("#{myService.beanName}")
private String name;

@Value("#{myService.getPort() + 100}")
private int port;

@Value("#{T(java.lang.Math).PI}")  // 调用类静态字段
private double pi;

JdbcTemplate 使用

执行SQL语句的方法大致分为3类:

创建JdbcTemplate对象:

// 创建JdbcTemplate对象,方便执行SQL语句
public JdbcTemplate(DataSource dataSource)
  1. execute:可以执行所有SQL语句,一般用于执行DDL语句。

    // execute可以执行所有SQL语句,因为没有返回值,一般用于执行DDL语句。
    public void execute(final String sql)
    
  2. update:用于执行INSERTUPDATEDELETE等DML语句。

    // 用于执行`INSERT`、`UPDATE`、`DELETE`等DML语句。
    public int update(final String sql)
    
  3. queryXxx:用于DQL数据查询语句

    // 执行查询语句,返回一个int类型的值。
    public int queryForInt(String sql);
      
    // 执行查询语句,返回一个long类型的数据。
    public long queryForLong(String sql);
    
    // 执行查询语句,返回一个指定类型的数据。
    public <T> T queryForObject(String sql, Class<T> requiredType);
    
    // 执行查询语句,将一条记录放到一个Map中。
    public Map<String, Object> queryForMap(String sql);
    
    // 执行查询语句,返回一个List集合,List中存放的是Map类型的数据。
    public List<Map<String, Object>> queryForList(String sql);
    
    // 执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据。
    public <T> List<T> query(String sql, RowMapper<T> rowMapper);
    
    // 执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据。
    public <T> List<T> query(String sql, RowMapper<T> rowMapper);
    
    // BeanPropertyRowMapper类实现了RowMapper接口
    public class BeanPropertyRowMapper<T> implements RowMapper<T>;
    

编程式事务 vs 声明式事务

当多个操作需要作为一个整体来执行时,事务可以确保数据的完整性和一致性,并避免出现异常和错误情况。在SpringBoot框架中,我们可以使用声明式事务和编程式事务来管理事务处理

开启使用

我们在启动类上添加注解:@EnableTransactionManagement

后续使用就可以添加注解@Transactional(rollbackFor = Exception.class)使用,或者是使用编程式事务使用了

声明式事务源码

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable{}

TransactionInterceptor UML图

image-20250720173127819

声明式事务主要是通过AOP实现,主要包括以下几个节点:

  1. 启动时扫描@Transactional注解:在启动时,Spring Boot会扫描所有使用了@Transactional注解的方法,并将其封装成TransactionAnnotationParser对象。
  2. AOP 来实现事务管理的核心类依然是 TransactionInterceptor。TransactionInterceptor 是一个拦截器,用于拦截使用了 @Transactional 注解的方法
  3. 将TransactionInterceptor织入到目标方法中:在AOP编程中,使用AspectJ编写切面类,通过@Around注解将TransactionInterceptor织入到目标方法中
  4. 在目标方法执行前创建事务:在目标方法执行前,TransactionInterceptor会调用PlatformTransactionManager创建一个新的事务,并将其纳入到当前线程的事务上下文中。
  5. 执行目标方法:在目标方法执行时,如果发生异常,则将事务状态标记为ROLLBACK_ONLY;否则,将事务状态标记为COMMIT
  6. 提交或回滚事务:在目标方法执行完成后,TransactionInterceptor会根据事务状态(COMMIT或ROLLBACK_ONLY)来决定是否提交或回滚事务。

源码:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
	// Work out the target class: may be {@code null}.
	// The TransactionAttributeSource should be passed the target class
	// as well as the method, which may be from an interface.
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
		@Override
		@Nullable
		public Object proceedWithInvocation() throws Throwable {
			return invocation.proceed();
		}
		@Override
		public Object getTarget() {
			return invocation.getThis();
		}
		@Override
		public Object[] getArguments() {
			return invocation.getArguments();
		}
	});
}

================================================================
下面是核心处理方法,把不太重要的代码忽略了,留下每一步的节点
================================================================
  
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
	final InvocationCallback invocation) throws Throwable {
	// 获取事务属性
	final TransactionManager tm = determineTransactionManager(txAttr);
	// 准备事务
	TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
	// 执行目标方法
	Object retVal = invocation.proceedWithInvocation();
	 // 回滚事务
	completeTransactionAfterThrowing(txInfo, ex);
	// 提交事务
	commitTransactionAfterReturning(txInfo);
}	

编程式事务源码

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean{}

TransactionTemplate UML图:

image-20250720173404761

TransactionTemplate类的execute()方法封装了事务的具体实现,通过调用TransactionCallback对象的doInTransaction()方法来执行业务逻辑并管理事务。在具体实现中,TransactionTemplate类会自动控制事务的提交和回滚,并将异常抛出给上层调用者进行处理。

@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
	Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

	if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
		return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
	}
	else {
		TransactionStatus status = this.transactionManager.getTransaction(this);
		T result;
		try {
			result = action.doInTransaction(status);
		}
		catch (RuntimeException | Error ex) {
			// Transactional code threw application exception -> rollback
			rollbackOnException(status, ex);
			throw ex;
		}
		catch (Throwable ex) {
			// Transactional code threw unexpected exception -> rollback
			rollbackOnException(status, ex);
			throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
		}
		this.transactionManager.commit(status);
		return result;
	}
}

二者区别

  • 技术实现方式:声明式事务是通过AOP技术来实现的,而编程式事务是通过编写具体的代码来实现的。
  • 代码耦合度:声明式事务可以将事务处理逻辑从业务代码中分离出来,从而降低代码的耦合度。而编程式事务需要在业务代码中显式地调用事务管理代码,因此会增加代码的耦合度。
  • 难易程度:声明式事务相对来说比较容易上手,开发人员只需要学习注解或XML配置即可。而编程式事务需要开发人员理解事务管理的底层机制,并编写具体的代码。
  • 性能影响:由于声明式事务是由容器来处理的,所以在一些场景下可能会对性能产生影响,大事务会有很多问题(下面在说一下大事务出现的问题)。而编程式事务由于直接调用事务管理API,相对来说会有更好的性能表现。

优缺点

1. 声明式事务

声明式事务通常通过AOP技术实现,在方法或类级别上声明事务属性。 声明式事务的优点包括:

简化代码:开发人员只需要关注业务逻辑,而无需手动管理事务,可以减少代码复杂度和工作量。 可配置性强:事务属性可以通过XML文件、注解等方式进行配置,灵活方便。 易于扩展:可以通过AOP技术轻松地扩展使其支持新的事务策略。

声明式事务存在以下缺点:

限制较大:事务属性需要在方法或类级别进行声明,这可能会导致某些情况下难以满足特定的业务需求。 难以调试:由于事务是在AOP层面进行管理的,因此在调试时可能难以追踪事务管理的具体细节。

2. 编程式事务

编程式事务通常通过API接口实现,开发人员可以在代码中显式地管理事务。 编程式事务的优点包括:

灵活性强:开发人员可以在代码中根据具体业务需要来控制事务的具体范围和属性。 易于调试:由于事务管理在代码层面上实现,因此开发人员可以很容易地追踪事务管理的细节。

编程式事务存在以下缺点:

代码复杂度高:需要在代码中手动处理事务,并处理各种异常情况,可能会增加代码的复杂度和工作量。 可配置性差:事务的范围和属性需要在代码中显式声明,这可能会导致一些特定的业务需求难以满足。

总之,声明式事务和编程式事务各有优缺点。开发人员需要根据具体业务需求和场景选择使用合适的事务管理方式。

使用场景

声明式事务通常适用于以下场景:

  • 大型企业级应用程序,需要管理多个事务。
  • 代码结构比较复杂,使用声明式事务可以更好地管理和维护代码(大事务参考上方的方案)。
  • 声明式事务可以将事务管理与业务逻辑分离,从而使得应用程序更加松耦合。

而编程式事务通常适用于以下场景:

  • 需要更精确地控制事务的范围和处理逻辑。
  • 编程式事务通常比声明式事务更加灵活,可以根据业务逻辑的需要来自定义事务的范围、隔离级别以及回滚机制等。
  • 在某些高并发场景下,可以使用编程式事务仅针对需要操作的数据进行锁定,而不是对整个业务逻辑加事务。

在实际场景中,可以根据需求综合考虑使用声明式事务和编程式事务的优势来进行选择。

大事务时间过长可能会导致以下问题:

数据库锁定:当事务涉及到大量的数据操作时,事务可能会占用数据库资源并长时间锁定相关数据。这可能会导致其他事务无法访问或修改这些数据,从而降低系统的并发性能和吞吐量。

资源耗尽:长时间运行的事务需要占用更多的系统资源,如内存和CPU等。如果系统资源不足,可能会导致系统出现延迟、死锁等问题,甚至导致系统崩溃。

事务失败概率增加:当事务时间过长时,事务执行期间可能会发生各种错误,如网络故障、硬件故障、操作系统问题等。此时,事务可能无法成功提交,导致数据丢失或数据不一致。

应用程序超时:应用程序通常会为每个事务设置一个超时时间,以避免事务持续时间过长。如果事务持续时间超过设定的超时时间,则应用程序可能会因为等待事务完成而阻塞,最终导致应用程序崩溃或超时。

回滚时间增加:如果事务失败需要回滚,长时间运行的事务将需要更长的时间来进行回滚操作。这可能会导致数据不一致或丢失,并增加数据库维护的工作量。 因此,开发人员应该尽量避免事务时间过长,合理地设置事务范围、优化事务操作方式以及减少数据访问次数等措施,以提高系统的并发性能和吞吐量。

大事务可以拆分小的事务,一下查询方面的可以提取出来,操作数据库的抽离出来专门加上事务。 也可以使用CompletableFuture组合式异步编排来解决大事务的问题

PlatformTransactionManager 实现类

实现类说明适用场景
DataSourceTransactionManager基于 JDBC 的事务管理器适用于使用 JdbcTemplate、MyBatis(无事务管理器集成)等场景
JpaTransactionManager基于 JPA 的事务管理器适用于使用 JPA(如 Hibernate EntityManager)的项目
HibernateTransactionManager基于 Hibernate 的事务管理器(非 JPA)使用 Hibernate 的老项目中(非 JPA 模式)
JtaTransactionManager基于 JTA 的分布式事务管理器支持全局事务,适用于分布式系统或多个资源(多个数据库/消息队列)参与的事务
ChainedTransactionManager链式事务管理器(非 Spring 官方)第三方实现,可组合多个事务管理器,比如同时操作多个数据库
ReactiveTransactionManager响应式事务管理器(WebFlux)用于响应式编程环境下(如 R2DBC)

如果你用的是 Spring Boot + MyBatis/MySQL,默认就是 DataSourceTransactionManager

如果用的是 Spring Boot + JPA,则默认是 JpaTransactionManager

可以通过 @EnableTransactionManagement 启用事务支持。

需要更灵活控制时可使用编程式事务并手动注入对应的 PlatformTransactionManager

@Transactional 注解

  • 作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
  • 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
  • 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效。

原理

@Transactional 是一个 AOP(面向切面编程) 注解,Spring 在运行时使用 代理对象(JDK 动态代理 或 CGLIB 代理) 来包裹你的业务方法,拦截调用并在方法执行前后进行如下处理:

  • 在方法调用前开启事务;
  • 方法执行成功则提交事务;
  • 方法抛出异常则回滚事务。

属性

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

传播行为(Propagation)

propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:

  • Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
  • Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
  • Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )
  • Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
  • Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
  • Propagation.NESTED :和 Propagation.REQUIRED 效果一样。

隔离级别(Isolation)

isolation :事务的隔离级别,默认值为 Isolation.DEFAULT

枚举值说明可能问题
DEFAULT使用数据库默认隔离级别(MySQL 默认为 REPEATABLE_READ-
READ_UNCOMMITTED最低级,允许读取未提交数据脏读、不可重复读、幻读
READ_COMMITTED允许读取已提交数据(Oracle 默认)不可重复读、幻读
REPEATABLE_READ可重复读(MySQL 默认)幻读
SERIALIZABLE最严格,完全串行执行无并发问题,但性能最差

回滚机制

Spring 默认只对 运行时异常(RuntimeException)和 Error 进行事务回滚,对受检异常(checked exception) 不会回滚

属性说明
rollbackFor指定哪些异常类触发回滚
noRollbackFor指定哪些异常类不触发回滚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值