前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >逐行阅读Spring5.X源码(十二)AOP源码分析,难!面试官都不一定懂!

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

作者头像
源码之路
发布2020-09-04 10:34:39
4880
发布2020-09-04 10:34:39
举报
文章被收录于专栏:源码之路源码之路

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

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

代码语言:javascript
复制
    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的准备工作

先把测试代码写出来

定义一个切面

代码语言:javascript
复制
@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");
    }

}

被代理的业务类

代码语言:javascript
复制
@Service
public class AopTest {

    public void aop(){
        System.out.println("This is my aop test");
    }
}
代码语言:javascript
复制
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源码,找到如下代码

代码语言:javascript
复制
// 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第一次开始调用后置处理器,进去看看吧!

直接定位到核心代码

代码语言:javascript
复制
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

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

进入源码

代码语言:javascript
复制
    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跟进代码,代码很难,简单讲一部分,等讲生命周期(更难)的时候还会讲

代码语言:javascript
复制
    @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了。

代码语言:javascript
复制
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

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

代码语言:javascript
复制
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName))

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

代码语言:javascript
复制
    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return (super.isInfrastructureClass(beanClass) ||
                (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
    }

调用父类的方法

代码语言:javascript
复制
    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);这行代码,进入到方法内,找到下面代码

代码语言:javascript
复制
    //处理循环依赖
    populateBean(beanName, mbd, instanceWrapper);
    //开启生命周期!!!!!!
    exposedObject = initializeBean(beanName, exposedObject, mbd);

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

代码语言:javascript
复制
    @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方法。

代码语言:javascript
复制
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的生成时机就不同了。

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

代码语言:javascript
复制
@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); ,我们讲过,你的业务类实例化后会提前暴露一个工厂类,依赖你的类执行到这里时,会先调用这行代码,我们分析过这行代码,这里再分析,完全两个味道

代码语言:javascript
复制
    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;
    }

看这行

代码语言:javascript
复制
singletonObject = singletonFactory.getObject();

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

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

代码语言:javascript
复制
    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方法吧,跟进去

代码语言:javascript
复制
public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

继续跟进createAopProxy方法

代码语言:javascript
复制
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

跟进createAopProxy

代码语言:javascript
复制
    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,你读懂了这篇文章就很容易解决。 加油

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AOP是在何时完成的?
  • AOP的准备工作
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档