源码
EnableLoadTimeWeaving
@EnableLoadTimeWeaving
注解用于启用加载时织入(LTW),可通过配置 aspectjWeaving
属性控制是否启用或自动探测 AspectJ 织入。
java
@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 织入。
java
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
@Nullable
private AnnotationAttributes enableLTW;
@Nullable
private LoadTimeWeavingConfigurer ltwConfigurer;
@Nullable
private ClassLoader beanClassLoader;
@Override
public 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;
}
@Override
public 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 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
}
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);
}
case ENABLED -> {
AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
}
}
}
return loadTimeWeaver;
}
}
重点:AspectJWeavingEnabler
AspectJWeavingEnabler
是一个 BeanFactoryPostProcessor
,用于在容器启动时注册 AspectJ 的类文件转换器以启用加载时织入,并屏蔽对自身(org.aspectj
)类的处理。
java
public class AspectJWeavingEnabler implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {
public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";
@Nullable
private ClassLoader beanClassLoader;
@Nullable
private LoadTimeWeaver loadTimeWeaver;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
this.loadTimeWeaver = loadTimeWeaver;
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
@Override
public 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;
}
@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;
}
return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
}
}
}
LoadTimeWeavingConfigurer
LoadTimeWeavingConfigurer
是一个用于提供自定义 LoadTimeWeaver
实例的回调接口。
java
public interface LoadTimeWeavingConfigurer {
LoadTimeWeaver getLoadTimeWeaver();
}
LoadTimeWeaver
LoadTimeWeaver 接口定义了在类加载时添加字节码转换器的能力,并提供可用于织入操作的类加载器。
java
public interface LoadTimeWeaver {
void addTransformer(ClassFileTransformer transformer);
ClassLoader getInstrumentableClassLoader();
ClassLoader getThrowawayClassLoader();
}
1. DefaultContextLoadTimeWeaver
DefaultContextLoadTimeWeaver
是 Spring 默认的加载时织入适配器,会根据当前环境(Tomcat、GlassFish、JBoss 或是否存在 spring-instrument 的 JVM agent)自动选择合适的 LoadTimeWeaver 实现,用于支持 AspectJ 等织入机制。
java
public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLoaderAware, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private LoadTimeWeaver loadTimeWeaver;
public DefaultContextLoadTimeWeaver() {
}
public DefaultContextLoadTimeWeaver(ClassLoader beanClassLoader) {
setBeanClassLoader(beanClassLoader);
}
@Override
public 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");
}
}
}
@Nullable
protected 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;
}
@Override
public 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();
}
}
@Override
public void addTransformer(ClassFileTransformer transformer) {
Assert.state(this.loadTimeWeaver != null, "Not initialized");
this.loadTimeWeaver.addTransformer(transformer);
}
@Override
public ClassLoader getInstrumentableClassLoader() {
Assert.state(this.loadTimeWeaver != null, "Not initialized");
return this.loadTimeWeaver.getInstrumentableClassLoader();
}
@Override
public 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 不可用。
java
public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {
private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent(
"org.springframework.instrument.InstrumentationSavingAgent",
InstrumentationLoadTimeWeaver.class.getClassLoader());
@Nullable
private final ClassLoader classLoader;
@Nullable
private 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();
}
@Override
public 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);
}
}
@Override
public ClassLoader getInstrumentableClassLoader() {
Assert.state(this.classLoader != null, "No ClassLoader available");
return this.classLoader;
}
@Override
public 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);
}
@Nullable
private 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;
@Nullable
private final ClassLoader targetClassLoader;
public FilteringClassFileTransformer(
ClassFileTransformer targetTransformer, @Nullable ClassLoader targetClassLoader) {
this.targetTransformer = targetTransformer;
this.targetClassLoader = targetClassLoader;
}
@Override
@Nullable
public 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);
}
@Override
public String toString() {
return "FilteringClassFileTransformer for: " + this.targetTransformer.toString();
}
}
}
3. ReflectiveLoadTimeWeaver
ReflectiveLoadTimeWeaver
使用反射调用 ClassLoader 提供的 addTransformer() 和可选的 getThrowawayClassLoader() 方法,实现类加载时的字节码增强功能,适用于底层类加载器不可直接访问或跨类加载器场景。
java
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;
@Nullable
private 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 optional
if (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;
}
@Override
public void addTransformer(ClassFileTransformer transformer) {
Assert.notNull(transformer, "Transformer must not be null");
ReflectionUtils.invokeMethod(this.addTransformerMethod, this.classLoader, transformer);
}
@Override
public ClassLoader getInstrumentableClassLoader() {
return this.classLoader;
}
@Override
public 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
<?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
xml
<aspectj>
<weaver>
<include within="xyz.idoly.demo.entity.*"/>
</weaver>
<aspects>
<aspect name="xyz.idoly.demo.aspectj.CustomAspect"/>
</aspects>
</aspectj>
代码
CustomEntity
java
package xyz.idoly.demo.entity;
import org.springframework.stereotype.Component;
@Component
public class CustomEntity {
public void customMethod() {
System.out.println("customMethod");
}
}
CustomAspect
java
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
java
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 {
@Bean
public CommandLineRunner commandLineRunner(CustomEntity customEntity) {
return args -> customEntity.customMethod();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
结果
powershell
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