第十篇 Spring AOP中Load Time Weaver



前言

本文介绍Spring AOP中Load Time Weaver的初始化过程,对其中有意思的一两个问题做分析和深入讨论,期待对你有所帮助。


一、先聊Load Time

阅读过第九篇的小伙伴,应该了解到class文件的产生和加载有3个大的阶段,编译,加载,运行。其中Load Time就是加载阶段。JDK 5.0之后提供了Instrument API,允许Java Agent注册Class Transformer。当JVM完成原始的class文件加载后,会运行该transformer做进一步的转换。

说到类的加载,日常的项目中有很多类,加载顺序是怎么样的呢?被动加载加主动加载。被动加载可以理解为按需加载,也就是使用时再加载,毕竟这样可以节约内存。主动加载,可以理解为一旦启动必须会用到的那些类,直接加载上得了,这样可以缩短应用初始化时间。

二、再说Weaver

Weaver的作用是将Aspect和具体的JoinPoint关联起来。拿监听者模式来类比,JoinPoint就是一个具体的EventSource,会在必要的时候产生Event,Aspect是对该Event感兴趣的EventListener,Weaver则把EventSource和EventListener关联起来。这里有一个潜在的依赖关系。那EventListener、EventSource两者谁依赖谁呢?

从处理逻辑上,逻辑自EventSource到EventListener,所以是EventListener依赖EventSource。实际的业务场景中,我们可能不希望event有丢失,也就是event肯定会被EventListener处理到,所以我们先初始化好EventListener,然后用EventListener来初始化EventSource。此时剧情又反转了。

总之,这个潜在的依赖关系对最终结果是有影响的。

三、回看Load Time Weaver

Load Time Weaver就是在Load Time将Aspect和JoinPoint Weave起来。玩过@Aspect的都知道,Aspect中声明了一个PointCut,仅站在Java语言层面PointCut的实际内容就是个字符串。假设其要对"org.apache"下的所有类的所有方法做切面通知,该声明不会导致类加载器去加载包“org.apache”下的所有类。这就导致,EventListener ready的时候,找不到具体的EventSource,weave过程失败。

Spring中是这么解决的?a、EventListener在所有的EventSource之前先初始化好,基于JVM agent机制;b、监控当前classLoader后续的加载类,做针对性的transformer处理。

到这里,想必你心中已经建立了关于LTW的导航地图,接下里就按照地图走一波。

四、LTW初始化过程

1、激活

XML方式:

<context:load-time-weaver />

其解析过程通过ContextNamespaceHandler中注册的LoadTimeWeaverBeanDefinitionParser来完成,最终向BeanFactory注册了两个关键类
org.springframework.context.weaving.AspectJWeavingEnabler
org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect

Annotation方式:

@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
@Configuration
public class App {

}

@EnableLoadTimeWeaving 标签的定义中import了LoadTimeWeaverConfiguration,后者实现了ImportAware对当前上下文的Meta做进一步的处理,其中完成了ltw的初始化

@Bean(name = ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public LoadTimeWeaver loadTimeWeaver() {
		Assert.state(this.beanClassLoader != null, "No ClassLoader set");
		LoadTimeWeaver loadTimeWeaver = null;

		if (this.ltwConfigurer != null) {
			// The user has provided a custom LoadTimeWeaver instance
			loadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver();
		}

		if (loadTimeWeaver == null) {
			// No custom LoadTimeWeaver provided -> fall back to the default
			loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);
		}

		if (this.enableLTW != null) {
			AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");
			switch (aspectJWeaving) {
				case DISABLED:
					// AJ weaving is disabled -> do nothing
					break;
				case AUTODETECT:
					if (this.beanClassLoader.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) == null) {
						// No aop.xml present on the classpath -> treat as 'disabled'
						break;
					}
					// aop.xml is present on the classpath -> enable
					AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
					break;
				case ENABLED:
					AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
					break;
			}
		}

		return loadTimeWeaver;
	}

注意这里只是激活,准确说是提议,相当于我们常说的“我想xxx", 具体是否真的能够LTW还要看具体配置。因为Spring AOP的LTW是重用了AspectJ中的LTW能力。因此内部仅做了必要的嫁接,实际的激活还要看基于AspectJ的配置是否到位。

首当其冲的配置自然是Java Agent是否配置,要包含的命令行参数如下:

-javaagent:spring-instrument-5.3.15.jar -javaagent:aspectjweaver-1.9.9.jar

其次是 aop.xml 配置是否存在。注解方式中ENABLED,表示直接启用;AUTO_DETECT 则META-INF/apo.xml的存在性决定是否启用。前者最终也依赖该配置文件;

接下来,进入真正的weave逻辑。

2. Weave过程

  1. 初始化Load Time Weaver
    Spring中的Load Time Weaver默认是DefaultContextLoadTimeWeaver。该类根据实际的运行环境初始化具体的LTW。Spring中提供了3种LTW,ServerSpecificLoadTimeWeaver、InstrumentationLoadTimeWeaver和ReflectiveLoadTimeWeaver。其中InstrumentationLoadTimeWeaver依赖spring-instrument.jar。

  2. 在Weaver上添加Transformer
    这里的Transformer则最终依赖到了AspectJ的ClassPreProcessor。从这里可以看到整个Transformer过程就是AspectJ的处理逻辑了。

public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {
	private static ClassPreProcessor classPreProcessor;

	static {
		try {
			classPreProcessor = new Aj();
			classPreProcessor.initialize();
		} catch (Exception e) {
			throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
		}
	}

	/**
	 * Invokes the weaver to modify some set of input bytes.
	 *
	 * @param loader the defining class loader
	 * @param className the name of class being loaded
	 * @param classBeingRedefined is set when hotswap is being attempted
	 * @param protectionDomain the protection domain for the class being loaded
	 * @param bytes the incoming bytes (before weaving)
	 * @return the woven bytes
	 */
	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] bytes) throws IllegalClassFormatException {
		if (classBeingRedefined != null) {
			System.err.println("INFO: (Enh120375):  AspectJ attempting reweave of '" + className + "'");
			classPreProcessor.prepareForRedefinition(loader, className);
		}
		return classPreProcessor.preProcess(className, bytes, loader, protectionDomain);
	}
}

总结

以上就是今天要聊的全部内容,对LTW的细节做了进一步的探讨。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值