Spring AOP源码浅析

1. Spring AOP示例代码


示例代码结构如下图所示:

其中切面类AspectObject的代码:

@Aspect
@Component
public class AspectObject {

    //抽取公共的切入点表达式
    //1、本类引用
    //2、其他的切面引用
    @Pointcut("execution(public * io.zgc.spring.features.aop.annotation.TargetObject.*(..))")
    public void pointCut(){};

    //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+ Arrays.asList(args)+"}");
    }

    @After("io.zgc.spring.features.aop.annotation.AspectObject.pointCut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
    }

    //JoinPoint一定要出现在参数表的第一位
    @AfterReturning(value="pointCut()",returning="result")
    public void logReturn(JoinPoint joinPoint,Object result){
        System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
    }
}

被代理类TargetObject的代码:

@Component
public class TargetObject implements TargetInterface{

    public String sayHello(String name) {
        System.out.println("hello,"+name);
        return "hello---"+name;
    }

}

测试客户端Client的代码:

package io.zgc.spring.features.aop.annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Client {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);

        TargetInterface bean = applicationContext.getBean(TargetInterface.class);
        bean.sayHello("zgc");
        System.out.println(bean.getClass().getName());
    }
}

运行测试代码,输入如下结果

sayHello运行。。。@Before:参数列表是:{[zgc]}
hello,zgc
sayHello结束。。。@After
sayHello正常返回。。。@AfterReturning:运行结果:{hello---zgc}
com.sun.proxy.$Proxy21

从输出结果,可以看出TargetObject类,已经被Spring修改成了代理类com.sun.proxy.$Proxy21并在sayHello方法执行的织入了代理逻辑。接下来,我们就来分析下Spring是在什么地方以及通过什么方式将TargetObject替换成代理对象。

2. Debug调试


我们使用倒推的方式进行debug,重点关注我们需要关注的代码。

2.1. getBean调试

我们先假设代理对象实在获取bean的时候发生的。

TargetInterface bean = applicationContext.getBean(TargetInterface.class);

Debug跟进去发现,它最终是在singletonObjects中将代理对象获取出来的,而singletonObjects是个Map结构,它里面存放的是单例bean,可以简单将其理解为bean工厂(tips:这种理解并不准确)。

这里我们可以得出结论:代理对象的创建一定是包含在ApplicationContext对象创建的过程中

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);

那我们就在singletonObjects放置值的地方加上断点,再通过程序的调用栈尝试查看代理对象在什么地方生成。

为了减低干扰,断点需要加上条件beanName.equals("targetObject");

2.2. ApplicationContext创建debug

上面断点设置后,我们重新运行程序,可以看到以下程序调用栈

我们向上追溯一下调用栈上的代码,调用addSingleton()方法时已经将代理对象通过入参传递进来。而DefaultSingletonBeanRegistry#getSingleton()的伪代码如下:

  public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                ... ...
                try {
                    // 这里获取targetObject的代理对象,实际会调用createBean方法
                    singletonObject = singletonFactory.getObject();
                }
                ... ...
                addSingleton(beanName, singletonObject);
            }
            return singletonObject;
        }
    }

继续向上追溯, AbstractBeanFactory#doGetBean, 其伪代码如下:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    // Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                ... ...
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    return (T) bean;
}

结合getSingletondoGetBean方法可知,当调用ObjectFactorygetObject方法时,会触发ObjectFactory类的createBean方法执行。

至此,我们知道了代理对象的创建是发生在createBean方法中,因此我们在此处增加条件断点,看看springcreateBean方法中做了哪些操作。

2.3. createBean调试

设置好断点之后,我们重新执行程序,一路step into

图中最下面的lambda$doGetBean就是我们给createBean设置断点的地方。最终程序执行到了AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization。其代码如下:

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到这里是遍历所有的BeanPostProcessor,然后依次执行其postProcessAfterInitialization方法。此时Spring运行的相关变量如下

这里注意一下,程序运行到这里,TargetObject对应的是原始对象,还没有被代理。

另外当前Spring上下文中包含的BeanPostProcessor中,有一个AnnotationAwareAspectJAutoProxyCreator。这个类是个后置处理器,它和代理对象的创建密不可分。

2.4. AnnotationAwareAspectJAutoProxyCreator调试

我们先来看看AnnotationAwareAspectJAutoProxyCreator类的继承结构

可以看到AnnotationAwareAspectJAutoProxyCreator类继承了BeanPostProcessorBeanFactoryAware。而BeanFactoryAware接口的postProcessAfterInitialization方法是在父类AbstractAutoProxyCreator中实现。

下面接着上面的applyBeanPostProcessors方法继续调试。

最终请求链路进入到ProxyCreatorSupport#createAopProxy方法(接下来的代码都是创建aop代理对象的核心逻辑),其源码如下:

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

DefaultAopProxyFactory#createAopProxy

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

最终调用JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)方法,其内部同步JDK动态代理,创建代理对象。

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

至此,我们已经清楚的知道Spring是如何创建AOP代理对象。

2.5. 小结

Spring在创建IOC容器时,通过Spring提供的后置处理器扩展点,先向容器注册一个AnnotationAwareAspectJAutoProxyCreator后置处理器。再通过该后置处理器(其他后置处理器也如此)可以介入bean创建的生命周期,将原生bean通过JDK动态代理和cglib的方式替换成AOP代理类。

3. @EnableAspectJAutoProxy


上面还有一个疑问,AnnotationAwareAspectJAutoProxyCreator是怎么注册到IOC容器中的?答案就是@EnableAspectJAutoProxy

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

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}

可以看到在@EnableAspectJAutoProxy注解中,为引入了(向容器注册)AspectJAutoProxyRegistrar类。

@Import(AspectJAutoProxyRegistrar.class)

AspectJAutoProxyRegistrar类最终会引入AnnotationAwareAspectJAutoProxyCreator后置处理器,相关代码如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

AopConfigUtils
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

注册AnnotationAwareAspectJAutoProxyCreator的代码最终是需要创建IOC容器时来触发,调用链路图如下:

3.1. 创建和注册AnnotationAwareAspectJAutoProxyCreator流程

流程:
1)、传入配置类(或者配置文件),创建ioc容器
2)、注册配置类,调用refresh()刷新容器;
3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;


AnnotationAwareAspectJAutoProxyCreator创建时序图

1、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
2、给容器中加别的BeanPostProcessor
3、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
4、再给容器中注册实现了Ordered接口的BeanPostProcessor;
5、注册没实现优先级接口的BeanPostProcessor;
6、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;创建internalAutoProxyCreator BeanPostProcessor 【 AnnotationAwareAspectJAutoProxyCreator 】

  • 6.1. 创建Bean的实例
  • 6.2. populateBean;给bean的各种属性赋值
  • 6.3. initializeBean:初始化bean;
    • 6.3.1. invokeAwareMethods():处理Aware接口的方法回调
    • 6.3.2. applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
    • 6.3.3. invokeInitMethods();执行自定义的初始化方法
    • 6.3.4. applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
  • 6.4. BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
    1. 把BeanPostProcessor注册到BeanFactory中;
beanFactory.addBeanPostProcessor(postProcessor);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容