《Spring源码深度解析》七(2)

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

7.2 静态AOP

关于LTW:加载时织入

《Spring源码深度解析》七(2)

7.2.1 与动态代理对比:

AOP的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强;
它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤, 而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类并无差别,所以在效率上会相对高些。

7.2.2 创建AOP静态代理

7.2.2.1 Instrucment

可以由此实现一个Java agent,通过此agent来修改类的字节码,即改变一个类;

Instrucment可以理解为类似一种更低级、更松耦合的AOP,可以从底层来改变一个类的行为。

7.2.2.2 AspectJ

AspectJ是在Instrucment的基础上进行封装的

AspectJ、JDK动态代理、CGlib的关系:

AspectJ是一个AOP框架,底层实现采用动态代理。SpringAOP使用AspectJ,其动态代理实现方式则jdk和cglib中切换。

Sprin嵌入了AspectJ,将动态代理的任务直接委托给了AspectJ;

SpringAOP:https://blog.csdn.net/flyfeifei66/article/details/82784321

我们借助于Spring Aop的命名空间可以将纯POJO转换为切面,实际上这些POJO只是提供了满足切点的条件时所需要调用的方法,但是,这种技术需要XML进行配置,不能支持注解。

所以spring借鉴了AspectJ的切面,以提供注解驱动的AOP,本质上它依然是Spring基于代理的AOP,只是编程模型与AspectJ完全一致,这种风格的好处就是不需要使用XML进行配置。

使用@Aspect方式,可以在类上直接一个@Aspect就搞定,不用在xml里配了。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,注意只是使用了它的注解功能而已,并不是核心功能 。SpringAop的底层技术依然是Jdk动态代理和Cglib。

7.2.3 Spring是怎么嵌入AspectJ的?

7.2.3.1 切入点(自定义标签):

ContextNamespaceHandler#
public void init() {
    ...
    registerBeanDefinitionParser("load-time-weaver"new LoadTimeWeaverBeanDefinitionParser());
    ...
}

7.2.3.2 解析:LoadTimeWeaverBeanDefinitionParser

doParse:

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    //与aop:aspectj-autoproxy/相同,也是以标签作为标志,进行相关处理类的注册
}

此处注册的类为org.Springframework.context.weaving.AspectWeavingEnabler,注册流程如下:

1.是否开启AspectJ。
配置了后,检测META-INF/aop.xml是否存在

2.将org.Springframework.context.weaving.AspectJWeavingEnabler封装在BeanDefinition中注册
其注册方式为:将类路径注册在新初始化的RootBeanDefinition中,在RootBeanDefinition的获取时会转换成对应的class

对于标签本身,Spring也会产生一个bean来保存
这个bean的id为loadTimeWeaver,
class 为org.Springframework.context.weaving.DefaultContextLoadTimeWeaver,
也就是完成了DefaultContextLoadTimeWeaver类的注册,其作用为保存标签

然后,在AbstractApplicationContext#prepareBeanFactory()中,
注册了LoadTimeWeaverBeanDefinitionParser,

此时,就激活了AspectJ的功能

7.2.3.3 织入

完成了AspectJ的准备工作后,就可以进行织入的分析了

依然从LoadTimeWeaverAwareProcessor开始(实现BeanPostProcessor接口)

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof LoadTimeWeaverAware) {//实现此接口的bean只有AspectJWeavingEnabler
        LoadTimeWeaver ltw = this.loadTimeWeaver;
        if (ltw == null) {
            ...
            ltw = this.beanFactory.getBean(//bean类:DefaultContextLoadTimeWeaver
                    ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
        }
        ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
    }
    return bean;
}

AspectJWeavingEnabler实现了BeanClassLoaderAware接口,所以在初始化的时候调用AbstractAutowireCapableBeanFactory的invokeAwareMethods的时候,会调用setBeanClassLoader,将beanClassLoader赋值给当前类,DefaultContextLoadTimeWeaver同理

public void setBeanClassLoader(ClassLoader classLoader){
    ...
    //实例化InstrumentationLoadTimeWeaver,并赋值给当前的instrumentation属性
    //技巧:这样就能直接使用此属性进行操作了
    this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader) ;
    ...
}

此时,bean之间的关系为:

《Spring源码深度解析》七(2)


原文始发于微信公众号(不止于Java):《Spring源码深度解析》七(2)