AOP靜態代理解析2-代碼織入


當我們完成了所有的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();  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM