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

2020-09-04 10:34:39 浏览数 (1)

警告:阅读此文前务必先阅读之前写的《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,你读懂了这篇文章就很容易解决。 加油

0 人点赞