1. AOP概述


AOP的作⽤:在程序运⾏期间在不修改源代码的基础上对已有⽅法进⾏增强(⽆侵⼊性: 解耦).
接下来我们来看Spring AOP如何来实现 .
2.Spring AOP快速⼊⻔
2.1 引⼊AOP依赖
< dependency >< groupId >org.springframework.boot</ groupId >< artifactId >spring-boot-starter-aop</ artifactId ></ dependency >
2.2 编写AOP程序
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class TimeAspect {
/**
* 记录⽅法耗时
*/
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
//记录⽅法执⾏开始时间
long begin = System.currentTimeMillis();
//执⾏原始⽅法
Object result = pjp.proceed();
//记录⽅法执⾏结束时间
long end = System.currentTimeMillis();
//记录⽅法执⾏耗时
log.info(pjp.getSignature() + "执⾏耗时: {}ms", end - begin);
return result;
}
}


3. Spring AOP 详解
3.1 Spring AOP核⼼概念
3.1.1 切点(Pointcut)

3.1.2 连接点(Join Point)
package com.example.demo.controller;
@RequestMapping("/book")
@RestController
public class BookController {
@RequestMapping("/addBook")
public Result addBook(BookInfo bookInfo) {
//...代码省略
}
@RequestMapping("/queryBookById")
public BookInfo queryBookById(Integer bookId){
//...代码省略}
@RequestMapping("/updateBook")
public Result updateBook(BookInfo bookInfo) {
//...代码省略
}
}
切点和连接点的关系连接点是满⾜切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合.⽐如:切点表达式: ⽐特全体教师连接点就是: 张三,李四等各个⽼师
3.1.3 通知(Advice)

3.1.4 切⾯(Aspect)

3.2 通知类型




3.3 @PointCut
@Slf4j
@Aspect
@Component
public class AspectDemo {
//定义切点(公共的切点表达式)
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
private void pt(){}
//前置通知
@Before("pt()")
public void doBefore() {
//...代码省略
}
//后置通知
@After("pt()")
public void doAfter() {
//...代码省略
}
//返回后通知
@AfterReturning("pt()")
public void doAfterReturning() {
//...代码省略
}
//抛出异常后通知
@AfterThrowing("pt()")
public void doAfterThrowing() {
//...代码省略
}
//添加环绕通知
@Around("pt()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//...代码省略
}
}
@Slf4j
@Aspect
@Component
public class AspectDemo2 {
//前置通知
@Before("com.example.demo.aspect.AspectDemo.pt()")
public void doBefore() {
log.info("执⾏ AspectDemo2 -> Before ⽅法");
}
}
3.4 切⾯优先级 @Order
为防⽌⼲扰, 我们把AspectDemo这个切⾯先去掉(把 @Component 注解去掉就可以 )为简单化, 只写了 @Before 和 @After 两个通知
@Component
public class AspectDemo2 {
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
private void pt(){}
//前置通知
@Before("pt()")
public void doBefore() {
log.info("执⾏ AspectDemo2 -> Before ⽅法");
}
//后置通知
@After("pt()")
public void doAfter() {
log.info("执⾏ AspectDemo2 -> After ⽅法");
}
}
@Component
public class AspectDemo3 {
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
private void pt(){}
//前置通知
@Before("pt()")
public void doBefore() {
log.info("执⾏ AspectDemo3 -> Before ⽅法");
}
//后置通知
@After("pt()")
public void doAfter() {
log.info("执⾏ AspectDemo3 -> After ⽅法");
}
}
@Component
public class AspectDemo4 {
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
private void pt(){}
//前置通知
@Before("pt()")
public void doBefore() {
log.info("执⾏ AspectDemo4 -> Before ⽅法");
}
//后置通知
@After("pt()")
public void doAfter() {
log.info("执⾏ AspectDemo4 -> After ⽅法");
}
}
@Aspect@Component@Order(2)public class AspectDemo2 {//... 代码省略}
@Aspect@Component@Order(1)public class AspectDemo3 {//... 代码省略}
@Aspect@Component@Order(3)public class AspectDemo4 {//... 代码省略}
重新运⾏程序, 访问接⼝ http://127.0.0.1:8080/test/t1
@Aspect
@Component
@Order(2)
public class AspectDemo2 {
//...代码省略
}
@Aspect
@Component
@Order(1)
public class AspectDemo3 {
//...代码省略
}
@Aspect
@Component
@Order(3)
public class AspectDemo4 {
//...代码省略
}

3.5 切点表达式
3.5.1 execution表达式
execution(< 访问修饰符 > < 返回类型 > < 包名 . 类名 . ⽅法 ( ⽅法参数 )> < 异常 >)
其中: 访问修饰符和异常可以省略
切点表达式⽀持通配符表达:
execution(public String com.example.demo.controller.TestController.t1())
省略访问修饰符
execution(String com.example.demo.controller.TestController.t1())
匹配所有返回类型
execution(* com.example.demo.controller.TestController.t1())
匹配TestController 下的所有⽆参⽅法
execution(* com.example.demo.controller.TestController.*())
匹配TestController 下的所有⽅法
execution(* com.example.demo.controller.TestController.*(..))
匹配controller包下所有的类的所有⽅法
execution(* com.example.demo.controller.*.*(..))
匹配所有包下⾯的TestController
execution(* com..TestController.*(..))
匹配com.example.demo包下, ⼦孙包下的所有类的所有⽅法
execution(* com.example.demo..*(..))
3.5.2 @annotation
@RequestMapping("/test")
@RestController
public class TestController {
@RequestMapping("/t1")
public String t1() {
return "t1";
}@RequestMapping("/t2")
public boolean t2() {
return true;
}
}
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/u1")
public String u1(){
return "u1";
}
@RequestMapping("/u2")
public String u2(){
return "u2";
}
}
3.5.2.1 ⾃定义注解 @MyAspect

package com.example.s20241022; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAspect { }
代码简单说明, 了解即可. 不做过多解释
3.5.2.2 切⾯类
@Slf4j
@Component
@Aspect
public class MyAspectDemo {
//前置通知
@Before("@annotation(com.example.demo.aspect.MyAspect)")
public void before(){
log.info("MyAspect -> before ...");
}
//后置通知
@After("@annotation(com.example.demo.aspect.MyAspect)")
public void after(){
log.info("MyAspect -> after ...");
}
}
3.5.2.3 添加⾃定义注解
@MyAspect
@RequestMapping("/t1")
public String t1() {
return "t1";
}
@MyAspect
@RequestMapping("/u1")
public String u1(){
return "u1";
}


4.Spring AOP 原理
4.1 代理模式



4.1.1 静态代理
public interface HouseSubject {void rentHouse ();}
2. 实现接⼝(房东出租房⼦)
public class RealHouseSubject implements HouseSubject{
@Overridepublic void rentHouse () {System.out.println( " 我是房东 , 我出租房⼦ " );}}
3. 代理(中介, 帮房东出租房⼦)
public class HouseProxy implements HouseSubject {// 将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy (HouseSubject houseSubject) {this .houseSubject = houseSubject;}@Overridepublic void rentHouse () {// 开始代理System.out.println( " 我是中介 , 开始代理 " );// 代理房东出租房⼦houseSubject.rentHouse();// 代理结束System.out.println( " 我是中介 , 代理结束 " );}}
4. 使⽤
public class StaticMain {public static void main (String[] args) {HouseSubject subject = new RealHouseSubject ();// 创建代理类HouseProxy proxy = new HouseProxy (subject);// 通过代理类访问⽬标⽅法proxy.rentHouse();}}
运⾏结果:
public interface HouseSubject {void rentHouse ();void saleHouse ();}
2. 接⼝实现修改
public class RealHouseSubject implements HouseSubject {@Overridepublic void rentHouse () {System.out.println( " 我是房东 , 我出租房⼦ " );}@Overridepublic void saleHouse () {System.out.println( " 我是房东 , 我出售房⼦ " );}}
3. 代理类修改
public class HouseProxy implements HouseSubject {// 将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy (HouseSubject houseSubject) {this .houseSubject = houseSubject;}@Overridepublic void rentHouse () {// 开始代理System.out.println( " 我是中介 , 开始代理 " );// 代理房东出租房⼦houseSubject.rentHouse();// 代理结束System.out.println( " 我是中介 , 代理结束 " );}@Overridepublic void saleHouse () {// 开始代理System.out.println( " 我是中介 , 开始代理 " );// 代理房东出租房⼦houseSubject.saleHouse();// 代理结束System.out.println( " 我是中介 , 代理结束 " );}}
4.1.2 动态代理技术
⽐如房屋中介, 我不需要提前预测都有哪些业务, ⽽是业务来了我再根据情况创建.
动态代理在我们⽇常开发中使⽤的相对较少,但是在框架中⼏乎是必⽤的⼀⻔技术. 学会了动态代理之后, 对于我们理解和学习各种框架的原理也⾮常有帮助.
实现 InvocationHandler 接⼝
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class JDKInvocationHandler implements InvocationHandler {// ⽬标对象即就是被代理对象private Object target;public JDKInvocationHandler (Object target) {this .target = target;}@Overridepublic Object invoke (Object proxy, Method method, Object[] args) throws Thro// 代理增强内容System.out.println( " 我是中介 , 开始代理 " );// 通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);// 代理增强内容System.out.println( " 我是中介 , 代理结束 " );return retVal;}}
创建⼀个代理对象并使⽤
public class DynamicMain {public static void main (String[] args) {HouseSubject target= new RealHouseSubject ();// 创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class []{HouseSubject.class},new JDKInvocationHandler (target));proxy.rentHouse();}}
代码简单讲解
public interface InvocationHandler {/*** 参数说明* proxy :代理对象* method :代理对象需要实现的⽅法,即其中需要重写的⽅法* args : method 所对应⽅法的参数*/public Object invoke (Object proxy, Method method, Object[] args)throws Throwable;}
通过实现 InvocationHandler 接⼝, 可以对被代理对象的⽅法进⾏功能增强.
2. Proxy
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException
{
//...代码省略
}
< dependency >< groupId >cglib</ groupId >< artifactId >cglib</ artifactId >< version >3.3.0</ version ></ dependency >
import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CGLIBInterceptor implements MethodInterceptor {// ⽬标对象 , 即被代理对象private Object target;public CGLIBInterceptor (Object target){this .target = target;}@Overridepublic Object intercept (Object o, Method method, Object[] objects,MethodProxy methodProxy) throws Throwable {// 代理增强内容System.out.println( " 我是中介 , 开始代理 " );// 通过反射调⽤被代理类的⽅法Object retVal = methodProxy.invoke(target, objects);// 代理增强内容System.out.println( " 我是中介 , 代理结束 " );return retVal;}}
创建代理类, 并使⽤
public class DynamicMain {public static void main (String[] args) {HouseSubject target= new RealHouseSubject ();HouseSubject proxy= (HouseSubject)Enhancer.create(target.getClass(), new CGLIBInterceptor (target));proxy.rentHouse();}}
public interface MethodInterceptor extends Callback {/*** 参数说明 :* o: 被代理的对象* method: ⽬标⽅法 ( 被拦截的⽅法 , 也就是需要增强的⽅法 )* objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法*/Object intercept (Object o, Method method, Object[] objects, MethodProxymethodProxy) throws Throwable;}
2. Enhancer.create()
Enhancer.create() ⽤来⽣成⼀个代理对象
cer.create()Enhancer.create() ⽤来⽣成⼀个代理对象
4.2 Spring AOP 源码剖析(了解)
Spring 源码过于复杂, 我们只摘出⼀些主要内容, 以了解为主
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)
this.beanFactory, beanName, beanClass);
}
//创建代理⼯⼚
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
/**
* 检查proxyTargetClass属性值,spring默认为false
* proxyTargetClass 检查接⼝是否对类代理, ⽽不是对接⼝代理
* 如果代理对象为类, 设置为true, 使⽤cglib代理
*/
if (!proxyFactory.isProxyTargetClass()) {
//是否有设置cglib代理
if (shouldProxyTargetClass(beanClass, beanName)) {
//设置proxyTargetClass为true,使⽤cglib代理
proxyFactory.setProxyTargetClass(true);
} else {
/**
* 如果beanClass实现了接⼝,且接⼝⾄少有⼀个⾃定义⽅法,则使⽤JDK代理
* 否则CGLIB代理(设置ProxyTargetClass为true )
* 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其
设为true
*/
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Use original ClassLoader if bean class not locally loaded in overriding
class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader !=
beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
//从代理⼯⼚中获取代理
return proxyFactory.getProxy(classLoader);
}

可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置注意:Spring Boot 2.X开始, 默认使⽤CGLIB代理可以通过配置项 spring.aop.proxy-target-class=false 来进⾏修改,设置默认为jdk代理SpringBoot设置 @EnableAspectJAutoProxy ⽆效, 因为Spring Boot 默认使⽤AopAutoConfiguration进⾏装配
@SpringBootApplicationpublic class DemoApplication {public static void main (String[] args) {ApplicationContext context =SpringApplication.run(DemoApplication.class, args);/*** HouseProxy houseProxy = context.getBean(HouseProxy.class);* 设置 spring.aop.proxy-target-class=true cglib 代理 , 运⾏成功* 设置 spring.aop.proxy-target-class=false jdk 代理 , 运⾏失败 , 不能代理类* 因为 HouseProxy 是⼀个类 , ⽽不是接⼝ , 需要修改为* HouseSubject houseProxy = (HouseSubject)context.getBean("realHouseSubject")**/HouseProxy houseProxy = context.getBean(HouseProxy.class);//HouseSubject houseProxy = (HouseSubject)context.getBean("realHouseSubject");// 正确运⾏System.out.println(houseProxy.getClass().toString());}}
public class ProxyFactory extends ProxyCreatorSupport {//... 代码省略// 获取代理public Object getProxy ( @Nullable ClassLoader classLoader) {// 分两步 先 createAopProxy, 后 getProxyreturn createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy () {if (! this .active) {activate();}return getAopProxyFactory().createAopProxy( this );}//... 代码省略}
createAopProxy的实现在 DefaultAopProxyFactory中
public class DefaultAopProxyFactory implements AopProxyFactory , Serializable {//... 代码省略@Overridepublic AopProxy createAopProxy (AdvisedSupport config) throwsAopConfigException {/*** 根据 proxyTargetClass 判断* 如果⽬标类是接⼝ , 使⽤ JDK 动态代理* 否则使⽤ cglib 动态代理*/if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() ||hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null ) {throw new AopConfigException ( "TargetSource cannot determinetarget class: " +"Either an interface or a target is required for proxycreation." );}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) ||ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy (config);}return new ObjenesisCglibAopProxy (config);}else {return new JdkDynamicAopProxy (config);}}//... 代码省略}
final class JdkDynamicAopProxy implements AopProxy , InvocationHandler,Serializable {//... 代码省略@Overridepublic Object getProxy ( @Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace( "Creating JDK dynamic proxy: " +this .advised.getTargetSource());}return Proxy.newProxyInstance(determineClassLoader(classLoader),this .proxiedInterfaces, this );}//... 代码省略}
CGLIB动态代理
class CglibAopProxy implements AopProxy , Serializable {//... 代码省略@Overridepublic Object getProxy ( @Nullable ClassLoader classLoader) {//... 代码省略// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}//... 代码省略}