当前位置: 首页 > news >正文

[spring6: @EnableLoadTimeWeaving]-使用案例

源码

EnableLoadTimeWeaving

@EnableLoadTimeWeaving 注解用于启用加载时织入(LTW),可通过配置 aspectjWeaving 属性控制是否启用或自动探测 AspectJ 织入。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LoadTimeWeavingConfiguration.class)
public @interface EnableLoadTimeWeaving {AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;enum AspectJWeaving {ENABLED, DISABLED, AUTODETECT}
}

LoadTimeWeavingConfiguration

LoadTimeWeavingConfiguration 根据 @EnableLoadTimeWeaving 注解配置加载时织入环境,提供 LoadTimeWeaver Bean 并根据策略启用 AspectJ 织入。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {@Nullableprivate AnnotationAttributes enableLTW;@Nullableprivate LoadTimeWeavingConfigurer ltwConfigurer;@Nullableprivate ClassLoader beanClassLoader;@Overridepublic void setImportMetadata(AnnotationMetadata importMetadata) {this.enableLTW = AnnotationConfigUtils.attributesFor(importMetadata, EnableLoadTimeWeaving.class);if (this.enableLTW == null) {throw new IllegalArgumentException("@EnableLoadTimeWeaving is not present on importing class " + importMetadata.getClassName());}}@Autowired(required = false)public void setLoadTimeWeavingConfigurer(LoadTimeWeavingConfigurer ltwConfigurer) {this.ltwConfigurer = ltwConfigurer;}@Overridepublic void setBeanClassLoader(ClassLoader beanClassLoader) {this.beanClassLoader = beanClassLoader;}@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 instanceloadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver();}if (loadTimeWeaver == null) {// No custom LoadTimeWeaver provided -> fall back to the defaultloadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);}if (this.enableLTW != null) {AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");switch (aspectJWeaving) {case DISABLED -> {// AJ weaving is disabled -> do nothing}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 -> enableAspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);}case ENABLED -> {AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);}}}return loadTimeWeaver;}
}

重点:AspectJWeavingEnabler

AspectJWeavingEnabler 是一个 BeanFactoryPostProcessor,用于在容器启动时注册 AspectJ 的类文件转换器以启用加载时织入,并屏蔽对自身(org.aspectj)类的处理。

public class AspectJWeavingEnabler implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";@Nullableprivate ClassLoader beanClassLoader;@Nullableprivate LoadTimeWeaver loadTimeWeaver;@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.beanClassLoader = classLoader;}@Overridepublic void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {this.loadTimeWeaver = loadTimeWeaver;}@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);}public static void enableAspectJWeaving(@Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {if (weaverToUse == null) {if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {// 说明 JVM 是通过 -javaagent:spring-instrument.jar 启动的weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);}else {throw new IllegalStateException("No LoadTimeWeaver available");}}weaverToUse.addTransformer(// AspectJClassBypassingClassFileTransformer 是一个包装器// 跳过 org.aspectj 自身的类避免递归织入new AspectJClassBypassingClassFileTransformer(// ClassPreProcessorAgentAdapter 是用于将 AspectJ 的 ClassPreProcessor 接入 Java 1.5 的 ClassFileTransformer 接口,// 实现类加载时的字节码织入,并支持类的热更新(reweaving)。new ClassPreProcessorAgentAdapter()));}private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {private final ClassFileTransformer delegate;public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {this.delegate = delegate;}@Overridepublic 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;}return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);}}
}

LoadTimeWeavingConfigurer

LoadTimeWeavingConfigurer 是一个用于提供自定义 LoadTimeWeaver 实例的回调接口。

public interface LoadTimeWeavingConfigurer {LoadTimeWeaver getLoadTimeWeaver();
}

LoadTimeWeaver

LoadTimeWeaver 接口定义了在类加载时添加字节码转换器的能力,并提供可用于织入操作的类加载器。

public interface LoadTimeWeaver {void addTransformer(ClassFileTransformer transformer);ClassLoader getInstrumentableClassLoader();ClassLoader getThrowawayClassLoader();
}

1. DefaultContextLoadTimeWeaver

DefaultContextLoadTimeWeaver 是 Spring 默认的加载时织入适配器,会根据当前环境(Tomcat、GlassFish、JBoss 或是否存在 spring-instrument 的 JVM agent)自动选择合适的 LoadTimeWeaver 实现,用于支持 AspectJ 等织入机制。

public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLoaderAware, DisposableBean {protected final Log logger = LogFactory.getLog(getClass());@Nullableprivate LoadTimeWeaver loadTimeWeaver;public DefaultContextLoadTimeWeaver() {}public DefaultContextLoadTimeWeaver(ClassLoader beanClassLoader) {setBeanClassLoader(beanClassLoader);}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);if (serverSpecificLoadTimeWeaver != null) {if (logger.isDebugEnabled()) {logger.debug("Determined server-specific load-time weaver: " +serverSpecificLoadTimeWeaver.getClass().getName());}this.loadTimeWeaver = serverSpecificLoadTimeWeaver;}else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {logger.debug("Found Spring's JVM agent for instrumentation");this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);}else {try {this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);if (logger.isDebugEnabled()) {logger.debug("Using 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:spring-instrument-{version}.jar");}}}@Nullableprotected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {String name = classLoader.getClass().getName();try {if (name.startsWith("org.apache.catalina")) {return new TomcatLoadTimeWeaver(classLoader);}else if (name.startsWith("org.glassfish")) {return new GlassFishLoadTimeWeaver(classLoader);}else if (name.startsWith("org.jboss.modules")) {return new JBossLoadTimeWeaver(classLoader);}}catch (Exception ex) {if (logger.isInfoEnabled()) {logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage());}}return null;}@Overridepublic void destroy() {if (this.loadTimeWeaver instanceof InstrumentationLoadTimeWeaver iltw) {if (logger.isDebugEnabled()) {logger.debug("Removing all registered transformers for class loader: " +this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());}iltw.removeTransformers();}}@Overridepublic void addTransformer(ClassFileTransformer transformer) {Assert.state(this.loadTimeWeaver != null, "Not initialized");this.loadTimeWeaver.addTransformer(transformer);}@Overridepublic ClassLoader getInstrumentableClassLoader() {Assert.state(this.loadTimeWeaver != null, "Not initialized");return this.loadTimeWeaver.getInstrumentableClassLoader();}@Overridepublic ClassLoader getThrowawayClassLoader() {Assert.state(this.loadTimeWeaver != null, "Not initialized");return this.loadTimeWeaver.getThrowawayClassLoader();}}

2. InstrumentationLoadTimeWeaver

InstrumentationLoadTimeWeaver 是基于 JVM Instrumentation 的加载时织入器(LoadTimeWeaver),用于在运行时向类加载器注册 ClassFileTransformer,从而支持 AOP、JPA 等框架进行字节码增强。必须使用 -javaagent:<path>/spring-instrument-{version}.jar 启动 JVM,否则 Instrumentation 不可用。

public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent("org.springframework.instrument.InstrumentationSavingAgent",InstrumentationLoadTimeWeaver.class.getClassLoader());@Nullableprivate final ClassLoader classLoader;@Nullableprivate final Instrumentation instrumentation;private final List<ClassFileTransformer> transformers = new ArrayList<>(4);public InstrumentationLoadTimeWeaver() {this(ClassUtils.getDefaultClassLoader());}public InstrumentationLoadTimeWeaver(@Nullable ClassLoader classLoader) {this.classLoader = classLoader;this.instrumentation = getInstrumentation();}@Overridepublic void addTransformer(ClassFileTransformer transformer) {Assert.notNull(transformer, "Transformer must not be null");FilteringClassFileTransformer actualTransformer =new FilteringClassFileTransformer(transformer, this.classLoader);synchronized (this.transformers) {Assert.state(this.instrumentation != null,"Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.");this.instrumentation.addTransformer(actualTransformer);this.transformers.add(actualTransformer);}}@Overridepublic ClassLoader getInstrumentableClassLoader() {Assert.state(this.classLoader != null, "No ClassLoader available");return this.classLoader;}@Overridepublic ClassLoader getThrowawayClassLoader() {return new SimpleThrowawayClassLoader(getInstrumentableClassLoader());}public void removeTransformers() {synchronized (this.transformers) {if (this.instrumentation != null && !this.transformers.isEmpty()) {for (int i = this.transformers.size() - 1; i >= 0; i--) {this.instrumentation.removeTransformer(this.transformers.get(i));}this.transformers.clear();}}}public static boolean isInstrumentationAvailable() {return (getInstrumentation() != null);}@Nullableprivate static Instrumentation getInstrumentation() {if (AGENT_CLASS_PRESENT) {return InstrumentationAccessor.getInstrumentation();}else {return null;}}private static class InstrumentationAccessor {public static Instrumentation getInstrumentation() {return InstrumentationSavingAgent.getInstrumentation();}}private static class FilteringClassFileTransformer implements ClassFileTransformer {private final ClassFileTransformer targetTransformer;@Nullableprivate final ClassLoader targetClassLoader;public FilteringClassFileTransformer(ClassFileTransformer targetTransformer, @Nullable ClassLoader targetClassLoader) {this.targetTransformer = targetTransformer;this.targetClassLoader = targetClassLoader;}@Override@Nullablepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {if (this.targetClassLoader != loader) {return null;}return this.targetTransformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);}@Overridepublic String toString() {return "FilteringClassFileTransformer for: " + this.targetTransformer.toString();}}}

3. ReflectiveLoadTimeWeaver

ReflectiveLoadTimeWeaver 使用反射调用 ClassLoader 提供的 addTransformer() 和可选的 getThrowawayClassLoader() 方法,实现类加载时的字节码增强功能,适用于底层类加载器不可直接访问或跨类加载器场景。

public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver {private static final String ADD_TRANSFORMER_METHOD_NAME = "addTransformer";private static final String GET_THROWAWAY_CLASS_LOADER_METHOD_NAME = "getThrowawayClassLoader";private static final Log logger = LogFactory.getLog(ReflectiveLoadTimeWeaver.class);private final ClassLoader classLoader;private final Method addTransformerMethod;@Nullableprivate final Method getThrowawayClassLoaderMethod;public ReflectiveLoadTimeWeaver() {this(ClassUtils.getDefaultClassLoader());}public ReflectiveLoadTimeWeaver(@Nullable ClassLoader classLoader) {Assert.notNull(classLoader, "ClassLoader must not be null");this.classLoader = classLoader;Method addTransformerMethod = ClassUtils.getMethodIfAvailable(this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class);if (addTransformerMethod == null) {throw new IllegalStateException("ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide an " +"'addTransformer(ClassFileTransformer)' method.");}this.addTransformerMethod = addTransformerMethod;Method getThrowawayClassLoaderMethod = ClassUtils.getMethodIfAvailable(this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME);// getThrowawayClassLoader method is optionalif (getThrowawayClassLoaderMethod == null) {if (logger.isDebugEnabled()) {logger.debug("The ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide a " +"'getThrowawayClassLoader()' method; SimpleThrowawayClassLoader will be used instead.");}}this.getThrowawayClassLoaderMethod = getThrowawayClassLoaderMethod;}@Overridepublic void addTransformer(ClassFileTransformer transformer) {Assert.notNull(transformer, "Transformer must not be null");ReflectionUtils.invokeMethod(this.addTransformerMethod, this.classLoader, transformer);}@Overridepublic ClassLoader getInstrumentableClassLoader() {return this.classLoader;}@Overridepublic ClassLoader getThrowawayClassLoader() {if (this.getThrowawayClassLoaderMethod != null) {ClassLoader target = (ClassLoader)ReflectionUtils.invokeMethod(this.getThrowawayClassLoaderMethod, this.classLoader);return (target instanceof DecoratingClassLoader ? target :new OverridingClassLoader(this.classLoader, target));}else {return new SimpleThrowawayClassLoader(this.classLoader);}}
}

例子

配置

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>xyz.idoly</groupId><artifactId>ltw-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>ltw-demo</name><properties><java.version>24</java.version></properties><dependencies><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.24</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-instrument</artifactId></dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies><build><plugins><plugin><groupId>dev.aspectj</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.14.1</version><dependencies><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>1.9.24</version></dependency></dependencies><configuration><complianceLevel>24</complianceLevel></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

resources\META-INF\aop.xml

<aspectj><weaver><include within="xyz.idoly.demo.entity.*"/></weaver><aspects><aspect name="xyz.idoly.demo.aspectj.CustomAspect"/></aspects>
</aspectj>

代码

CustomEntity

package xyz.idoly.demo.entity;import org.springframework.stereotype.Component;@Component
public class CustomEntity {public void customMethod() {System.out.println("customMethod");}    
}

CustomAspect

package xyz.idoly.demo.aspectj;import org.aspectj.lang.ProceedingJoinPoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public aspect CustomAspect{private static final Log log = LogFactory.getLog(CustomAspect.class);pointcut customMethodExecution(): execution(void xyz.idoly.demo.entity.CustomEntity.customMethod());Object around(): customMethodExecution() {String methodName = ((ProceedingJoinPoint)thisJoinPoint).getSignature().toShortString();Object result = null;log.info("AJ Aspect: Around customMethod - Before execution of " + methodName);try {result = proceed();log.info("AJ Aspect: Around customMethod - After successful execution of " + methodName);} catch (Throwable e) {log.error("AJ Aspect: Around customMethod - Exception in " + methodName + " with error: " + e.getMessage());if (e instanceof RuntimeException) {throw (RuntimeException) e;} else {throw new RuntimeException("AspectJ caught checked exception in " + methodName, e); }}return result;}
}

Application

package xyz.idoly.demo;import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableLoadTimeWeaving;import xyz.idoly.demo.entity.CustomEntity;@EnableLoadTimeWeaving
@SpringBootApplication
public class Application {@Beanpublic CommandLineRunner commandLineRunner(CustomEntity customEntity) {return args -> customEntity.customMethod();}public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

结果

PS D:\workspace\java\ltw-demo> mvnd clean package
PS D:\workspace\java\ltw-demo> java -javaagent:.\spring-instrument-6.2.8.jar  -jar .\target\ltw-demo-0.0.1-SNAPSHOT.jar.   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::                (v3.5.3)2025-07-11T23:33:19.886+08:00  INFO 15172 --- [ltw-demo] [           main] xyz.idoly.demo.Application               : Starting Application v0.0.1-SNAPSHOT using Java 24.0.1 with PID 15172 (D:\workspace\java\redis-demo\target\ltw-demo-0.0.1-SNAPSHOT.jar started by idoly in D:\workspace\java\redis-demo)
2025-07-11T23:33:19.888+08:00  INFO 15172 --- [ltw-demo] [           main] xyz.idoly.demo.Application               : No active profile set, falling back to 1 default profile: "default"
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor (jar:nested:/D:/workspace/java/ltw-demo/target/ltw-demo-0.0.1-SNAPSHOT.jar/!BOOT-INF/lib/aspectjweaver-1.9.24.jar!/)
WARNING: Please consider reporting this to the maintainers of class org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
2025-07-11T23:33:20.512+08:00  INFO 15172 --- [ltw-demo] [           main] xyz.idoly.demo.Application               : Started Application in 1.001 seconds (process running for 1.322)
2025-07-11T23:33:20.517+08:00  INFO 15172 --- [ltw-demo] [           main] xyz.idoly.demo.aspectj.CustomAspect      : AJ Aspect: Around customMethod - Before execution of customMethod
customMethod
2025-07-11T23:33:20.518+08:00  INFO 15172 --- [ltw-demo] [           main] xyz.idoly.demo.aspectj.CustomAspect      : AJ Aspect: Around customMethod - After successful execution of customMethod
http://www.lryc.cn/news/585787.html

相关文章:

  • 人脸图像生成(DCGAN)
  • Linux入门篇学习——Linux 编写第一个自己的命令,make 工具和 makefile 文件
  • C++编程基础
  • 大模型在卵巢癌预测及诊疗方案制定中的应用研究
  • Linux驱动基本概念(内核态、用户态、模块、加载、卸载、设备注册、字符设备)
  • Allegro 17.4操作记录
  • 【理念●体系】从零打造 Windows + WSL + Docker + Anaconda + PyCharm 的 AI 全链路开发体系
  • 数据库系统的基础知识(三)
  • uniapp---入门、基本配置了解
  • spring-ai RAG(Retrieval-Augmented Generation)
  • ESP32_启动日志分析
  • 力扣 hot100 Day41
  • RLHF:人类反馈强化学习 | 对齐AI与人类价值观的核心引擎
  • Linux711 Mysql
  • openpilot:为您的汽车插上智能驾驶的翅膀
  • 创意总监的动态视觉秘诀:用AE动态遮罩AI,轻松实现“人景分离”
  • 【每日刷题】加一
  • Java 中的锁分类
  • 【牛客刷题】吃糖果----糖果甜度问题(贪心策略详解)
  • 小车循迹功能的实现(第六天)
  • UML 与 SysML 图表对比全解析:软件工程 vs 系统工程建模语言
  • 持有对象-泛型和类型安全的容器
  • 线程通信V
  • 【Linux】系统引导修复
  • InnoDB 存储引擎的 架构
  • 渗透测试之木马后门实验
  • 世界现存燃油汽车品牌起源国别梳理
  • k8s新增jupyter服务
  • 中国国际会议会展中心模块化解决方案的技术经济分析报告
  • 【机器学习应用】基于集成学习的电力负荷预测系统实战案例