Spring AOP实现浅析三之执行代理

《Spring AOP实现浅析 一》Spring AOP实现浅析 二》中,我们了解了Java中动态代理的两种实现、Spring AOP的基础概念、基础类。本文带你了解代理对象的创建和代理逻辑执行。

本文中源码来自Spring 5.3.x分支,github源码地址:https://github.com/spring-projects/spring-framework

一 创建代理对象

1.1 选择代理方式

ProxyFactory.getProxy()方法如下:

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

AopProxy接口,是对JDK动态代理和CGLIB代理开箱即用的抽象,实现类如下,其中ObjenesisCglibAopProxy是CglibAopProxy的一个子类。

AopProxyFactory接口,用于创建AopProxy对象。源码中的默认实现类DefaultAopProxyFactory,其中有对使用哪种代理实现的判断逻辑。

ProxyFactory是ProxyConfig的子类,optimize和proxyTargetClass属性默认值都是false,即默认使用JDK代理。当我们设置任意一个为true时,就有可能使用cglib代理。

proxyFactory.setOptimize(true);
proxyFactory.setProxyTargetClass(true);

当给proxyFactory添加Interface时,必然会使用JDK代理。(Spring AOP自动创建代理对象时,会判断类是否实现了接口,并设置ProxyFactory.interfaces属性)

proxyFactory.addInterface(OrderInterface.class);

注意这几点:

  1. Spring 3.2.0版本前,代理实现只有JDK动态代理一种方案。
  2. Spring 3.2.0版本引入了Cglib,因为Cglib性能更好,且创建的代理对象即可以赋值给父类变量,也可以赋值给接口变量。Cglib代理对类型转换支持更好。
  3. optimize即优化的意思,设置为true时,即从默认的JDK代理,改用更好的Cglib代理。

1.2 创建代理对象

JdkDynamicAopProxy中实现很简单,通过Proxy.newProxyInstance创建代理对象。

JdkDynamicAopProxy类也是InvocationHandler接口实现,当代理对象执行某个方法时,会进入invoke()方法。

在CglibAopProxy实现中,创建Enhancer对象并设置superClass、advised、callbackFilter、callbacks等属性,调用Enhancer.create()来创建代理对象。

DynamicAdvisedInterceptor是一个关键callback,当代理对象执行某个方法时,会进入intercept()方法。

二 执行代理逻辑

以我们熟悉的JDK代理实现为例。

2.1 整体流程

以JdkDynamicAopProxy为例,当代理对象执行任何方法时,都会进入JdkDynamicAopProxy#invoke中,步骤如下:

  1. 对equals、hashCode方法,不进行增强,直接使用被代理对象执行方法;

  2. 如果exposeProxy=true,设置代理对象到AopContext.currentProxy。
  3. 根据被代理对象、当前执行的方法,与ProxyFactory中所有Advisor进行匹配,并将Advice适配成MethodInterceptor对象;

  4. 得到List<MethodInterceptor> chain,并放入methodCache缓存中(一个Map)

  5. 如果当前方法未命中任何Advisor,则使用被代理对象直接反射执行;

  6. 如果有命中的Advisor,则使用ReflectiveMethodInvocation执行Interceptor chain;
  7. 执行完最后一个MethodInterceptor了,就会执行被代理对象的当前方法;

  8. 代理结束,还原AopContext.currentProxy为旧值。

2.2 为方法匹配Advisor

当前方法是否匹配Advisor,是根据Advisor的pointcut属性来判断。

一个Pointcut接口实现,持有ClassFilter接口、MethodMatcher接口实现类对象;

ClassFilter用于检查当前类是否匹配;

	boolean matches(Class<?> clazz);

MethodMatcher用于检查当前方法是否匹配;

boolean matches(Method method, Class<?> targetClass);

可见,Pointcut就在类和方法层面,规定了一个Advisor的适用范围。

2.3 适配Advice为MethodInterceptor

如下,当我们定义Advice实现如MethodBeforeAdvice时,只提供了增强逻辑,那被代理对象的原方法是如何被调用的?

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class SimpleBeforeAdvice implements MethodBeforeAdvice {

    // 只定义代理逻辑,不用手动回调
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("MethodBeforeAdvice...");
    }
}

当前方法匹配到一个Advisor时,会将Advisor的advice属性,适配成MethodInterceptor对象。

MethodInterceptor接口就是Advice的子接口,仅有一个方法声明。

// invocation就是MethodInterceptor链
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;

MethodBeforeAdviceAdapter类,会将MethodBeforeAdvice适配成MethodBeforeAdviceInterceptor对象,最终执行的是Interceptor的invoke方法。

相似的还有AfterReturningAdviceInterceptor、AfterReturningAdviceAdapter、ThrowsAdviceInterceptor。

之所以如此:

  • 一是因为Advice实现中只需提供增强逻辑,没有回调被代理对象原方法;
  • 二是因为Advice只是一个个独立的通知,当一个方法匹配到多个Advice时,需要将它们按顺序依次执行完,最后才执行一次被代理对象原方法。

3.4 执行MethodInterceptor链

MethodInvocation接口就是链的定义,有JDK代理的实现ReflectiveMethodInvocation,它有这些属性:

// 代理对象
protected final Object proxy;

// 被代理对象
@Nullable
protected final Object target;

// 被代理方法
protected final Method method;

// 当前方法匹配的所有MethodInterceptor
protected final List<?> interceptorsAndDynamicMethodMatchers;

// 标记执行到哪个Interceptor了
private int currentInterceptorIndex = -1;

ReflectiveMethodInvocation.process方法中,会依次执行interceptorsAndDynamicMethodMatchers中每个MethodInterceptor;而MethodInterceptor.invoke方法中,又会回调ReflectiveMethodInvocation.process。

这样一来,链条便逐个推进了。

2.5 流程图

三 exposeProxy原理

3.1 AopContext原理

ProxyConfig的exposeProxy属性,表示对当前线程是否暴露代理对象,默认为false;当设置为true时,我们可在方法中获取代理对象。比如:

package com.xiakexing.service;

import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;

@Component
public class UserService {

    public void test() {
        UserService self = (UserService) AopContext.currentProxy();
        System.out.println(self);
    }
}

底层是如何实现的呢?来看源码。

AopContext类持有静态属性ThreadLocal,用于在线程中保存当前代理对象。

将在执行通知的第一步,把代理对象设置到currentProxy。

JdkDynamicAopProxy#invoke中,在执行通知前,暴露代理对象;被代理类的原方法执行结束后,在finally中还原ThreadLocal。

在CglibAopProxy.DynamicAdvisedInterceptor#intercept中,也有相似的代码。

线程中,方法是基于方法栈的后进先出顺序被执行的。

当A类的a方法,调用B类的b方法时,currentProxy从proxyA,切换为proxyB;

当b方法执行完毕时,需要将currentProxy又切换回proxyA,以便a方法继续执行。

当AopContext.setCurrentProxy()入参为null时,会释放ThreadLocal(避免内存泄露)。

3.2 替代方案

AopContext类有如下说明

  1. Spring的AOP框架默认情况下不公开代理,因为这样做会带来性能成本。
  2. 当有合理的替代方法时,不应该使用AopContext,因为它使应用程序代码依赖于Spring AOP框架才能使用。

在使用@EnableAspectJAutoProxy时,exposeProxy也默认false。

可见,Spring官方并不推荐使用AopContext。为了在业务方法中获取代理对象,又有哪些替代方案呢?

  • 在当前类中注入自己
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UserService {

    @Autowired
    private UserService self;

    public void test() {
        System.out.println(self);
    }
}
  • 从ApplicationContext中获取代理对象
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class UserService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void test() {
        UserService self = applicationContext.getBean(UserService.class);
        System.out.println(self);
    }
}

在下一篇文章中,我们将分析@EnableAspectJAutoProxy的底层实现:

  • 如何将@Aspect、 @Before处理为Advisor;
  • 在bean生命周期的哪个阶段创建了代理对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员侠客行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值