通过一个简单的入门例子,结合源码推导出SpringAop底层实现原理

Posted by JoshSu Blog on October 6, 2018

[TOC]

前提条件

本文不解释Aop是什么?为什么要使用Aop?这些概念, 看本文前, 要求大家对这些概念要非常清楚, 不清楚的自行在网上搜索学习。

本文将通过一个简单的例子入手, 一步一步的剖析出SpringAop底层实现细节。由于SpringAop底层实现重要技术为动态代理(JDK动态代理、CGLIB), 在剖析中重点以JDK动态代理为切入点, 读本文前大家一定要熟悉JDK动态代理技术, 之前写过一篇博文《剖析JDK动态代理技术》, 不明白的可以看看, 希望对大家有帮助。熟悉JDK动态代理技术的朋友, 一定对Proxy类、InvocationHandler接口、invoke方法、Proxy.newProxyInstance方法非常清楚, 每个类及方法调用时机及调用流程都要心中清楚, 这是阅读本文的基础。

从一个简单例子入手

业务逻辑

// 一个简单的商品接口
public interface IProductService {
    // 保存商品
    void save();
    // 删除商品
    void delete();
}
// 一个简单的IProductService实现类
public class ProductService implements IProductService {
    public void save() {
        System.out.println("save product");
    }
    public void delete() {
        System.out.println("delete product");
    }
}

定义一个切面

@Aspect
public class PkgTypeAspectConfig {
    @Pointcut("within(com.josh.aop.service.ProductService)")
    public void matchType() {
    }

    @Before("matchType()")
    public void before() {
        System.out.println("");
        System.out.println("###before");
    }

    @After("matchType()")
    public void after() {
        System.out.println("");
        System.out.println("###after");
    }
}

XML配置

// 文件名: spring-aop.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd  
				http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        
    <aop:aspectj-autoproxy />
		<bean id="productService" class="com.josh.aop.service.ProductService"/>
		<bean id="pkgTypeAspectConfig" class="com.josh.aop.config.PkgTypeAspectConfig" />
</beans>

运行

public class ProductServiceTest {
    @Test
    public void adminInsertTest() throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
        IProductService productService = (IProductService) context.getBean("productService");
        productService.save();
        productService.delete();
    }
}

运行结果

###before
insert product
###after

###before
delete product
###after

以上例子不难理解, 这是编写SpringAop功能时的能用流程, 相信大家肯定都用过, 同时体现了框架的强大之处, 仅仅使用了几行代码, 就完成了如此强大的功能, 使用之余, 不知大家是否思考过, 为什么依赖几个简单的配置(@Aspect、@Pointcut、@Before、@After、aop:aspectj-autoproxy), 强大的功能就能实现, 这背后到底发生了什么?

网上几乎每篇文章都会说, SpringAop实现的核心是动态代理技术, 《剖析JDK动态代理技术》这篇文章让我们知道可以把内存中动态代理生成的类保存到文件中, 反编译可以查看生成的动态类。

public class ProductServiceTest {
    @Test
    public void adminInsertTest() throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
        IProductService productService = (IProductService) context.getBean("productService");
        byte[] proxyClassBytes = ProxyGenerator.generateProxyClass(productService.getClass().getSimpleName(), new Class<?>[]{IProductService.class});
        FileOutputStream fileOutputStream;
        fileOutputStream = new FileOutputStream(productService.getClass().getSimpleName() + ".class");
        fileOutputStream.write(proxyClassBytes);
        productService.save();
        productService.delete();
    }
}

运行上面代码, 可以在目录中看到生成了一个class文件, 通过对该class文件进行反编译, 反编译后的java文件代码如下:

/*
 * Decompiled with CFR 0_132.
 *
 * Could not load the following classes:
 *  com.josh.aop.service.IProductService
 */
import com.josh.aop.service.IProductService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy9 extends Proxy implements IProductService {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy9(InvocationHandler invocationHandler) throws  {
        super(invocationHandler);
    }

    public final boolean equals(Object object) throws  {
        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 void save() throws  {
        try {
            this.h.invoke(this, m4, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

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

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

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.josh.aop.service.IProductService").getMethod("save", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.josh.aop.service.IProductService").getMethod("delete", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }
}

我们再次来回顾一下使用JDK动态代理的方式, 结合《剖析JDK动态代理技术》这篇文章, 是否产生了这样一个联想, Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西, 在整个过程中, 对于InvocationHandler的创建是最为核心的, 在自定义的InvocationHandler中需要重写3个函数。

1、构造函数, 将代理的对象传入。
2、invoke方法, 此方法中实现了AOP增强的所有逻辑.
3、Proxy.newProxyInstance() 方法, 此方法千篇一律, 但是必不可少。

通过己有知识推导SpringAOP的实现, 心中不停自问, SpringAOP中的JDK代理是不是也是这么做的呢? 如果是那以上这些代码在哪里呢? 带着这些问题又陷入沉思。到底实现了InvocationHandler接口的类是哪个?通过断点我们可以一看究竟。

h -> JdkDynamicAopProxy, 《剖析JDK动态代理技术》这篇文件里明确写了h这个成员变量是什么, 看到这里太高兴了, JdkDynamicAopProxy不就是实现了 InvocationHandler 的那个类嘛。接下来我们好好分析一下 JdkDynamicAopProxy 这个类。

JdkDynamicAopProxy 类分析

// 
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    ......
    ......
    @Override
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    
    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ......
        ......
        ......
    }
    ......
    ......
}

通过该类, 可以看到上面要寻找的东西, Proxy.newProxyInstance()、invoke() 等等这些方法。

截止现在代理生成的类也清楚了, 调用方法时, 实际调用的代理类里面的invoke方法, invoke方法也找到了, 接下来我们好好分析一下invoke方法。

JdkDynamicAopProxy.invoke() 方法分析(方法太长,就不列出方法体了)

逻辑猜想

之前我们手写的两个例子, invoke方法的逻辑为. 在方法调用前写一段逻辑, 接着 method.invoke(targetClass, args), 最后在方法结束前写一段逻辑。顺着这个思路, 但是SpringAop中一个方法可以支持很多增强(advise), 如果当只有一个前置增强时, 可以将增强方法放在目标方法调用前执行, 如果当只有一个后置增强时, 可以将增强方法放在目标方法调用后执行, 如果当前置增强和后置增强都存在时, 可以将增强方法放在目标方法调用前后执行。但是如果有10个前置增强呢,或者10个后置增强,或者有无数个前置增强和后置增强呢,再如果有很多环绕增强呢, 硬编码肯定无法满足要求。

很多方法都需要执行, 但是按照顺序依次执行, 是不是我们把所有要执行的方法放在数组里面, 遍历该数组, 达到链式执行的效果, 这样再多的增强都可以顺序的执行完了。SpringAop里面真的是这样实现的吗? 带着疑问继续探索。

invoke核心逻辑

public Object invoke(Object proxy, Method method, Object[] args) {
    ......
    ......
    // 获取当前方法的拦截器(各种增强方法)链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    ......
    ......
    // 将拦截器封装在ReflectiveMethodInvocation, 以便于使用其proceed进行链式调用拦截器
    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // 执行拦截器链
    retVal = invocation.proceed();
    ......
    ......
    // 返回结果
    return retVal
}

针对以上代码, 我们先暂时不去细探, 下面我先罗列几个类, 因为通过执行拦截器链最终都会执行到下面几个类里面的相应方法。
@Before -> AtBefore -> AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable;
@After -> AtAfter -> AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable;
@AfterReturning -> AtAfterReturning -> AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable;
@AfterThrowing -> AtAfterThrowing -> AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable;
@Around -> AtAround -> AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable;

以上5种增强方式中, @After、@AfterThrowing、@Around 这三个对应的类都实现了MethodInterceptor接口, 都具有invoke方法, 但是@Before、@AfterReturning所对应的类没有实现MethodInterceptor接口, 为了在调用链中统一能执行相同的方法, 我们需要将@Before、@AfterReturning这两个注解i所对应的类进行转化, 让其具备同样实现MethodInterceptor接口。getInterceptorsAndDynamicInterceptionAdvice()方法就是干了这个事情, 接着分析 getInterceptorsAndDynamicInterceptionAdvice 方法.

getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)

根据该方法可以看到如下代码:

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    // 如果实现了MethodInterceptor接口的, 直接加入
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    // 遍历所有的适配器, 满足要求的适配器, 将进行转化。
    // this.adapters => {
    //      new MethodBeforeAdviceAdapter(),
    //      new AfterReturningAdviceAdapter(),
    //      new ThrowsAdviceAdapter()
    // }
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
}

经过上面代码转化后, 所有的增强注解, 都会变成都实现了MethodInterceptor接口的类, 都具有invoke方法。
@Before -> AtBefore -> AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable -> MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable;
@After -> AtAfter -> AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable;
@AfterReturning -> AtAfterReturning -> AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable -> AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable;
@AfterThrowing -> AtAfterThrowing -> AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable;
@Around -> AtAround -> public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable;

接下来我们继续分析 执行拦截器链 方法, invocation.proceed()

invocation.proceed()

public Object proceed() throws Throwable {
    ......
    // 递归调用proceed方法时, 当满足下面方法时, 调用目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 方法内实现方案为: 反射
        return invokeJoinpoint();
    }
    ......
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    ......
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    ......
}

对该方法进行分析, 看到了invoke方法, 也看到了+1操作, 猜想一下, 是不是通过++操作, 不停的遍历增强器, 调用属于该bean的增强器的 invoke 方法。达到链式调用的效果。

当@After增强器, invoke代码如下:

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

递归调用proceed()方法, finally所有的时机是否与After语义一致.

当@Before增强器, invoke代码如下:

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

// this.advice.before 方法如下。
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
    invokeAdviceMethod(getJoinPointMatch(), null, null);
}

先执行before增强所对应的方式, 然后再递归调用proceed(), 是否实现了Before语议?

当@AfterReturning增强器, invoke代码如下:

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

// this.advice.afterReturning 方法如下。
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
    if (shouldInvokeOnReturnValueOf(method, returnValue)) {
        invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
    }
}

其它具体的方式就不列举了, 都是相同的执行流程。

接下来, 我们模拟一下实际场景, 看看拦截器链的调用过程。 假设有4个拦截器, 两个@Before, 两个@After. 大家可以在线上画一下, 是如何进行递归调用的。

到目前为止, 代理类的方法调用代码就己分析完了, 但是我们还有很多疑问。
1、为什么当前代理类在执行时, 就能知道它具有哪些拦截器?
2、如何找到拦截器?
3、哪些bean需要被代理? 判断条件是什么?

aop:aspectj-autoproxy 标签及bean始化化

怎样才能搞清楚上面3个问题, 其实猜想一下, 上面3个问题, 都可以归类于初始化阶段便能完成的操作, 在初始化所有非延迟单例bean时, 通过beanFactory获取到ioc里面所有的beanDefinition, 遍历所有的beanDefinition, 找到有@Aspect注解的bean, 解析出该bean所有的方法, 将@Before、@After等注解的方法封装成一个一个的增强器(advisor), 保存到缓存里面, 待bean初始化时, 取出切面表达式与beanClass、beanName进行匹配, 看是否能匹配到切面表达式, 如果能匹配就将满足条件的advisor与beanName进行K-V存储。同时需要将该beanName生成代理类,生成代理类时, 将能匹配上目标类的增强器赋值给InvocationHandler类的成员变量, 当执行代理类方法时好使用。

对熟悉Spring初始化流程的开发来说, 上面的解决方案应该不难想到, 初始化肯定涉及到标签解析及bean初始化, 接着往下分析.

aop:aspectj-autoproxy 标签逻辑分析

关于自定义标签的解析, 这里不做详细说明。通过aop命名空间, 不难找到这句配置

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

接下来

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        ......
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        ......
    }
}

接下来

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }
}

以上代码,比较简单,大家去断点跟一下就大体能明白,我在这里总结一下,aop:aspectj-autoproxy该标签完成了对AnnotationAwareAspectJAutoProxyCreator类型的自动注册, 那么这个类到底做了什么工作来完成AOP的操作呢? 这才是我们接下来的重点。

AnnotationAwareAspectJAutoProxyCreator类分析

我们看到AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,而实现BeanPostProcessor后, 当Spring加载Bean时会在实例前调用其postProcessAfterInitialization方法。不明白的请大家看我之前的博文《学习基于Spring框架开发的核心功能源码时, 首先得清楚以下3个知识点》 。而我们对于AOP初始化逻辑的分析也由此开始。

AbstractAutoProxyCreator的postProcessAfterInitialization方法

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 如果它适合被代理, 则需要封装指定bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ......
    ......
    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    ......
    ......
}

函数中我们己经看到了代理创建的雏形。当然, 真正开始之前还需要经过一些判断, 比如是否己经处理过或者是否是需要跳过的bean, 而真正创建代理的代码是从getAdvicesAndAdvisorsForBean开始的。 创建代理主要包含两个步骤:

1、获取增强方法或者增强器;
2、根据获取的增强进行代理。

获取增强器及能匹配当前bean

调用的类关系如下:
AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean
=> AbstractAdvisorAutoProxyCreator.findEligibleAdvisors
==> AbstractAdvisorAutoProxyCreator.findCandidateAdvisors
===> BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans
==> AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply
===> AopUtils.findAdvisorsThatCanApply
大体流程如下, 由于以上源码太多并且过于复杂, 这里以流程图的形式来梳理逻辑:

通过上面流程图及图上注释, 相信大家能明白这个逻辑。

创建代理

这里只写JDK动态代理, 上面的事例也只会走到这个逻辑里面来, CGLIB不写了。动态代理需要实现了InvocationHandler接口的类, 代码如下:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    ......
    ......
    return new JdkDynamicAopProxy(config);
    ......
    ......
}
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    // 构造函数
    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        ......
        // 将满足该bean的增强器赋值给了成员变量advised
        this.advised = config;
    }
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

Proxy.newProxyInstance函数所涉及的3个参数、含义及逻辑就非常的清晰了。

总结

通过上面分析, SpringAop涉及以下三大块功能, 简单画了一下流程图.