曹工說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,我這邊有兩個:
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後,可以看到效果:
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:
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; }
大家在前面看到了,除了上面註冊的這個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介面的。
按理說,正常的流程是:
- spring掃描xml或註解,獲取註冊的bean definition
- BeanFactoryPostProcessor對第一步獲取到的bean definition進行處理,可能是修改,也有可能會註冊更多的bean definition,這一步完成後,才是最終的bean definition集合
- 對全部的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的功能。
總結
這一篇有點長,我感覺寫了好久,但如果大家能細細閱讀並理解的話,我覺得目的也就達到了。但是,這個東西本身足夠複雜,所以,寫得肯定有不那麼容易懂的地方,大家可以問我