跳转至

Spring AOP

约 4161 个字 959 行代码 6 张图片 预计阅读时间 26 分钟

AOP 切点表达式和使用以及基于 JDKCGLIB 的动态代理类关系:

Joinpoint,织入点,指需要执行代理操作的某个类的某个方法(仅支持方法级别的JoinPoint);PointcutJoinPoint的表述方式,能捕获JoinPoint

最常用的切点表达式是AspectJ的切点表达式。

  • 需要匹配类,定义ClassFilter接口
  • 匹配方法,定义MethodMatcher接口

PointCut需要同时匹配类和方法,包含ClassFilterMethodMatcher, AspectJExpressionPointcut是支持AspectJ切点表达式的PointCut实现,简单实现仅支持execution函数。

AdvisedSupport 是一个核心类,用于支持和管理代理对象的配置和功能

AdvisedSupport 的主要功能是:

  1. 存储 AOP 配置元信息:
    • 目标对象(被代理对象)。
    • 代理的接口列表。
    • 应用的拦截器链或通知(Advices)。
  2. 管理代理行为:
    • 决定代理是 JDK 动态代理还是 CGLIB 动态代理。
    • 负责控制代理对象的生命周期。
  3. 支持运行时动态修改:
    • 可以在运行时动态添加或移除通知(Advice)。

JDK动态代理

AopProxy是获取代理对象的抽象接口,JdkDynamicAopProxy的基于JDK动态代理的具体实现。

TargetSource,被代理对象的封装。

MethodInterceptor,方法拦截器,是AOP Alliance的"公民",顾名思义,可以拦截方法,可在被代理执行的方法前后增加代理行为。

测试代码
Java
public class AopTest {

    @Test
    public void testJdkDynamicProxy() throws Exception {
        WorldService worldService = new WorldServiceImpl();

        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(worldService);
        WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
        MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* test.aop.bean.WorldService.explode(..))").getMethodMatcher();
        advisedSupport.setTargetSource(targetSource);
        advisedSupport.setMethodInterceptor(methodInterceptor);
        advisedSupport.setMethodMatcher(methodMatcher);

        WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        proxy.explode();
    }
}
Java
1
2
3
4
5
6
7
8
9
public class WorldServiceInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("do something before the Earth explodes");
        Object res = methodInvocation.proceed();
        System.out.println("do something after the Earth explodes");
        return res;
    }
}

1. JDK动态代理源码

代码位置:com.iflove.simplespring.aop.framework.JdkDynamicAopProxy

Java
@Override
public Object getProxy() {
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
        // 启动代理方法 
        MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
        return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
    }
    return method.invoke(advised.getTargetSource().getTarget(), args);
}

重点关注以上两个核心方法

  1. getProxy()
    • getProxy() 方法用于创建代理对象,返回一个基于 Java 动态代理的代理实例。
  2. invoke()
    • invoke() 方法用于处理代理对象上的方法调用,判断是否需要应用代理逻辑。如果方法匹配切面条件,则执行增强逻辑;否则直接调用目标方法。

2. 运行测试代码

在测试代码的末尾加上

Java
System.out.println(proxy.getClass());
while (true) {}

保证程序始终运行

下载 arthas 并启动

Bash
java -jar arthas-boot.jar

选择对应进程

输入

Bash
jad com.sun.proxy.$Proxy2

获得代理类信息如下:

Java
/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  test.aop.bean.WorldService
 */
package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import test.aop.bean.WorldService;

public final class $Proxy2
extends Proxy
implements WorldService {
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    public $Proxy2(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("test.aop.bean.WorldService").getMethod("explode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke(this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void explode() {
        try {
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

可以看到代理类信息如下 public final class $Proxy2 extends Proxy implements WorldService

继承了 Proxy,实现了 WorldService 接口

上述信息印证了一个重要的概念,即:

知识点

JDK 动态代理生成的代理类和代理对象为兄弟关系,需要一个共同的接口

接下来看代理类实现的 explode 方法

Java
public final void explode() {
    try {
        this.h.invoke(this, m3, null);
        return;
    }
    catch (Error | RuntimeException throwable) {
        throw throwable;
    }
    catch (Throwable throwable) {
        throw new UndeclaredThrowableException(throwable);
    }
}

其他都很好理解,问题是:这里的 h 是什么?

先给出解答:hInvocationHandler

接下来,一步一步从源码角度分析

首先,这个代理类是通过 JdkDynamicAopProxygetProxy 方法获取的

定义如下

Java
1
2
3
4
@Override
public Object getProxy() {
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this);
}

以及 Proxy.newProxyInstance 的方法签名

Java
1
2
3
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

可以看到在 getProxy 方法中返回代理对象的最后一个参数为 this,即 JdkDynamicAopProxy 本身,而 JdkDynamicAopProxy 这个类实现了 InvocationHandler 接口,所以在动态生成的代理类中调用的 this.h.invoke 其实就是 JdkDynamicAopProxyinvoke 方法。


最后再来看 invoke 方法

Java
1
2
3
4
5
6
7
8
9
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
        // 启动代理方法
        MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
        return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
    }
    return method.invoke(advised.getTargetSource().getTarget(), args);
}

核心逻辑如下:

  1. 方法匹配:

    • 调用 advised.getMethodMatcher().matches(...) 判断当前方法是否符合切面条件。
    • 如果匹配,执行代理逻辑;否则直接调用目标方法。
  2. 代理逻辑:

    • 获取 MethodInterceptor 对象,封装了增强逻辑。
    • 调用 MethodInterceptor.invoke(...),传入一个ReflectiveMethodInvocation 对象。
    • ReflectiveMethodInvocationSpring AOP 的核心类,用于封装方法调用的上下文(目标对象、方法、参数等)。
  3. 直接调用目标方法:

    • 如果方法不匹配代理条件,直接调用目标对象的方法,使用反射机制。

Cglib 动态代理

与基于JDK的动态代理在运行期间为接口生成对象的代理对象不同

基于CGLIB的动态代理能在运行期间动态构建字节码的class文件,为类生成子类,因此被代理类不需要继承自任何接口

重要概念

cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系

测试代码
Java
private AdvisedSupport advisedSupport;

@Before
public void setup() {
    WorldService worldService = new WorldServiceImpl();

    advisedSupport = new AdvisedSupport();
    TargetSource targetSource = new TargetSource(worldService);
    WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
    MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* test.aop.bean.WorldService.explode(..))").getMethodMatcher();
    advisedSupport.setTargetSource(targetSource);
    advisedSupport.setMethodInterceptor(methodInterceptor);
    advisedSupport.setMethodMatcher(methodMatcher);
}
Java
1
2
3
4
5
@Test
public void testCglibDynamicProxy1() throws Exception {
    WorldService proxy = (WorldService) new CglibAopProxy(advisedSupport).getProxy();
    proxy.explode();
}
Java
1
2
3
4
5
@Test
public void testCglibDynamicProxy2() throws Exception {
    WorldServiceImpl proxy = (WorldServiceImpl) new CglibAopProxy(advisedSupport).getProxy();
    proxy.explode();
}

运行上述代码均无报错,即验证了 代理与目标之间是子父关系

1. Cglib 动态代理源码

代码位置:com.iflove.simplespring.aop.framework.CglibAopProxy

Java
public class CglibAopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        Class<?> aClass = advised.getTargetSource().getTarget().getClass();
        aClass = ClassUtils.isCglibProxyClass(aClass)? aClass.getSuperclass() : aClass;
        enhancer.setSuperclass(aClass);
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
            if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
                return advised.getMethodInterceptor().invoke(methodInvocation);
            }
            return methodInvocation.proceed();
        }
    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target, method, arguments);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return this.methodProxy.invoke(this.target, this.arguments);
        }

    }
}

JDK 动态代理不同的是,Cglib 动态代理拥有两个特别的内部类 DynamicAdvisedInterceptorCglibMethodInvocation

  • CglibMethodInvocation 扩展 ReflectiveMethodInvocation 类以支持 CGLIB 代理
  • 主要是覆盖了 invokeJoinpoint() 方法,如果有 MethodProxy 对象,则通过调用 MethodProxy#invoke 方法,否则通过反射调用

那么问题来了,methodProxy 究竟是什么?不使用 methodProxy 行不行?

首先回答 不使用 methodProxy 行不行? 这个问题

答案是 可以!

如果把 proceed 方法中的 methodProxy 替换为 method,代理依然生效,且不会报错

接下来,从源码角度分析 methodProxy 究竟是什么?


2. CGLIB 动态代理原理与性能优化

MethodProxy 是 CGLIB 中一个关键的类,用于执行代理方法。它通过 ASM 字节码框架生成动态代理类,以提高方法调用的性能。与传统的反射方式相比,MethodProxy 能避免频繁反射操作,从而优化性能。

进入 proceed 方法中的 invoke 方法,源码如下

Java
public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        FastClassInfo fci = this.fastClassInfo;
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException var4) {
        InvocationTargetException e = var4;
        throw e.getTargetException();
    } catch (IllegalArgumentException var5) {
        IllegalArgumentException e = var5;
        if (this.fastClassInfo.i1 < 0) {
            throw new IllegalArgumentException("Protected method: " + this.sig1);
        } else {
            throw e;
        }
    }
}

继续进入该方法下的 init 方法

Java
private void init() {
    if (this.fastClassInfo == null) {
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                CreateInfo ci = this.createInfo;
                FastClassInfo fci = new FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(this.sig1);
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }

}

init 方法下,有两个特殊的类 f1f2,这两个其实就是 FastClass

将源码拉到底部,可以看到 FastClass 定义如下:

Java
1
2
3
4
5
6
7
8
9
private static class FastClassInfo {
    FastClass f1;
    FastClass f2;
    int i1;
    int i2;

    private FastClassInfo() {
    }
}
  • F1 表示已经创建的代理类,包含代理逻辑
  • F2 表示原始类的方法封装,加速原生类方法调用

无论是代理类还是原生的类, 在创建代理、执行代理内部的一些方法以及调用一些值的时候, 如果要使用到原生类中的方法的话,是需要通过反射的方式去执行的。

可以往极端一点的方向思考,如果原生类中被调用的方法非常多的话,会导致 java 进行频繁的反射,而使用反射,又会导致 JVM 机制进行反射的检查,所以这样做是相当牺牲性能的。

为了解决这个问题,Cglib 创建了一个 MethodProxy 类,将原生类中所有需要代理的方法存入这个类中,那么在需要使用的时候,直接执行即可,相当于一个缓存的作用,好处就是可以提高性能


MethodProxy 类中还有一个特别的方法 invokeSuper,这个方法代表直接调用原始类的方法

Java
methodProxy.invokeSuper(this.target, this.arguments);

那么顾名思义,invoke 方法,就是调用代理类的代理方法


3. Cglib 动态代理类

类似上文获取 JDK 动态代理类的方法

在测试代码的末尾加上

Java
System.out.println(proxy.getClass());
while (true) {}

保证程序始终运行

下载 arthas 并启动

Bash
java -jar arthas-boot.jar

获取动态代理类代码如下(有点小长):

动态代理类
Java
/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  test.aop.bean.WorldService
 *  test.aop.bean.WorldServiceImpl
 */
package test.aop.bean;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import test.aop.bean.WorldService;
import test.aop.bean.WorldServiceImpl;

public class WorldServiceImpl$$EnhancerByCGLIB$$81790592
extends WorldServiceImpl
implements WorldService,
Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$explode$0$Method;
    private static final MethodProxy CGLIB$explode$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    public WorldServiceImpl$$EnhancerByCGLIB$$81790592() {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = this;
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(worldServiceImpl$$EnhancerByCGLIB$$81790592);
    }

    static {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$STATICHOOK1();
    }

    public final boolean equals(Object object) {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object2 = methodInterceptor.intercept(this, CGLIB$equals$1$Method, new Object[]{object}, CGLIB$equals$1$Proxy);
            return object2 == null ? false : (Boolean)object2;
        }
        return super.equals(object);
    }

    public final String toString() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            return (String)methodInterceptor.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
        }
        return super.toString();
    }

    public final int hashCode() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return object == null ? 0 : ((Number)object).intValue();
        }
        return super.hashCode();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            return methodInterceptor.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
        }
        return super.clone();
    }

    @Override
    public Object newInstance(Callback[] callbackArray) {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = new WorldServiceImpl$$EnhancerByCGLIB$$81790592();
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(null);
        return worldServiceImpl$$EnhancerByCGLIB$$81790592;
    }

    @Override
    public Object newInstance(Callback callback) {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = new WorldServiceImpl$$EnhancerByCGLIB$$81790592();
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(null);
        return worldServiceImpl$$EnhancerByCGLIB$$81790592;
    }

    @Override
    public Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592;
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
        Class[] classArray2 = classArray;
        switch (classArray.length) {
            case 0: {
                worldServiceImpl$$EnhancerByCGLIB$$81790592 = new WorldServiceImpl$$EnhancerByCGLIB$$81790592();
                break;
            }
            default: {
                throw new IllegalArgumentException("Constructor not found");
            }
        }
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$SET_THREAD_CALLBACKS(null);
        return worldServiceImpl$$EnhancerByCGLIB$$81790592;
    }

    public final void explode() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$explode$0$Method, CGLIB$emptyArgs, CGLIB$explode$0$Proxy);
            return;
        }
        super.explode();
    }

    @Override
    public Callback[] getCallbacks() {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = this;
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class<?> clazz = Class.forName("test.aop.bean.WorldServiceImpl$$EnhancerByCGLIB$$81790592");
        Class<?> clazz2 = Class.forName("test.aop.bean.WorldServiceImpl");
        CGLIB$explode$0$Method = ReflectUtils.findMethods(new String[]{"explode", "()V"}, clazz2.getDeclaredMethods())[0];
        CGLIB$explode$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "explode", "CGLIB$explode$0");
        clazz2 = Class.forName("java.lang.Object");
        Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());
        CGLIB$equals$1$Method = methodArray[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = methodArray[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = methodArray[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = methodArray[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    }

    final String CGLIB$toString$2() {
        return super.toString();
    }

    private static final void CGLIB$BIND_CALLBACKS(Object object) {
        block2: {
            Object object2;
            block3: {
                WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = (WorldServiceImpl$$EnhancerByCGLIB$$81790592)object;
                if (worldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BOUND) break block2;
                worldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BOUND = true;
                object2 = CGLIB$THREAD_CALLBACKS.get();
                if (object2 != null) break block3;
                object2 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) break block2;
            }
            worldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];
        }
    }

    final boolean CGLIB$equals$1(Object object) {
        return super.equals(object);
    }

    final void CGLIB$explode$0() {
        super.explode();
    }

    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }

    @Override
    public void setCallback(int n, Callback callback) {
        switch (n) {
            case 0: {
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                break;
            }
        }
    }

    @Override
    public void setCallbacks(Callback[] callbackArray) {
        Callback[] callbackArray2 = callbackArray;
        WorldServiceImpl$$EnhancerByCGLIB$$81790592 worldServiceImpl$$EnhancerByCGLIB$$81790592 = this;
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
        String string = ((Object)signature).toString();
        switch (string.hashCode()) {
            case -508378822: {
                if (!string.equals("clone()Ljava/lang/Object;")) break;
                return CGLIB$clone$4$Proxy;
            }
            case 1741417204: {
                if (!string.equals("explode()V")) break;
                return CGLIB$explode$0$Proxy;
            }
            case 1826985398: {
                if (!string.equals("equals(Ljava/lang/Object;)Z")) break;
                return CGLIB$equals$1$Proxy;
            }
            case 1913648695: {
                if (!string.equals("toString()Ljava/lang/String;")) break;
                return CGLIB$toString$2$Proxy;
            }
            case 1984935277: {
                if (!string.equals("hashCode()I")) break;
                return CGLIB$hashCode$3$Proxy;
            }
        }
        return null;
    }

    final Object CGLIB$clone$4() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public Callback getCallback(int n) {
        MethodInterceptor methodInterceptor;
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
        switch (n) {
            case 0: {
                methodInterceptor = this.CGLIB$CALLBACK_0;
                break;
            }
            default: {
                methodInterceptor = null;
            }
        }
        return methodInterceptor;
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {
        CGLIB$STATIC_CALLBACKS = callbackArray;
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {
        CGLIB$THREAD_CALLBACKS.set(callbackArray);
    }
}

主要关注动态代理后的 explode 方法做了什么?

Java
public final void explode() {
    MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
    if (methodInterceptor == null) {
        WorldServiceImpl$$EnhancerByCGLIB$$81790592.CGLIB$BIND_CALLBACKS(this);
        methodInterceptor = this.CGLIB$CALLBACK_0;
    }
    if (methodInterceptor != null) {
        Object object = methodInterceptor.intercept(this, CGLIB$explode$0$Method, CGLIB$emptyArgs, CGLIB$explode$0$Proxy);
        return;
    }
    super.explode();
}

首先获取 MethodInterceptor,如果不为空,则调用 MethodInterceptorintercept 方法

DynamicAdvisedInterceptor 类的 intercept 方法,执行动态代理的方法

AOP 代理工厂

1. 测试代码

Java
@Test
public void testProxyFactory() {
    // 使用 JDK 动态代理
    advisedSupport.setProxyTargetClass(false);
    WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();

    // 使用 Cglib 动态代理
    advisedSupport.setProxyTargetClass(true);
    proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();
}

2. AOP 代理工厂代码

Java
public class ProxyFactory {
    private AdvisedSupport advisedSupport;

    public ProxyFactory(AdvisedSupport advisedSupport) {
        this.advisedSupport = advisedSupport;
    }

    public Object getProxy() {
        return createAopProxy().getProxy();
    }

    private AopProxy createAopProxy() {
        if (advisedSupport.isProxyTargetClass()) {
            return new CglibAopProxy(advisedSupport);
        }

        return new JdkDynamicAopProxy(advisedSupport);
    }
}

注意到切换 JDKCglib 动态代理之间的关键是 AdvisedSupportproxyTargetClass 参数

  • proxyTargetClassfalse 代表使用 JDK 动态代理
  • proxyTargetClasstrue 代表使用 Cglib 动态代理

AdvisedSupportproxyTargetClass 参数 默认值false,即默认使用 JDK 动态代理

几种常用的Advice:BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice...

SpringAOP 联盟中的 Advice 细化出各种类型的 Advice,常用的有BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice,我们可以通过扩展MethodInterceptor来实现。

测试代码

Java
1
2
3
4
5
6
7
public class WorldServiceBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("BeforeAdvice: do something before the earth explodes");
    }
}
Java
@Test
public void testBeforeAdvice() throws Exception {
    //设置BeforeAdvice
    WorldServiceBeforeAdvice beforeAdvice = new WorldServiceBeforeAdvice();
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(beforeAdvice);
    advisedSupport.setMethodInterceptor(methodInterceptor);

    WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();
}

简单来说,就是通过规范 Interceptor 分类,根据需要的代理时机选择对应的 AdviceInterceptor 即可,相对简单,不过多赘述

Spring AOP 拦截器链

虽然在前面我们完成了对方法的增强,但并不完美。我们的目前的代码只能支持对方法的单个增强。作为spring的核心功能如果不支持多切面的话有点太别扭了。spring利用了拦截器链来完成了对多个切面的支持。

ProxyFactory

让我们从 ProxyFactory 开始,来看一下代理对象的整个创建流程。至于为什么从 ProxyFactory开,这是因为代理对象最终是用 ProxyFactorygetProxy() 函数来获得的。

Java
public class ProxyFactory extends AdvisedSupport{


    public ProxyFactory() {
    }

    public Object getProxy() {
        return createAopProxy().getProxy();
    }

    private AopProxy createAopProxy() {
        if (this.isProxyTargetClass()||this.getTargetSource().getTargetClass().length==0) {
            return new CglibAopProxy(this);
        }
        return new JdkDynamicAopProxy(this);
    }
}

为了更贴合spring的实现,这里更改了 ProxyFactory 使其继承AdvisedSupport,正如spring源码中做的那样。


基于JDK动态代理

ProxyFactory 只是简单的做了下选择,当我们设置 proxyTargetClass 属性或者被代理对象没有接口时会调用 cjlib 动态代理,否则调用 jdk 动态代理。二者实现并没有太大区别,这里只贴出jdk动态代理的实现。

Java
    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取目标对象
        Object target=advised.getTargetSource().getTarget();
        Class<?> targetClass = target.getClass();
        Object retVal = null;
        // 获取拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        if(chain==null||chain.isEmpty()){
            return method.invoke(target, args);
        }else{
            // 将拦截器统一封装成ReflectiveMethodInvocation
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // 执行拦截器链
            retVal = invocation.proceed();
        }
        return retVal;
    }

jdk动态代理可以分为获取拦截器链,将拦截器统一封装成 ReflectiveMethodInvocation,执行拦截器链三部分。我们来逐一看一下这三部分。


1. 获取拦截器链

首先将获取到所有与当前method匹配的advice(增强),跟踪getInterceptorsAndDynamicInterceptionAdvice代码,我们发现Spring AOP也使用缓存进行提高性能,如果该方法已经获取过拦截器,则直接取缓存,否则通过advisorChainFactory获取拦截器链。

AdvisorChainFactory是用来获得拦截器链接口。它的一个实现类为DefaultAdvisorChainFactory

AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice

Java
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        Integer cacheKey=method.hashCode();
        List<Object> cached = this.methodCache.get(cacheKey);
        if (cached == null) {
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }


接下来,对advisorChainFactory获取拦截器链进行解读。

整体代码并不复杂,首先获取所有Advisor(切面),通过pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)校验当前代理对象是否匹配该Advisor,再通过pointcutAdvisor.getPointcut().getMethodMatcher()校验是否匹配当前调用method

如果通过校验,则提取advisor中的interceptors增强,添加到interceptorList中。

这里可能有读者会疑惑,明明是要获取MethodInterceptor,可AdvisedSupportgetAdvice()返回的是Advice(增强),其实如果我们点开MethodInterceptor的源码,我们会发现MethodInterceptor继承了Interceptor接口,而Interceptor又继承了Advice接口。因为这里的AdviceMethodInterceptor我们都是用的AOP联盟的接口,所以特此说明一下。

DefultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice

Java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport config, Method method, Class<?> targetClass) {
        Advisor[] advisors = config.getAdvisors().toArray(new Advisor[0]);
        List<Object> interceptorList = new ArrayList<>(advisors.length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 校验当前Advisor是否适用于当前对象
                if (pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    // 校验Advisor是否应用到当前方法上
                    match = mm.matches(method,actualClass);
                    if (match) {
                        MethodInterceptor interceptor = (MethodInterceptor) advisor.getAdvice();
                            interceptorList.add(interceptor);
                    }
                }
            }
        }
        return interceptorList;
    }
2.将拦截器封装成ReflectiveMethodInvocation

这里也是重写了ReflectiveMethodInvocation的实现,来支持多切面。

Java
1
2
3
4
5
6
7
8
    public ReflectiveMethodInvocation(Object proxy,Object target, Method method, Object[] arguments,Class<?> targetClass,List<Object> chain) {
        this.proxy=proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.targetClass=targetClass;
        this.interceptorsAndDynamicMethodMatchers=chain;
    }
3.执行拦截器链

spring能够保证多个切面同时匹配同一方法的而不出现乱序的关键就在下面一段代码了。

ReflectiveMethodInvocation#proceed()

Java
    public Object proceed() throws Throwable {
        // 初始currentInterceptorIndex为-1,每调用一次proceed就把currentInterceptorIndex+1
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            // 当调用次数 = 拦截器个数时
            // 触发当前method方法
            return method.invoke(this.target, this.arguments);
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        // 普通拦截器,直接触发拦截器invoke方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }

我们看到,MethodInvocation只是简单的将拦截器链的所有拦截器一一执行,最后再触发当前的method方法。

这是很简单高效的方法,但问题是我们希望某些增强比如AfterReturningAdvice能够在方法执行完才被执行,这就涉及到不同增强的执行顺序的问题了。

MethodInvocation显然没有考虑顺序的问题,一个AfterReturningAdvice很可能在BeforeAdvice之前被调用。那么该如何保证顺序问题呢?

答案是,控制增强的调用顺序其实由每个拦截器负责,所以我们需要分析MethodBeforeAdviceInterceptorAfterReturningAdviceInterceptor

Java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice {

    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor() {
    }

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        this.advice = advice;
    }

    public void setAdvice(MethodBeforeAdvice advice) {
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
}
Java
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice {

    private AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor() {
    }

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        this.advice = advice;
    }

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

看了源码大家应该就清楚了,拦截器链执行的顺序正时在各个拦截器的invoke方法中实现的。before先执行advice增强方法再链式调用,而after则是先执行链式调用,再调用advice增强方法,也就是一个递归的过程。和二叉树的遍历有些异曲同工之处。

测试

Java
@Before
public void setup() {
    WorldService worldService = new WorldServiceImpl();
    advisedSupport = new ProxyFactory();
    //Advisor是Pointcut和Advice的组合
    String expression = "execution(* test.aop.bean.WorldService.explode(..))";
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression(expression);
    AfterReturningAdviceInterceptor methodInterceptor = new AfterReturningAdviceInterceptor(new WorldServiceAfterReturnAdvice());
    advisor.setAdvice(methodInterceptor);
    TargetSource targetSource = new TargetSource(worldService);
    advisedSupport.setTargetSource(targetSource);
    advisedSupport.addAdvisor(advisor);
}

@Test
public void testBeforeAdvice() throws Exception {
    //设置BeforeAdvice
    String expression = "execution(* test.aop.bean.WorldService.explode(..))";
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression(expression);
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
    advisor.setAdvice(methodInterceptor);
    advisedSupport.addAdvisor(advisor);
    ProxyFactory factory = (ProxyFactory) advisedSupport;
    WorldService proxy = (WorldService) factory.getProxy();
    proxy.explode();
}

PointcutAdvisor:Pointcut和Advice的组合

Advisor是包含一个Pointcut和一个Advice的组合,Pointcut用于捕获JoinPointAdvice决定在JoinPoint执行某种操作。实现了一个支持aspectj表达式的AspectJExpressionPointcutAdvisor

Java
public class DynamicProxyTest {

    @Test
    public void testAdvisor() throws Exception {
        WorldService worldService = new WorldServiceImpl();

        //Advisor是Pointcut和Advice的组合
        String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
        AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
        advisor.setExpression(expression);
        MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
        advisor.setAdvice(methodInterceptor);

        ClassFilter classFilter = advisor.getPointcut().getClassFilter();
        if (classFilter.matches(worldService.getClass())) {
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = new TargetSource(worldService);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
//          advisedSupport.setProxyTargetClass(true);   //JDK or CGLIB

            WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
            proxy.explode();
        }
    }
}

动态代理融入Bean的生命周期

BeanPostProcessor处理阶段可以修改和替换bean,正好可以在此阶段返回代理对象替换原对象。不过我们引入一种特殊的BeanPostProcessor——InstantiationAwareBeanPostProcessor,织入逻辑位于BeanPostProcessor#postProcessAfterInitialization,具体实现查看AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation

DefaultAdvisorAutoProxyCreator是处理横切逻辑的织入返回代理对象的InstantiationAwareBeanPostProcessor实现类,当对象实例化时,生成代理对象并返回。

至此,bean的生命周期如下

  1. 读取xml文件
  2. load beandefinition
  3. BeanFactoryProcessor 修改 BeanDefinition
  4. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
  5. bean实例化
  6. BeanFactoryAware(setBeanFactory, setBeanName, setBeanClassLoader)
  7. BeanPostProcessor前置处理 (例:ApplicationContextAwareProcessor)
  8. 执行 bean 的初始化方法
    1. InitializingBean#afterPropertiesSet
    2. 自定义初始化方法init-method
  9. BeanPostProcessor后置处理(InstantiationAwareBeanPostProcessor#postProcessAfterInitialization创建代理对象返回)
  10. 如果是DisposableBean注册销毁方法
  11. 判断scope
    1. 如果是singleton,放入缓存
    2. 如果是prototype,过
  12. 使用
  13. 执行bean的销毁方法(singleton)
    1. DisposableBean#destroy
    2. 自定义销毁方法destroy-metod

@EnableAspectJAutoProxy

spring中我们要开启自动代理的功能需要在配置类添加如下的注解:

Java
@EnableAspectJAutoProxy
public class AppConfiguration {}

当然在xml中配置也可:

XML
<aop:aspectj-autoproxy />

注解源码如下:

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}

在该注解中我们可以配置proxyTargetClassexposeProxy两个重要属性,它们分别控制着Spring如何创建和使用代理对象来实现切面功能。

proxyTargetClass

用于指示Spring AOP应使用哪种类型的代理来织入切面:

  • proxyTargetClass = true: 指示Spring使用CGLIB库生成基于类的代理。这意味着Spring会创建目标类的子类,并在其中插入切面逻辑。这种代理方式可以拦截类的所有方法(包括非接口方法和final方法),适用于那些没有接口或者需要对非公共方法进行增强的场景。

  • proxyTargetClass = false 或未指定(默认情况): Spring将使用标准的JDK动态代理技术,即创建一个实现了目标类所有接口的新代理对象,并在代理对象的方法调用中插入切面逻辑。这种方式仅能用于代理实现了至少一个接口的目标类,且只能拦截通过接口声明的方法

exposeProxy

用于控制代理对象是否应被公开给被代理对象的内部方法访问:

  • exposeProxy = true: 当设置为true时,Spring会确保在AOP代理对象上下文中,通过 AopContext.currentProxy() 方法能够获取到当前正在执行的代理对象。 这对于在被代理对象内部需要调用自身其他方法,并希望这些内部方法调用也能触发切面逻辑的情况非常有用。 例如,一个服务类中的某个方法可能需要调用另一个私有受保护的方法,而这两个方法都被同一个切面所增强。在这种情况下,若不暴露代理,内部方法调用将不会经过切面处理;而暴露代理后,可以通过 AopContext.currentProxy().methodToCall() 的方式确保内部方法调用也得到切面的拦截。

  • exposeProxy = false 或未指定(默认情况): 默认情况下,Spring不会特别暴露代理对象,因此在被代理对象内部直接通过 this 调用其他方法时,这些方法调用将不会触发切面逻辑,而是直接调用目标类的原始方法。

关于 exposeProxy,来看一个业务中的实际使用场景 (我的 IM 项目中用到了)

以下是一段处理好友申请的业务逻辑

Java
/**
 * 申请好友
 * @param uid     uid
 * @param request 请求
 * @return {@link RestBean}
 */
@Transactional
@Override
@RedissonLock(key = "#uid")
public RestBean<Void> apply(Long uid, FriendApplyReq request) {
    // 不能添加自己为好友
    if (Objects.equals(uid, request.getTargetUid())) {
        return RestBean.failure(FriendErrorEnum.SELF_APPLY_FORBIDDEN);
    }
    // 判断是否存在好友关系
    UserFriend isFriend = userFriendDao.getByFriend(uid, request.getTargetUid());
    // 已经存在好友关系
    if (Objects.nonNull(isFriend)) {
        return RestBean.failure(FriendErrorEnum.ALREADY_FRIENDS);
    }
    // 判断是否存在申请记录 (我 -> 对方) 且 申请状态为 待审批
    UserApply myFriendApply = userApplyDao.getFriendApply(uid, request.getTargetUid());
    // 存在申请
    if (Objects.nonNull(myFriendApply)) {
        return RestBean.failure(FriendErrorEnum.EXISTS_FRIEND_APPLY);
    }
    // 判断是否存在申请记录 (对方 -> 我)
    UserApply friendApply = userApplyDao.getFriendApply(request.getTargetUid(), uid);
    // 如果存在,直接同意
    if (Objects.nonNull(friendApply)) {
        // 获取当前执行的对象的代理实例(确保事务正确执行),同意申请
        ((FriendService) AopContext.currentProxy()).applyApprove(uid, new FriendApplyApproveReq(friendApply.getId()));
        return RestBean.success();
    }
    UserApply userApply = FriendAdapter.buildFriendApply(uid, request);
    userApplyDao.save(userApply);
    // 用户申请事件,向对方异步发送请求消息
    applicationEventPublisher.publishEvent(new UserApplyEvent(this, userApply));
    return RestBean.success();
}

主要关注业务中处理 如果申请记录(对方->我)存在,则直接同意申请 的处理逻辑

这一行代码的核心是确保事务和其他切面逻辑(如 @Transactional@RedissonLock)在调用 applyApprove 方法时能够生效。

为什么需要通过代理调用?

  • 事务传播问题

    • 在 Spring 中,如果类中的一个方法直接调用同类的另一个方法(比如 apply 调用 this.applyApprove),Spring 的动态代理不会生效
    • 这会导致 applyApprove 方法上的事务注解 @Transactional 被忽略,事务功能失效
  • 代理解决方案:

    • 使用 AopContext.currentProxy() 获取当前类的代理对象
    • 通过代理对象调用方法,保证事务和切面逻辑生效。

AOP 生成代理的三个时机

实例化前(BeforeInstantiation)

讲道理一般不用

具体时机在 createBean 方法上半部分 #resolveBeforeInstantiation,如果 Bean实例化前 被动态代理了,判断 Bean 是否为 null,如果不为 null,则短路并直接返回

无代理则进入下半部分 doCreateBean

Java
/**
 * Apply before-instantiation post-processors, resolving whether there is a
 * before-instantiation shortcut for the specified bean.
 * @param beanName the name of the bean
 * @param mbd the bean definition for the bean
 * @return the shortcut-determined bean instance, or {@code null} if none
 */
@SuppressWarnings("deprecation")
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}
Java
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    // 缓存代理
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // 如果我们有一个自定义的TargetSource,在这里创建代理。
    // 抑制目标bean默认的实例化过程,就是说有这个配置就不会按照原有的方式进行实例化了
    // TargetSource将以自定义方式处理目标实例。
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

初始化后(AfterInitialization)

具体时机在 Bean 实例化之后,填充属性完毕之后,在调用 initializeBean 方法中的子方法 applyBeanPostProcessorsAfterInitialization, 即 BeanPostProcessor的后置方法

原理可以看以下这个类图,AOP代理的核心类 AutoProxyCreator 均实现了 BeanPostProcessor 这个接口

Java
/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
@Nullable
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyBeanReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

循坏依赖

如果开启了循环引用,在存在循环引用问题时,从三级缓存获取一个bean的代理对象的早期引用的时候可能为其生成代理,实现的逻辑和上边一样

doCreateBean 方法中

Java
1
2
3
4
5
// 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来
if (beanDefinition.isSingleton()) {
    Object finalBean = bean;
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
}
Java
1
2
3
4
5
6
7
8
protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
    Object exposedObject = bean;
    for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
            exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(exposedObject, beanName);
            if (null == exposedObject) return exposedObject;
        }
    }
Java
1
2
3
4
5
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    earlyProxyReferences.add(beanName);
    return wrapIfNecessary(bean, beanName);
}