曹工說Spring Boot源碼(15)-- Spring從xml文件里到底得到了什么(context:load-time-weaver 完整解析)


寫在前面的話

相關背景及資源:

曹工說Spring Boot源碼(1)-- Bean Definition到底是什么,附spring思維導圖分享

曹工說Spring Boot源碼(2)-- Bean Definition到底是什么,咱們對着接口,逐個方法講解

曹工說Spring Boot源碼(3)-- 手動注冊Bean Definition不比游戲好玩嗎,我們來試一下

曹工說Spring Boot源碼(4)-- 我是怎么自定義ApplicationContext,從json文件讀取bean definition的?

曹工說Spring Boot源碼(5)-- 怎么從properties文件讀取bean

曹工說Spring Boot源碼(6)-- Spring怎么從xml文件里解析bean的

曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中得到了什么(上)

曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中得到了什么(util命名空間)

曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中得到了什么(context命名空間上)

曹工說Spring Boot源碼(10)-- Spring解析xml文件,到底從中得到了什么(context:annotation-config 解析)

曹工說Spring Boot源碼(11)-- context:component-scan,你真的會用嗎(這次來說說它的奇技淫巧)

曹工說Spring Boot源碼(12)-- Spring解析xml文件,到底從中得到了什么(context:component-scan完整解析)

曹工說Spring Boot源碼(13)-- AspectJ的運行時織入(Load-Time-Weaving),基本內容是講清楚了(附源碼)

曹工說Spring Boot源碼(14)-- AspectJ的Load-Time-Weaving的兩種實現方式細細講解,以及怎么和Spring Instrumentation集成

工程代碼地址 思維導圖地址

工程結構圖:

概要

本篇是spring源碼的第15篇,前面13/14兩篇,重點講了load-time-weaver的使用和底層原理。load-time-weaver,通俗地說,就是在JVM加載class時做文章,本來加載一個class A,但是實際JVM加載的class A,可能是被增強過的,被修改過的,所以,這是一種應用面更廣,適用場景更多的,性能也更加優秀的aop方案,避免了運行時aop的性能消耗。

使用

使用demo,我這邊有兩個:

  1. tomcat war包場景:

    https://gitee.com/ckl111/all-simple-demo-in-work/tree/master/test-load-time-weaver

    本應用為war包應用,ide里使用tomcat啟動即可,訪問:

    http://localhost:20000/test.do (端口修改為自己的)。

    訪問上述url后,可以看到效果:

  2. java獨立應用場景:

    https://gitee.com/ckl111/all-simple-demo-in-work/tree/master/spring-load-time-weave-demo

    本應用為獨立應用,直接啟動foo.Main中的main方法即可。

    值得注意的是,此時啟動時,需要指定:-javaagent:E:\repo\org\springframework\spring-instrument\4.3.7.RELEASE\spring-instrument-4.3.7.RELEASE.jar

    執行main方法后,效果如下:

代碼我就不仔細拉下來講了,和前面13、14兩講差不多。

context:load-time-weaver的解析

上面兩個demo,都是在spring的配置文件里,進行了如下配置:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                 http://www.springframework.org/schema/context/spring-context.xsd">
	// 運行時加載的核心,就在於此
    <context:load-time-weaver/>

</beans>

我們知道,解析context命名空間的,主要是org.springframework.context.config.ContextNamespaceHandler

package org.springframework.context.config;

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

   @Override
   public void init() {
      registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
      registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
      registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
      registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
      // 這個就是我們要找的
      registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
      registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
   }

}

從上述init方法中,可以看出,解析load-time-weaver元素的類為:LoadTimeWeaverBeanDefinitionParser。

該類的類結構如下,可以看到,實現了BeanDefinitionParser,這個接口的方法,很簡單,就是給你一個xml元素,你負責解析BeanDefinition。

import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;

public interface BeanDefinitionParser {

   BeanDefinition parse(Element element, ParserContext parserContext);

}

我們現在看下本解析類的實現:

@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
   builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   // 判斷aspectJ織入是否啟用,一般來說,只要classpath下存在META-INF/aop.xml,就算做啟用
   if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
      if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {
         // 注冊一個bean,bean的class為ASPECTJ_WEAVING_ENABLER_CLASS_NAME常量,該常量為:org.springframework.context.weaving.AspectJWeavingEnabler
         RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
         parserContext.registerBeanComponent(
               new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME));
      }

      if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
         new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
      }
   }
}

// 判斷aspectJ織入是否啟用,一般來說,只要classpath下存在META-INF/aop.xml,就算做啟用
protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
   if ("on".equals(value)) {
      return true;
   }
   else if ("off".equals(value)) {
      return false;
   }
   else {
      ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
      return (cl.getResource("META-INF/aop.xml") != null);
   }
}

我們從上面這段代碼,可以看到,doParse時,注冊了一個beanDefinition,該beanDefinition的class為:

org.springframework.context.weaving.AspectJWeavingEnabler。

其實,這段代碼總共會注冊2個bean definition:

  1. doParse方法的參數BeanDefinitionBuilder builder,大家看到了吧,這個參數是父類傳進來的,最終會被注冊為一個bean definition,這個bean的class是啥呢,可以看到下面的代碼,獲取class是調用了子類的getBeanClassName。

    // 本類為上述解析類LoadTimeWeaverBeanDefinitionParser的父類
    org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
    
    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
       BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
       String parentName = getParentName(element);
       if (parentName != null) {
          builder.getRawBeanDefinition().setParentName(parentName);
       }
    	// 調用子類的方法,獲取bean class
        String beanClassName = getBeanClassName(element);
        if (beanClassName != null) {
            builder.getRawBeanDefinition().setBeanClassName(beanClassName);
        }
       // 就是這里,會把builder傳給子類進行處理;
       doParse(element, parserContext, builder);
       // 這里會通過builder,獲取到BeanDefinition,返回給上層去注冊
       return builder.getBeanDefinition();
    }
    
    // 被父類調用,獲取bean class,這里返回的class為:首先看看xml元素是否設置了該屬性,如果沒設置,返回默認class:org.springframework.context.weaving.DefaultContextLoadTimeWeaver
    @Override
    protected String getBeanClassName(Element element) {
       if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
          return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
       }
       return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
    }
    
  2. 大家在前面看到了,除了上面注冊的這個bean definition,在LoadTimeWeaverBeanDefinitionParser的doParse里,還注冊了一個bean definition,類型為org.springframework.context.weaving.AspectJWeavingEnabler。

本來可能還會注冊一個,這個暫時不太了解,先跳過:

@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
   builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

   if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
      if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
         parserContext.registerBeanComponent(
               new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME));
      }
	// 這里如果滿足,還會自動注冊<context:spring-configured/>
      if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) 	{
         new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
      }
   }
}

匯總一下,注冊了2個bean definition,其中一個為特殊類型的bean

bean class bean 類型 實現的接口
DefaultContextLoadTimeWeaver 普通bean LoadTimeWeaver,BeanClassLoaderAware
AspectJWeavingEnabler 實現了BeanFactoryPostProcessor,會在spring獲取完成全部的bean definition后,會所有的bean definition進行后置處理 BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware,

context:load-time-weaver如何生效

上面我們看到,注冊的兩個bean,其中一個比較特殊,是實現了BeanFactoryPostProcessor接口的。

按理說,正常的流程是:

  1. spring掃描xml或注解,獲取注冊的bean definition
  2. BeanFactoryPostProcessor對第一步獲取到的bean definition進行處理,可能是修改,也有可能會注冊更多的bean definition,這一步完成后,才是最終的bean definition集合
  3. 對全部的bean definition集合挨個遍歷,如果是單例,且沒有設置為lazy-init,則馬上對其進行實例化。其中,這一步又有幾個小步驟。我這里找了個網上的圖,相對還比較准確。

但還有個問題,既然第二步中,BeanFactoryPostProcessor要去處理spring中所有的bean definition,那,BeanFactoryPostProcessor要怎么生成呢?

不用擔心,BeanFactoryPostProcessor 它自己也是bean definition,生成的話,也是走和普通bean definition一樣的流程。只是,BeanFactoryPostProcessor這些bean 的生成的時機比較超前。

下面這個代碼,大家肯定比較熟悉了:

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // BeanFactoryPostProcessor 這種類型的bean,在此時發揮作用,這時,就會通過getBean來先進行它自身的實例化
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }
   }
}

在上述的 invokeBeanFactoryPostProcessors(beanFactory)中,中間會調用到下面的代碼:

#org.springframework.context.support.PostProcessorRegistrationDelegate

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
   // 獲取各種BeanFactoryPostProcessor
   ...

   String[] postProcessorNames =
         beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
   ...
       
   // 這里,遍歷所有的BeanFactoryPostProcessor,對每個BeanFactoryPostProcessor進行getBean來實例化
   List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
   for (String postProcessorName : orderedPostProcessorNames) {
      orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
   }
   invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

   ...
   beanFactory.clearMetadataCache();
}

這里,我們只關注我們前文通過context:load-time-weaver解析到的那個AspectJWeavingEnabler:

public class AspectJWeavingEnabler
      implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered 

它實現了BeanClassLoaderAware,spring會把當前使用的類加載器傳給這個bean;

它實現了LoadTimeWeaverAware,spring會給它傳遞一個LoadTimeWeaver:

public interface LoadTimeWeaverAware extends Aware {

   void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);
}

調用setLoadTimeWeaver傳遞LoadTimeWeaver,發生在什么時候呢?

其實,這個操作是由org.springframework.context.weaving.LoadTimeWeaverAwareProcessor來完成的,這個類,是一個BeanPostProcessor,既然是BeanPostProcessor,就是在bean已經生成了之后,實例化之前。

也就是說,在AspectJWeavingEnabler這個bean被創建后,但是還沒有實例化之前,會調用BeanPostProcessor來對bean進行處理;其中一個BeanPostProcessor就是LoadTimeWeaverAwareProcessor。

我們接下來看LoadTimeWeaverAwareProcessor的實現:

org.springframework.context.weaving.LoadTimeWeaverAwareProcessor

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   if (bean instanceof LoadTimeWeaverAware) {
      LoadTimeWeaver ltw = this.loadTimeWeaver;
      if (ltw == null) {
      	 // 通過spring 的beanFactory去獲取LoadTimeWeaver bean
         ltw = this.beanFactory.getBean(
               ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
      }
      // 這里,會把LoadTimeWeaver bean,設置給AspectJWeavingEnabler 這個bean
      ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
   }
   return bean;
}

LoadTimeWeaver bean的獲取

上面,我們看到了,在LoadTimeWeaverAwareProcessor 里,要去通過getBean(LoadTimeWeaver.class)來獲取LoadTimeWeaver。

我們知道,這個bean definition已經在解析context:load-time-weaver時注冊了,其類型為:

DefaultContextLoadTimeWeaver,這個bean class不特別,一個普通bean,實現了LoadTimeWeaver接口,還實現了一個生命周期接口:BeanClassLoaderAware

// 需要感知BeanClassLoader,因此實現了BeanClassLoaderAware
public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLoaderAware, DisposableBean

public interface BeanClassLoaderAware extends Aware {
	void setBeanClassLoader(ClassLoader classLoader);
}

我們先看看其實現的功能接口LoadTimeWeaver:

public interface LoadTimeWeaver {
   // 這個方法,我們可以傳:類字節碼轉換器;也就是說,切面的那些邏輯,就是封裝為ClassFileTransformer傳遞進去
   void addTransformer(ClassFileTransformer transformer);

   ClassLoader getInstrumentableClassLoader();
}

我們再看看它的setBeanClassLoader方法,有什么特別的沒:

@Override
public void setBeanClassLoader(ClassLoader classLoader) {
   // 根據classloader來創建容器相關的 LoadTimeWeaver
   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()) {
      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");
      }
   }
}

這個方法,其實很關鍵,這里就會根據當前的classloader,來判斷當前屬於哪個容器環境。這個邏輯在createServerSpecificLoadTimeWeaver里,如果當前classloader的名字,以org.apache.catalina開頭,說明當前是在tomcat里運行,就會創建tomcat的相應實現類的實例。

protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
   String name = classLoader.getClass().getName();
   try {
      if (name.startsWith("weblogic")) {
         return new WebLogicLoadTimeWeaver(classLoader);
      }
      else if (name.startsWith("org.glassfish")) {
         return new GlassFishLoadTimeWeaver(classLoader);
      }
      // 如果當前classloader的名字,以org.apache.catalina開頭,說明當前是在tomcat里運行
      else if (name.startsWith("org.apache.catalina")) {
         return new TomcatLoadTimeWeaver(classLoader);
      }
      else if (name.startsWith("org.jboss")) {
         return new JBossLoadTimeWeaver(classLoader);
      }
      else if (name.startsWith("com.ibm")) {
         return new WebSphereLoadTimeWeaver(classLoader);
      }
   }
   catch (IllegalStateException ex) {
      logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage());
   }
   return null;
}

大家可以看看類圖:

所以,大家看到,LoadTimeWeaver有多種實現,前面就會根據當前classloader的名稱(比如在tomcat時,當前線程的classloader是org.apache.catalina.loader.WebappClassLoader,來創建LoadTimeWeaver在tomcat下的實現類TomcatLoadTimeWeaver的實例)

如果是獨立的java應用,則會創建InstrumentationLoadTimeWeaver 這種實現類的實例,供后續使用。

AspectJWeavingEnabler這個BeanFactoryPostProcessor 如何工作

經過上面的講解,我們獲取到了LoadTimeWeaver bean,最終呢,這個bean也會設置到AspectJWeavingEnabler 里面。

為啥呢,因為AspectJWeavingEnabler實現了 LoadTimeWeaverAware的,還記得吧。

public class AspectJWeavingEnabler
		implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered

那么,一切就緒,我們看看這個BeanFactoryPostProcessor是怎么處理spring 的bean definition的。

public class AspectJWeavingEnabler
		implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {

	private ClassLoader beanClassLoader;

	private LoadTimeWeaver loadTimeWeaver;

	public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";


	public void setBeanClassLoader(ClassLoader classLoader) {
		this.beanClassLoader = classLoader;
	}

	public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
		this.loadTimeWeaver = loadTimeWeaver;
	}
	
    // ok,就是這里
    @override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
         //調用
		enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
	}
	
    
	public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader beanClassLoader) {
         // 這里,因為weaverToUse已經是有值了,所以,會直接進入下面去
		if (weaverToUse == null) {
			if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
				weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
			}
			else {
				throw new IllegalStateException("No LoadTimeWeaver available");
			}
		}
        // 針對注入進來的LoadTimeWeaver,調用它的addTransformer,把aspectJ的ClassFileTransformer設置進去
		weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer(
					new ClassPreProcessorAgentAdapter()));
	}

這里的weaverToUse,我們知道,就是前面說的DefaultContextLoadTimeWeaver。

我們看看DefaultContextLoadTimeWeaver的addTransformer方法,發現它代理給了具體的LoadTimeWeaver:


	public void addTransformer(ClassFileTransformer transformer) {
		this.loadTimeWeaver.addTransformer(transformer);
	}

tomcat下運行時的實現類

假設我們是在tomcat模式下運行,這里實際調用的,就是tomcat的實現類:

public class TomcatLoadTimeWeaver implements LoadTimeWeaver {

	private static final String INSTRUMENTABLE_LOADER_CLASS_NAME = "org.apache.tomcat.InstrumentableClassLoader";


	private final ClassLoader classLoader;

	private final Method addTransformerMethod;

	private final Method copyMethod;


	public TomcatLoadTimeWeaver() {
		this(ClassUtils.getDefaultClassLoader());
	}

	public TomcatLoadTimeWeaver(ClassLoader classLoader) {
		Assert.notNull(classLoader, "ClassLoader must not be null");
		this.classLoader = classLoader;

		Class<?> instrumentableLoaderClass;
		try {
			instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME);
			if (!instrumentableLoaderClass.isInstance(classLoader)) {
				// Could still be a custom variant of a convention-compatible ClassLoader
				instrumentableLoaderClass = classLoader.getClass();
			}
		}
		catch (ClassNotFoundException ex) {
			// We're on an earlier version of Tomcat, probably with Spring's TomcatInstrumentableClassLoader
			instrumentableLoaderClass = classLoader.getClass();
		}

		try {
			this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class);
			// Check for Tomcat's new copyWithoutTransformers on InstrumentableClassLoader first
			Method copyMethod = ClassUtils.getMethodIfAvailable(instrumentableLoaderClass, "copyWithoutTransformers");
			if (copyMethod == null) {
				// Fallback: expecting TomcatInstrumentableClassLoader's getThrowawayClassLoader
				copyMethod = instrumentableLoaderClass.getMethod("getThrowawayClassLoader");
			}
			this.copyMethod = copyMethod;
		}
		catch (Throwable ex) {
			throw new IllegalStateException(
					"Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available", ex);
		}
	}


	@Override
	public void addTransformer(ClassFileTransformer transformer) {
        this.addTransformerMethod.invoke(this.classLoader, transformer);
	}

}

其實,這里的addTransformer實現,就是調用了addTransformerMethod 這個method,這個method呢,其實就是:

org.apache.tomcat.InstrumentableClassLoader.addTransformerMethod (ClassFileTransformer transformer)方法,有興趣大家可以翻到第14篇看一下,里面很詳細介紹了tomcat的實現細節。

java獨立應用時的實現類

此時的實現類,就是InstrumentationLoadTimeWeaver。


public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {

	private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent(
			"org.springframework.instrument.InstrumentationSavingAgent",
			InstrumentationLoadTimeWeaver.class.getClassLoader());


	private final ClassLoader classLoader;

	private final Instrumentation instrumentation;

	private final List<ClassFileTransformer> transformers = new ArrayList<ClassFileTransformer>(4);


	/**
	 * Create a new InstrumentationLoadTimeWeaver for the default ClassLoader.
	 */
	public InstrumentationLoadTimeWeaver() {
		this(ClassUtils.getDefaultClassLoader());
	}

	/**
	 * Create a new InstrumentationLoadTimeWeaver for the given ClassLoader.
	 * @param classLoader the ClassLoader that registered transformers are supposed to apply to
	 */
	public InstrumentationLoadTimeWeaver(ClassLoader classLoader) {
		Assert.notNull(classLoader, "ClassLoader must not be null");
		this.classLoader = classLoader;
		this.instrumentation = getInstrumentation();
	}


	@Override
	public void addTransformer(ClassFileTransformer transformer) {
		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.");
			}
			this.instrumentation.addTransformer(actualTransformer);
			this.transformers.add(actualTransformer);
		}
	}


	/**
	 * Obtain the Instrumentation instance for the current VM, if available.
	 * @return the Instrumentation instance, or {@code null} if none found
	 * @see #isInstrumentationAvailable()
	 */
	private static Instrumentation getInstrumentation() {
		if (AGENT_CLASS_PRESENT) {
			return InstrumentationAccessor.getInstrumentation();
		}
		else {
			return null;
		}
	}


	/**
	 * Inner class to avoid InstrumentationSavingAgent dependency.
	 */
	private static class InstrumentationAccessor {

		public static Instrumentation getInstrumentation() {
			return InstrumentationSavingAgent.getInstrumentation();
		}
	}


	/**
	 * Decorator that only applies the given target transformer to a specific ClassLoader.
	 */
	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;
		}

		@Override
		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);
		}
	}

}

核心就兩點,如果啟動時,加了下面的參數

-javaagent:E:\repo\org\springframework\spring-instrument\4.3.7.RELEASE\spring-instrument-4.3.7.RELEASE.jar

下面這個語句就是true,org.springframework.instrument.InstrumentationSavingAgent這個類里的static字段,就會將JVM暴露給我們的instrumentation保存下來。

private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent(
		"org.springframework.instrument.InstrumentationSavingAgent",
		InstrumentationLoadTimeWeaver.class.getClassLoader());

然后我們這里的addTransformer方法,就可以將ClassFileTransformer設置到instrumentation里面去:

@Override
public void addTransformer(ClassFileTransformer transformer) {
	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.");
		}
         // 這一句是核心! 將類字節碼轉換器,add到instrumentation字段。
		this.instrumentation.addTransformer(actualTransformer);
		this.transformers.add(actualTransformer);
	}
}

前面也說了,這個字段就是jvm暴露給我們的,所以我們對其進行操作,給它設置了ClassFileTransformer,以完成ltw的功能。

總結

這一篇有點長,我感覺寫了好久,但如果大家能細細閱讀並理解的話,我覺得目的也就達到了。但是,這個東西本身足夠復雜,所以,寫得肯定有不那么容易懂的地方,大家可以問我。


免責聲明!

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



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