博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AOP静态代理解析2-代码织入
阅读量:4668 次
发布时间:2019-06-09

本文共 10382 字,大约阅读时间需要 34 分钟。

当我们完成了所有的AspectJ的准备工作后便可以进行织入分析了,首先还是从LoadTimeWeaverAwareProcessor开始。

LoadTimeWeaverAwareProcessor实现BeanPostProcessor方法,那么对于BeanPostProcessor接口来讲,postProcessBeforeInitialization与postProcessAfterInitialization有着其特殊意义,也就是说在所有bean的初始化之前与之后都会分别调用对应的方法,那么在LoadTimeWeaverAwareProcessor中的postProcessBeforeInitialization函数中完成了什么样的逻辑呢? 

@Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        if (bean instanceof LoadTimeWeaverAware) {            LoadTimeWeaver ltw = this.loadTimeWeaver;//DefaultContextLoadTimeWeaver            if (ltw == null) {                Assert.state(this.beanFactory != null,                        "BeanFactory required if no LoadTimeWeaver explicitly specified");                ltw = this.beanFactory.getBean(                        ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);            }            ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);        }        return bean;    }

在LoadTimeWeaverAwareProcessor中的postProcessBeforeInitialization函数中,因为最开始的if判断注定这个后处理器只对LoadTimeWeaverAware类型的bean起作用,而纵观所有的bean,实现LoadTimeWeaver接口的类只有AspectJWeavingEnabler

当在Spring中调用AspectJWeavingEnabler时,this.loadTimeWeaver尚未被初始化,那么,会直接调用beanFactory.getBean方法获取对应的DefaultContextLoadTimeWeaver类型的bean,并将其设置为AspectJWeavingEnabler类型bean的loadTimeWeaver属性中。

AspectJWeavingEnabler实现了BeanClassLoaderAware以及Ordered接口,实现BeanClassLoaderAware接口保证了在bean初始化的时候调用AbstractAutowireCapableBeanFactory的invokeAwareMethods的时候将beanClassLoader赋值给当前类。而实现Ordered接口则保证在实例化bean时当前bean会被最先初始化。

DefaultContextLoadTimeWeaver类又同时实现了LoadTimeWeaver、BeanClassLoaderAware以及DisposableBean。其中DisposableBean接口保证在bean销毁时会调用destroy方法进行bean的清理,而BeanClassLoaderAware接口则保证在bean的初始化调用AbstractAutowireCapableBeanFactory的invokeAwareMethods时调用setBeanClassLoader方法。

@Override    public void setBeanClassLoader(ClassLoader classLoader) {        LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);        if (serverSpecificLoadTimeWeaver != null) {            if (logger.isInfoEnabled()) {                logger.info("Determined server-specific load-time weaver: " +                        serverSpecificLoadTimeWeaver.getClass().getName());            }            this.loadTimeWeaver = serverSpecificLoadTimeWeaver;        }        else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
       //检查当前虚拟机中的Instrumentation实例是否可用 logger.info("Found Spring's JVM agent for instrumentation"); this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader); } else { try { this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader); logger.info("Using a reflective load-time weaver for class loader: " + this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName()); } catch (IllegalStateException ex) { throw new IllegalStateException(ex.getMessage() + " Specify a custom LoadTimeWeaver or start your " + "Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar"); } } }

也就是经过以上程序setBeanClassLoader和postProcessBeforeInitialization的处理后,在Spring中的bean之间的关系如下:

  1. AspectJWeavingEnabler类型的bean中的loadTimeWeaver属性被初始化为DefaultContextLoadTimeWeaver类型的bean;
  2. DefaultContextLoadTimeWeaver类型的bean中的loadTimeWeaver属性被初始化为InstrumentationLoadTimeWeaver。

上面的函数中有一句很容易被忽略但是很关键的代码:

this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);

这句代码不仅仅是实例化了一个InstrumentationLoadTimeWeaver类型的实例,而且在实例化过程中还做了一些额外的操作。在实例化过程中判断了当前是否存在Instrumentation实例,最终会取InstrumentationSavingAgent类中的instrumentation的静态属性,判断这个属性是否是null,InstrumentationSavingAgent这个类是spring-instrument-3.2.9.RELEASE.jar的代理入口类,当应用程序启动时启动了spring-instrument-3.2.9.RELEASE.jar代理时,即在虚拟机参数中设置了-javaagent参数,虚拟机会创建Instrumentation实例并传递给premain方法InstrumentationSavingAgent会把这个类保存在instrumentation静态属性中所以在程序启动时启动了代理时InstrumentationLoadTimeWeaver.isInstrumentationAvailable()这个方法是返回true的,所以loadTimeWeaver属性会设置成InstrumentationLoadTimeWeaver对象。对于注册转换器,如addTransformer函数等,便可以直接使用此属性(instrumentation)进行操作了。

public class InstrumentationSavingAgent {      private static volatile Instrumentation instrumentation;      public static void premain(String agentArgs, Instrumentation inst) {          instrumentation = inst;      }      public static Instrumentation getInstrumentation() {          return instrumentation;      }    }

因为AspectJWeavingEnabler类同样实现了BeanFactoryPostProcessor,所以当所有bean解析结束后会调用其postProcessBeanFactory方法。看下AspectJWeavingEnabler类的enableAspectJWeaving方法,

@Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);    }    public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader beanClassLoader) {        if (weaverToUse == null) {       //此时已经被初始化为DefaultContextLoadTimeWeaver            if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {                weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);            }            else {                throw new IllegalStateException("No LoadTimeWeaver available");            }        }     //使用DefaultContextLoadTimeWeaver类型的bean中的loadTimeWeaver属性注册转换器        weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer(                    new ClassPreProcessorAgentAdapter()));    }

AspectJClassBypassingClassFileTransformer类和ClassPreProcessorAgentAdapter类都实现了字节码转换接口ClassFileTransformer

private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {        private final ClassFileTransformer delegate;        public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {            this.delegate = delegate;        }        @Override        public byte[] transform(ClassLoader loader, String className, Class
classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) { return classfileBuffer; }       //委托给AspectJ代理继续处理 return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } }

 这也是一个修饰器模式,最终会调用ClassPreProcessorAgentAdapter的transform方法执行字节码转换逻辑,在类加载器定义类时(即调用defineClass方法)会调用此类的transform方法来进行字节码转换替换原始类。

  1. AspectJClassBypassingClassFileTransformer的作用仅仅是告诉AspectJ以org.aspectj开头的或者org/aspectj开头的类不进行处理。
  2. ClassPreProcessorAgentAdapter类中的代码比较多,它的主要工作是解析aop.xml文件,解析类中的Aspect注解,并且根据解析结果来生成转换后的字节码。

接下来就看看InstrumentationLoadTimeWeaver类的addTransformer方法代码:

public void addTransformer(ClassFileTransformer transformer) {      Assert.notNull(transformer, "Transformer must not be null");      FilteringClassFileTransformer actualTransformer =              new FilteringClassFileTransformer(transformer, this.classLoader);      synchronized (this.transformers) {          if (this.instrumentation == null) {              throw new IllegalStateException(                      "Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.");          }       //加入到jdk的instrumentation中加载class时自动调用      this.instrumentation.addTransformer(actualTransformer);          this.transformers.add(actualTransformer);      }  }

从代码中可以看到,这个方法中,把类转换器actualTransformer通过instrumentation实例注册给了虚拟机。这里采用了修饰器模式,actualTransformer对transformer进行修改封装,下面是FilteringClassFileTransformer这个内部类的代码:

private static class FilteringClassFileTransformer implements ClassFileTransformer {      private final ClassFileTransformer targetTransformer;      private final ClassLoader targetClassLoader;      public FilteringClassFileTransformer(ClassFileTransformer targetTransformer, ClassLoader targetClassLoader) {          this.targetTransformer = targetTransformer;          this.targetClassLoader = targetClassLoader;      }      public byte[] transform(ClassLoader loader, String className, Class
classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (!this.targetClassLoader.equals(loader)) { return null; } return this.targetTransformer.transform( loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } @Override public String toString() { return "FilteringClassFileTransformer for: " + this.targetTransformer.toString(); } }

这里面的targetClassLoader就是容器的bean类加载,在进行类字节码转换之前先判断执行类加载的加载器是否是bean类加载器,如果不是的话跳过类装换逻辑直接返回null,返回null的意思就是不执行类转换还是使用原始的类字节码。什么情况下会有类加载不是bean的类加载器的情况?AbstractApplicationContext的prepareBeanFactory方法中有一行代码:

// Detect a LoadTimeWeaver and prepare for weaving, if found.        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));            // Set a temporary ClassLoader for type matching.            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));        }

当容器中注册了loadTimeWeaver之后会给容器设置一个ContextTypeMatchClassLoader类型的临时类加载器,在织入切面时只有在bean实例化时织入切面才有意义,在进行一些类型比较或者校验的时候,比如判断一个bean是否是FactoryBean、BPP、BFPP,这时候不涉及到实例化,所以做字节码转换没有任何意义,而且还会增加无谓的性能消耗,所以在进行这些类型比较时使用这个临时的类加载器执行类加载,这样在上面的transform方法就会因为类加载不匹配而跳过字节码转换,这里有一点非常关键的是,ContextTypeMatchClassLoader的父类加载就是容器bean类加载器,所以ContextTypeMatchClassLoader类加载器是不遵循“双亲委派”的,因为如果它遵循了“双亲委派”,那么它的类加载工作还是会委托给bean类加载器,这样的话if里面的条件就不会匹配,还是会执行类转换。ContextTypeMatchClassLoader的类加载工作会委托给ContextOverridingClassLoader类对象,有兴趣可以看看ContextOverridingClassLoader和OverridingClassLoader这两个类的代码。这个临时的类加载器会在容器初始化快结束时,容器bean实例化之前被清掉,代码在AbstractApplicationContext类的finishBeanFactoryInitialization方法:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {       ...      beanFactory.setTempClassLoader(null);      // Allow for caching all bean definition metadata, not expecting further changes.      beanFactory.freezeConfiguration();      // Instantiate all remaining (non-lazy-init) singletons.      beanFactory.preInstantiateSingletons();  }

 

转载于:https://www.cnblogs.com/wade-luffy/p/6078446.html

你可能感兴趣的文章
[HEOI2016/TJOI2016]求和(第二类斯特林数)
查看>>
一道阿里面试题
查看>>
pta 习题集5-19 列车厢调度
查看>>
浅谈Ddos攻击攻击与防御
查看>>
HOJ 2148&POJ 2680(DP递推,加大数运算)
查看>>
HOJ 2156 &POJ 2978 Colored stones(线性动规)
查看>>
EL表达式学习笔记(JSTL)
查看>>
mybatis按datetime条件查询,参数为时间戳时
查看>>
常见软件开发模型
查看>>
改进方案1.0
查看>>
C#使用Monitor类、Lock和Mutex类进行多线程同步
查看>>
在O(1)时间删除链表结点
查看>>
NASA的10条代码编写原则
查看>>
C#异步编程
查看>>
8 定制10MINs 3
查看>>
唤起头像剪裁页面
查看>>
《重大技术需求征集系统》项目目标文档
查看>>
SelectUser.aspx
查看>>
unity之局域网
查看>>
2017IEC计算机第二次作业
查看>>