逐行阅读Spring5.X源码(十二)AOP源码分析,难!面试官都不一定懂!

警告:阅读此文前务必先阅读之前写的《spring如何解决循环引用》,本篇文章高度依赖循环引用。

在循环依赖中我们讲了spring实例化bean的入口,refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,看doGetBean中的如下代码

    if (mbd.isSingleton()) {
                    // 实例化bean
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            // 真正的完成bean的创建
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

createBean(beanName, mbd, args);方法真正的完成了bean的实例化,包括循环依赖、AOP、生命周期等。

AOP是在何时完成的?

所谓的AOP无非就是将bean加强,在bean的方法前后加上其他的方法而已,bean的class在虚拟机启动的时候就加载到JVM里了,我们不会通过修改class来动态扩展bean的功能,但是可以新生成一个类(动态代理类),这个类呢,包含了bean的所有功能,同时又进行了加强,然后将这个动态代理类实例化,替换掉原有的bean,最后放到spring单例池中。不知道我这毫无文采的大白话有没有讲清楚。

所以,AOP肯定是跟bean的实例化息息相关的,注意,我说的是实例化,而不是生命周期。也就是说,我们AOP肯定是在bean实例化好了后再进行动态代理,想想JDK的动态代理,是需要一个实例化的被代理对象的,在《spring如何解决循环引用》这篇文章中我们详细讲了实例化及循环依赖。所以,AOP是在两个地方完成代理的:

  1. 所有的bean实例化完成,且循环依赖也完成,紧接着开始AOP动态代理,前提是你所代理的类没有被别的类依赖。
  2. 如果你的类被别的类依赖了,那么在依赖获取的过程中进行AOP动态代理。
    这是《spring如何解决循环引用》文章最后三级缓存的总结

问什么需要二级缓存?为什么需要bean的半成品需要提前暴露一个工厂? AOP就是一个原因吧!我现在有一个实例A,实例B要依赖我,但是A需要被代理,也就是说,A被代理后才能注入给B,那我B现在就要注入你,总不能等整个容器所有bean都实例化好后再来注入吧,讲道理也不是不可能,只是spring觉着太麻烦没那个必要,干脆,在注入的时候就直接AOP吧(后面会将源码)!讲清楚了没有!

OK,下马开始讲AOP源码!

AOP的准备工作

先把测试代码写出来
定义一个切面

@Component
@Aspect
public class UserAspect {
    @Pointcut("execution(* com.config.aop.AopTest.*(..))")
    public void pintCut(){
        System.out.println("point cut");
    }
    @Before("com.config.aop.UserAspect.pintCut()")
    public void beforeAdvice(){
        System.out.println("before");
    }

}

被代理的业务类


@Service
public class AopTest {

    public void aop(){
        System.out.println("This is my aop test");
    }
}
public class SpringTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Config.class);
        context.refresh();
        AopTest aopTest =  context.getBean(AopTest.class);
        System.out.println(aopTest);
        aopTest.aop();
    }
}

随着项目的进行,我们的业务类会越来越多,spring首先要过滤掉哪些类需要代理,哪些不需要。其实,就是判断类上的注解啦!
跟着上面的源码createBean(beanName, mbd, args);继续走。

再次声明 !!! 这里不明白上文的源码的,补习《spring如何解决循环引用》,不要放弃。

进入createBean源码,找到如下代码

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

看英文翻译,给Bean后置处理器一个机会,返回一个替代目标对象的代理对象。

注意,此时还没后面的bean初始化代码,这里只是做准备工作,做什么准备?继续看。

原来,AOP实现是靠后置处理器完成的,本专题以前的博文讲了BeanFactoryPostProcessor,而这里是 BeanPostProcessors ,两者有啥区别?BeanFactoryPostProcessor后置处理器干预了BeanDefinition的生成,而BeanPostProcessors 干预了bean的实例化。

Object bean = resolveBeforeInstantiation(beanName, mbdToUse); 是AOP第一次开始调用后置处理器,进去看看吧!

直接定位到核心代码

bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

有些读者会怀疑,不是说逐行阅读吗?怎么最近都是直接定位了!讲道理,如果之前的博文你都读过,非核心的代码你都能自行分析了,太简单的没必要在这列出了,毕竟大家的时间都很宝贵!

进入源码

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

for循环,拿到所有的BeanPostProcessors。



这么多后置处理器用到哪一个呢,自己调试去,看我吹没用。


AnnotationAwareAspectJAutoProxyCreator,就他!F5跟进代码,代码很难,简单讲一部分,等讲生命周期(更难)的时候还会讲

    @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;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        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;
    }

if (this.advisedBeans.containsKey(cacheKey))查看advisedBeans集合是否包含这个类的名字,如果包含就直接返回null了。

private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

advisedBeans 就是一个集合,用来保存不需要代理的类。比如我们上面定义的切面本身是不需要被代理的,还有加了@Configuration注解的Config配置类,也是不需要代理的,Config其实已经被代理了,之前讲过。

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName))

这行代码就是判断我们这个业务类是否需要被代理,进入isInfrastructureClass代码:

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return (super.isInfrastructureClass(beanClass) ||
                (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
    }

调用父类的方法

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && logger.isTraceEnabled()) {
            logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }
        return retVal;
    }

首先会调用父类的方法
class1.isAssignableFrom(class2) 判定此 Class1 对象所表示的类或接口与指定的 Class2 参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。
Advice、Pointcut、Advisor等是跟切面相关的,不需要代理。
如果父类方法返回false,继续判断该类是不是一个切面,是切面的话也是不需要被代理的。

shouldSkip(beanClass, beanName)会根据你的AOP配置,找到

符合条件的放入advisedBeans 集合中,后面会根据这个集合判断业务类是否需要被代理。

OK,趁热打铁,我们看AOP代理实现,我们现在跳回createBean源码,从Object bean = resolveBeforeInstantiation(beanName, mbdToUse);继续往后看Object beanInstance = doCreateBean(beanName, mbdToUse, args);这行代码,进入到方法内,找到下面代码

    //处理循环依赖
    populateBean(beanName, mbd, instanceWrapper);
    //开启生命周期!!!!!!
    exposedObject = initializeBean(beanName, exposedObject, mbd);

populateBean完成了bean的实例化及循环依赖,继续看initializeBean方法,这个方法就是开启了声明周期,AOP实现也是在这里面,进入看源码倒数第二行wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);,这个方法就是完成了AOP代理的实现,他是怎么实现的呢???

    @Override
    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;
    }

擦!还是后置处理器诶!


进去

进入wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 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;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

尼玛,spring源码太难了! 我花了好久好久才研究到这里。

if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey)))if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)),这两行判断跟上面的一样,不解释。

如果这两个判断都不成立,下面的代码就是代理的创建!创建!创建!

createProxy里面创建了代理,具体怎么通过CGLIB创建的?读者自行查看吧!读spring源码一定要动手调试,光看,没用!

返回到exposedObject = initializeBean(beanName, exposedObject, mbd);
这个时候,exposedObject 就是代理对象了

继续返回,


然后继续F6往下调试代码

此时,就注册到spring单例池中的,这个addSingleton方法是在getSingleton函数里完成的,为啥在这个函数里咧?在这篇文章里,我一言难尽,看《spring如何解决循环引用》

AOP,就是这么实现的,调用过程很复杂,其实就是靠后置处理器。

还没完,如果你的要代理的业务类被其他类循环依赖了,那么AOP的生成时机就不同了。

先加上两个循环依赖的测试代码

@Service
public class AopTest {

    @Autowired
    AopTest2 aopTest2;
    public void aop(){
        System.out.println("This is my aop test");
    }
}

@Service
public class AopTest2 {
    @Autowired
    AopTest aopTest;

    public void aop(){
        System.out.println("This is my aop test");
    }
}

refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,在该方法里的第三行:
Object sharedInstance = getSingleton(beanName); ,我们讲过,你的业务类实例化后会提前暴露一个工厂类,依赖你的类执行到这里时,会先调用这行代码,我们分析过这行代码,这里再分析,完全两个味道

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//      这个singletonObjects就是微观层面的IOC容器,循环创建刚开始时,IOC确实是空的,
//      但是我前面一开始的getBean()方法是存在递归调用现象的
        /**
         * 直接举2个例子:
         * 第一:假如现在在实例化A,结果有发现需要给A注入B,
         * 那Spring是不是得获得B,怎么获得呢? 递归使用getBean(BName)完成,
         * 第二个例子: A被添加上了@Lazy注解,是懒加载的,但是终究有一个会通过getBean(AName)获取A,
         * 这是发现A是实例化需要B,B肯定已经实例化完事了,同样是通过递归getBean(BName)实现注入,
         * 在这两个过程中就是getSingleton()保证不会重复创建已经存在的实例
         */
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

看这行

singletonObject = singletonFactory.getObject();

F5跟进源码,进入到下面代码



对吧,就是提前暴露的工厂,F5跟进

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

尼玛,又是后置处理器


跟进去

如果,有人,能看到这里,应该不需要我解释了。这就是AOP生成的第二个时机,在循环依赖过程中实现AOP,也是半成品的bean实例化完后为什么要暴露一个工厂的原因,而不是一个简单的bean对象,因为工行能够提供方法呀,在方法里我们就能处理这个对象啊,这里的处理单指AOP代理,不知道我讲没有讲清楚。


上面讲过了,一模一样的,代码复用啦
这里注意Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);方法就是找到切面的,怎么找到的,读者可以跟进代码读一下,就是拿到所有的beanDefinition,然后找所有带有@Aspect注解的类,因为我们可以定义多个切面,所以继续找到我们业务类所在的那个切面,也就时遍历所有的切面,然后根据@Pointcut("execution(* com.config.aop.AopTest.*(..))")进行匹配。


好吧,不卖关子了,我们看看getProxy方法吧,跟进去

public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

继续跟进createAopProxy方法

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

跟进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);
        }
    }

关于AOP的配置信息全部在config参数中,看一下我们最关心的两个值,切面和目标对象。


对吧,都有了,那就代理吧!
判断是否实现了接口,实现接口了就用JDK自带的动态代理技术,不然就用CGLIB动态代理技术。我们这里显然用了CGLIB动态代理技术,然后一直返回到下图所示

进入getProxy创建代理对象

至此,代理对象就创建完成了,我讲的大概的过程,没有细讲每行代码,感兴趣的自行读吧,我实在写不下去了,肩膀疼!

我们知道aopTest2,是在populateBean(beanName, mbd, instanceWrapper);完成了属性注入对吧,注入完成后我们看一下情况

看见没,aopTest2依赖了aopTest,此时注入的就是代理的aopTest了。

面试中,AOP会经常被问到,笔者给别人面试也会问。其实面试官不是想问你AOP的概念和如何使用,他会考察你对spring的了解程度,或者看你是否阅读过源码。面试造火箭,工作拧螺丝。确实,工作中很少去看spring源码,但是现在java程序员泛滥了,你如何脱颖而出,面试官也很为难啊,我知道A和B都会用,但是A读过源码,说明他对技术感兴趣,而且肯钻研,工作会游刃有余!

你面试如果给面试官分析上面的动态代理过程,差不多能秒杀大部分面试官了!面试官也不见得所有的技术都去读源码的!

文章的最后,给大家留个悬念! Spring中的事务是怎么回事?原理呢?源码如何实现?其实也是用到了AOP,你读懂了这篇文章就很容易解决。 加油

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

推荐阅读更多精彩内容