浅析AOP实现原理(3)Spring AOP实现原理

前言

有一点Spring基础的同学应该都知道,Spring的动态代理默认使用的是JDK动态代理(对于非接口的类,用cglib,与JDK动态代理类似,这里不多做解释),不清楚JDK动态代理的点这里。并且,我们知道通过jdk动态代理,我们可以拿到一个代理后的对象,而Spring的bean实例化过程,就涉及到代理对象的替换

Spring Bean的生命周期

想知道Spring是如何实现的动态代理,必须对bean的生命周期和实例化过程有初步了解。笔者通过阅读源码,得知一个bean的实例化过程,是由一系列的BeanPostProcessor(后置处理器)处理的,在bean实例化之前的注解解析,实例化过程中确定构造器,实例化后的属性注入,属性注入完的初始化步骤,分别由特定的后置处理器来完成。
如果要在Spring当中实现aop动态代理,笔者认为主要有两种思路:

  • 1、实例化之前将BeanDefinition中的class替换为代理类,可以通过自定义BeanFactoryPostProcessor或者ImportBeanDefinitionRegisterar来实现
  • 2、实例化完成之后,通过自定义的BeanPostProcessor将对象替换成代理对象

由于jdk动态代理和cglib动态代理,都只提供了获取代理对象的方法,因此我们可以猜测Spring是利用第二种方式实现的。

猜想验证

下面是一段aop切面的demo:

@Aspect
@Component
public class MyAspect {
    @Autowired
    private MyService myService;

    @Pointcut("execution(* com.lwl.controller.MyController.testAop(..))")
    public void testAop(){}

    @Around("testAop()")
    public Object aopWeaveing(ProceedingJoinPoint joinPoint) throws Throwable {
       //代理代码,忽略
    }
}

下面是被代理的方法和类:

@RestController
@RequestMapping("/aop")
public class MyController {
    @GetMapping("test")
    @AssertLogin
    public String testAop(){
        //业务代码,忽略
    }
}

bean的实例化过程主要发生在context的refresh方法中的finishBeanFactoryInitialization方法,其中
调用了DefaultLisableBeanFactory的preInstantiateSingletons,通过getBean(beanName)来实例化bean
因此我们跟踪到执行了自定义beanPostProcessor的AbstractAutowireCapableBeanFactory的initializeBean方法当中:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        //忽略不重要的代码
        //wrappedBean即bean对象
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
           //调用beanPostProcessor的postProcessBeforeInitialization方法
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //调用bean中实现的afterPropertiesSet或被@PostConstuct注解修饰的方法,即初始化方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        if (mbd == null || !mbd.isSynthetic()) {
          //调用beanPostProcessor的postProcessAfterInitialization方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

这里需要明确的是,beanPostProcessor的两个方法postProcessBeforeInitialization和postProcessAfterInitialization都能够替换对象,区别在于postProcessBeforeInitialization在bean执行初始化方法之前执行,简单思考一下,我们对对象进行代理时,很可能需要用到该bean对象初始化之后的属性,因此我们猜测代理的过程发生在初始化之后的postProcessAfterInitialization过程中。
笔者将断点打在调用applyBeanPostProcessorsAfterInitialization方法之前,可以看到此时的wrapperBean还是MyController


断点往下走,执行完applyBeanPostProcessorsAfterInitialization方法,发现wrappedBean已经变成了CGLib动态代理对象:

这就验证了我们之前的猜测

实现原理分析

我们进入applyBeanPostProcessorsAfterInitialization方法看看spring到底做了什么事

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

        Object result = existingBean;
         //遍历容器中所有的beanPostProcessor,执行postProcessAfterInitialization方法
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到该方法中遍历了spring容器里所有的beanPostProcessor,并挨个执行postProcessAfterInitialization。由于笔者是在平时开发的springboot环境中随便写了一个aop类做调试,因此此时的容器中有非常多的后置处理器,简单看一下一共有三十个:


image.png

目前我们能确定的是,一定是某一个后置处理器执行完了postProcessAfterInitialization方法,才将对象替换成了代理对象,那如何从这么多的beanPostProcessor中找到那个凶手?两种方法:

  • 1、还是用之前打断点的方法,看看哪一个后置处理器执行完以后对象发生了改变,但是这里有三十多个后置处理器,作为一个有追求的程序员考虑到算法时间复杂度为O(n),不行这个方法需要优化
  • 2、靠猜- -笔者死磕源码的经历,大部分情况靠猜,事实上百分之八十都能猜对。这三十个beanPostProcessor中,从名字上看,似乎只有数组下标为5的AnnotationAwareAspectJAutoProxyCreator看起来比较可疑(又是Aspect又是AutoProxy的)
    因此我们将断点条件修改为只有beanPostProcessor为该对象时才暂停:


    image.png

    继续调试,成功进入断点,执行之前为MyController


    image.png

    断点执行完以后对象变成了Cglib代理对象:
    image.png

    猜想再次被验证了,那么我们的重点就转换为查看这个beanPostProcessor中究竟做了什么,一路跟踪:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
             //重点看这个方法,它返回了代理后的对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
              //根据bean的类型和bean的名称,获取与该类相关的切面
        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;
        }
        return bean;
    }


protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
         //从切面对象中提取我们定义的增强逻辑代码,构造成proxyFactory代理工厂
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
                //通过代理工厂创建代理
        return proxyFactory.getProxy(getProxyClassLoader());
    }

//该方法为proxyFactory中的getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

调试发现createAopProxy()返回了一个CglibAopProxy,说明此时使用的是cglib动态代理



而CglibAopProxy的getProxy里面就是我们非常熟悉的cglib动态代理的代码了:

public Object getProxy(@Nullable ClassLoader classLoader) {
             //rootClass即MyController.class
            Class<?> rootClass = this.advised.getTargetClass();
            Class<?> proxySuperClass = rootClass;
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }

            enhancer.setSuperclass(proxySuperClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

           //根据要代理的类生成callback,这个是重点
            Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
            return createProxyClassAndInstance(enhancer, callbacks);
    }



private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
         //advised即之前创建好的proxyFactory,包含了我们交给spring管理的所有Aspect
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();
        //将切面构造成Callback,主要看这个类
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
             ... ...
    }


//构造的callback:
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
       //重点看重写的intercept方法
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();
            try {
             //如果代理对象被设置为可以暴露,则将当前的代理对象注入
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                target = targetSource.getTarget();
                Class<?> targetClass = (target != null ? target.getClass() : null);
              //把切面的增强逻辑转换成list
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
             //如果没有增强逻辑,则直接执行原来的方法
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                }
                else {
               //有增强逻辑,则根据spring aop配置规则确定执行顺序
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = processReturnType(proxy, target, method, retVal);
                return retVal;
            }
        }
    }

总结

Spring的aop,是在bean被实例化并初始化之后,通过beanPostProcessor的postProcessAfterInitialization,通过cglib(jdk动态代理),在实际逻辑执行前后插入我们的增强逻辑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342