@EnableLoadTimeWeaving源码分析

@EnableAspectJAutoProxy源码分析
master
linlei 2024-04-13 13:56:09 +08:00
parent 34ad90f5e7
commit 7c8a58fad9
34 changed files with 1167 additions and 98 deletions

View File

@ -210,6 +210,8 @@
- [Advisor](spring-aop/spring-aop-advisor/README.md):用于将通知和切点结合,实现切面编程的横切关注点。<img src="https://img.shields.io/badge/Level-%E7%AE%80%E5%8D%95-0099ff"></img>
- [AdvisorAdapter](spring-aop/spring-aop-advisorAdapter/README.md):适配不同类型通知到拦截器链。<img src="https://img.shields.io/badge/Level-%E4%B8%80%E8%88%AC-%23FF6347"></img>
- [TargetSource](spring-aop/spring-aop-targetSource/README.md)管理AOP代理对象的获取与释放。<img src="https://img.shields.io/badge/Level-%E7%AE%80%E5%8D%95-0099ff"></img>
- [@EnableAspectJAutoProxy](spring-aop/spring-aop-enableAspectJAutoProxy/README,md)启用AspectJ切面自动代理。<img src="https://img.shields.io/badge/Level-%E7%AE%80%E5%8D%95-0099ff"></img>
- [@EnableLoadTimeWeaving](spring-aop/spring-aop-enableLoadTimeWeaving/README.md)启用Spring加载时编织。<img src="https://img.shields.io/badge/Level-%E7%AE%80%E5%8D%95-0099ff"></img>
+ Spring AOT

View File

@ -18,16 +18,16 @@
<module>spring-aop-methodMatcher</module>
<module>spring-aop-jdkProxy</module>
<module>spring-aop-cglibProxy</module>
<module>spring-aop-annotation-aspect</module>
<module>spring-aop-annotation-pointcut</module>
<module>spring-aop-annotation-around</module>
<module>spring-aop-annotation-before</module>
<module>spring-aop-annotation-after</module>
<module>spring-aop-annotation-afterReturning</module>
<module>spring-aop-annotation-afterThrowing</module>
<module>spring-aop-annotation-declareParents</module>
<module>spring-aop-annotation-enableAspectJAutoProxy</module>
<module>spring-aop-annotation-enableLoadTimeWeaving</module>
<module>spring-aop-aspectj-aspect</module>
<module>spring-aop-aspectj-pointcut</module>
<module>spring-aop-aspectj-around</module>
<module>spring-aop-aspectj-before</module>
<module>spring-aop-aspectj-after</module>
<module>spring-aop-aspectj-afterReturning</module>
<module>spring-aop-aspectj-afterThrowing</module>
<module>spring-aop-aspectj-declareParents</module>
<module>spring-aop-enableAspectJAutoProxy</module>
<module>spring-aop-enableLoadTimeWeaving</module>
<module>spring-aop-advice-methodInterceptor</module>
<module>spring-aop-advice-methodBeforeAdvice</module>
<module>spring-aop-advice-afterReturningAdvice</module>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-afterReturning</artifactId>
</project>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-afterThrowing</artifactId>
</project>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-declareParents</artifactId>
</project>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-enableAspectJAutoProxy</artifactId>
</project>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-enableLoadTimeWeaving</artifactId>
</project>

View File

@ -1,14 +0,0 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-pointcut</artifactId>
</project>

View File

@ -9,6 +9,6 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-after</artifactId>
<artifactId>spring-aop-aspectj-after</artifactId>
</project>

View File

@ -0,0 +1,14 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-aspectj-afterReturning</artifactId>
</project>

View File

@ -0,0 +1,14 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-aspectj-afterThrowing</artifactId>
</project>

View File

@ -9,6 +9,6 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-aspect</artifactId>
<artifactId>spring-aop-aspectj-around</artifactId>
</project>

View File

@ -0,0 +1,31 @@
## Aspect
- [Aspect](#Aspect)
- [一、基本信息](#一基本信息)
- [二、基本描述](#二基本描述)
- [三、主要功能](#三主要功能)
- [四、接口源码](#四接口源码)
- [五、主要实现](#五主要实现)
- [六、最佳实践](#六最佳实践)
- [七、源码分析](#七源码分析)
- [八、常见问题](#八常见问题)
### 一、基本信息
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、基本描述
### 三、主要功能
### 四、接口源码
### 五、主要实现
### 六、最佳实践
### 七、源码分析
### 八、常见问题

View File

@ -9,6 +9,6 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-before</artifactId>
<artifactId>spring-aop-aspectj-aspect</artifactId>
</project>

View File

@ -0,0 +1,11 @@
package com.xcs.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.xcs.spring")
public class AppConfig {
}

View File

@ -0,0 +1,13 @@
package com.xcs.spring;
import com.xcs.spring.service.MyService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AspectDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService service = context.getBean(MyService.class);
System.out.println("service.getClass() = " + service.getClass());
service.doSomething();
}
}

View File

@ -0,0 +1,15 @@
package com.xcs.spring;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
class LoggingAspect {
@Before("execution(* com.xcs.spring.service.MyService.doSomething(..))")
public void beforeAdvice() {
System.out.println("Before executing the method..." );
}
}

View File

@ -0,0 +1,10 @@
package com.xcs.spring.service;
import org.springframework.stereotype.Service;
@Service
public class MyService {
public void doSomething() {
System.out.println("Doing something...");
}
}

View File

@ -9,6 +9,6 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-annotation-around</artifactId>
<artifactId>spring-aop-aspectj-before</artifactId>
</project>

View File

@ -0,0 +1,14 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-aspectj-declareParents</artifactId>
</project>

View File

@ -0,0 +1,14 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-aspectj-pointcut</artifactId>
</project>

View File

@ -0,0 +1,347 @@
## @EnableAspectJAutoProxy
- [@EnableAspectJAutoProxy](#@EnableAspectJAutoProxy)
- [一、基本信息](#一基本信息)
- [二、基本描述](#二基本描述)
- [三、主要功能](#三主要功能)
- [四、注解源码](#注解源码)
- [五、最佳实践](#五最佳实践)
- [六、源码分析](#六源码分析)
- [七、常见问题](#七常见问题)
### 一、基本信息
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、基本描述
`EnableAspectJAutoProxy`注解是Spring框架中的一个注解用于启用AspectJ自动代理功能它能够自动将AspectJ切面与Spring的IoC容器集成无需显式配置大量AOP相关的内容从而简化AOP的使用和配置。
### 三、主要功能
1. **启用AspectJ自动代理功能**
+ 通过在配置类上添加该注解Spring会自动启用AspectJ自动代理功能无需显式配置大量AOP相关内容。
2. **自动创建代理对象**
+ Spring会自动创建代理对象来应用切面将切面逻辑与目标对象进行结合。
3. **集成AspectJ切面与Spring容器**
+ 能够方便地将AspectJ切面与Spring的IoC容器集成实现横切关注点的模块化管理。
4. **简化AOP配置**
+ 减少了手动配置AOP所需的繁琐步骤提高了开发效率。
5. **自动扫描切面**
+ Spring会自动扫描应用中的AspectJ切面并将其应用到相应的目标对象中无需手动配置切面。
### 四、注解源码
`EnableAspectJAutoProxy`注解用于启用支持处理使用AspectJ的`@Aspect`注解标记的组件在Spring中类似于XML配置中的`<aop:aspectj-autoproxy>`功能。它允许通过配置类轻松集成AspectJ切面并控制代理类型和代理的可见性。
```java
/**
* 启用支持处理使用AspectJ的{@code @Aspect}注解标记的组件,
* 类似于Spring的{@code <aop:aspectj-autoproxy>} XML元素中的功能。
* 应用在如下的@{@link Configuration}类上:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableAspectJAutoProxy
* public class AppConfig {
*
* &#064;Bean
* public FooService fooService() {
* return new FooService();
* }
*
* &#064;Bean
* public MyAspect myAspect() {
* return new MyAspect();
* }
* }</pre>
*
* 这里的{@code FooService}是一个典型的POJO组件{@code MyAspect}是一个
* {@code @Aspect}-风格的切面:
*
* <pre class="code">
* public class FooService {
*
* // 各种方法
* }</pre>
*
* <pre class="code">
* &#064;Aspect
* public class MyAspect {
*
* &#064;Before("execution(* FooService+.*(..))")
* public void advice() {
* // 适当地提供FooService方法的建议
* }
* }</pre>
*
* 在上述场景中,{@code @EnableAspectJAutoProxy}确保{@code MyAspect}
* 将被正确处理,并且{@code FooService}将被代理,混合其中的建议。
*
* <p>用户可以通过{@link #proxyTargetClass()}属性控制为{@code FooService}创建的代理类型。
* 以下示例启用了CGLIB风格的“子类”代理而不是默认的基于接口的JDK代理方式。
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableAspectJAutoProxy(proxyTargetClass=true)
* public class AppConfig {
* // ...
* }</pre>
*
* <p>注意,{@code @Aspect} bean可以像任何其他组件一样进行组件扫描。
* 只需将切面标记为{@code @Aspect}和{@code @Component}
*
* <pre class="code">
* package com.foo;
*
* &#064;Component
* public class FooService { ... }
*
* &#064;Aspect
* &#064;Component
* public class MyAspect { ... }</pre>
*
* 然后使用@{@link ComponentScan}注解来同时选择它们:
*
* <pre class="code">
* &#064;Configuration
* &#064;ComponentScan("com.foo")
* &#064;EnableAspectJAutoProxy
* public class AppConfig {
*
* // 不需要显式的{@code @Bean}定义
* }</pre>
*
* <b>注意:{@code @EnableAspectJAutoProxy}仅适用于其本地应用上下文,
* 允许在不同级别选择性地对bean进行代理。</b>
* 如果需要在多个级别应用其行为例如常见的根Web应用程序上下文和任何单独的{@code DispatcherServlet}应用程序上下文中,
* 请在每个单独的上下文中重新声明{@code @EnableAspectJAutoProxy}。
*
* <p>该功能要求类路径上存在{@code aspectjweaver}。
* 虽然{@code spring-aop}一般情况下对该依赖是可选的,但是对于{@code @EnableAspectJAutoProxy}及其基础设施,它是必需的。
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see org.aspectj.lang.annotation.Aspect
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* 指示是否创建基于子类CGLIB的代理而不是标准的基于Java接口的代理。默认值为{@code false}。
*/
boolean proxyTargetClass() default false;
/**
* 指示代理是否应该被AOP框架公开为一个{@code ThreadLocal},以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。
* 默认情况下关闭,即不保证{@code AopContext}访问将起作用。
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
```
### 五、最佳实践
使用`EnableAspectJAutoProxy`注解和Spring的基于注解的应用上下文来启用AspectJ自动代理功能。在程序中首先创建了一个基于注解的应用上下文然后通过该上下文获取了`FooService` bean并调用了其方法。
```java
public class EnableAspectJAutoProxyDemo {
public static void main(String[] args) {
// 创建基于注解的应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从应用上下文中获取FooService bean
FooService fooService = context.getBean(FooService.class);
// 调用FooService的方法
fooService.foo();
}
}
```
通过`@Configuration`注解表示它是一个配置类用于配置Spring应用的bean。其中通过`@EnableAspectJAutoProxy`注解启用了AspectJ自动代理功能使得Spring能够自动处理切面。在配置中定义了两个bean`FooService`和`MyAspect`,分别用于创建`FooService`的实例和`MyAspect`切面的实例。
```java
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public FooService fooService() {
return new FooService();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
```
`FooService`是一个简单的Java类其中包含了一个名为`foo`的方法。
```java
public class FooService {
public void foo() {
System.out.println("foo");
}
}
```
`MyAspect`是一个使用了`@Aspect`注解的Java类表示它是一个切面。在这个类中定义了一个名为`advice`的方法,并使用了`@Before`注解来指定在目标方法执行之前执行的通知。
```java
@Aspect
public class MyAspect {
@Before("execution(* FooService+.*(..))")
public void advice() {
System.out.println("Before method execution");
}
}
```
### 六、源码分析
在`org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions`方法中首先注册了AspectJ注解自动代理创建器然后获取了`@EnableAspectJAutoProxy`注解的属性。如果`@EnableAspectJAutoProxy`注解中指定了`proxyTargetClass`属性为true则强制使用CGLIB代理如果指定了`exposeProxy`属性为true则强制代理对象暴露为ThreadLocal。
```java
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 1、注册AspectJ注解自动代理创建器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 获取@EnableAspectJAutoProxy注解的属性
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
///2.如果@EnableAspectJAutoProxy注解指定了proxyTargetClass属性为true则强制使用CGLIB代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 3.如果@EnableAspectJAutoProxy注解指定了exposeProxy属性为true则强制代理对象暴露为ThreadLocal
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
```
在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)`方法中注册AspectJ注解自动代理创建器。它提供了一个重载方法允许传入一个额外的参数但这里调用的是没有额外参数的版本。在方法中它会根据给定的`BeanDefinitionRegistry`对象来注册AspectJ注解自动代理创建器并返回相应的BeanDefinition。
```java
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
```
在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry,source)`方法中注册AspectJ注解自动代理创建器并且可以指定源对象。在方法中它调用了一个辅助方法`registerOrEscalateApcAsRequired`该方法会根据需要注册或升级AspectJ注解自动代理创建器并返回相应的BeanDefinition。
```java
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
```
在`org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired`方法中,首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则比较现有自动代理创建器与指定类的优先级,如果指定类的优先级更高,则进行升级操作;如果不存在,则创建一个新的自动代理创建器的定义,并将其注册到给定的`BeanDefinitionRegistry`中。
```java
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// 检查BeanDefinitionRegistry对象是否为null
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经存在相同名称的自动代理创建器,则进行升级操作
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 获取已存在的自动代理创建器的BeanDefinition
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 比较已存在的自动代理创建器与指定类的优先级
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
// 如果指定类的优先级高于已存在的自动代理创建器,则进行升级
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 如果不存在相同名称的自动代理创建器,则进行注册操作
// 创建新的自动代理创建器的BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册新的自动代理创建器的BeanDefinition
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
```
在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying`方法中,强制自动代理创建器使用基于类的代理。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`proxyTargetClass`属性为`true`,表示使用基于类的代理。
```java
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
// 检查是否存在自动代理创建器的BeanDefinition
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 获取自动代理创建器的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 设置proxyTargetClass属性为true表示使用基于类的代理
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
```
在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToExposeProxy`方法中,强制自动代理创建器暴露代理对象。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`exposeProxy`属性为`true`,表示暴露代理对象。
```java
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
// 检查是否存在自动代理创建器的BeanDefinition
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 获取自动代理创建器的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 设置exposeProxy属性为true表示暴露代理对象
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
```
### 七、常见问题
1. **代理类型选择**
+ 在@EnableAspectJAutoProxy注解中有一个proxyTargetClass属性用于指定代理类型。如果选择了错误的代理类型可能会导致意外的行为或错误。通常选择默认的接口代理是比较安全的选择。
2. **切面优先级问题**
+ 如果同时存在多个切面并且它们的通知方法匹配相同的连接点可能会导致切面执行顺序不符合预期。可以通过实现Ordered接口或使用@Order注解来控制切面的执行顺序。
3. **代理对象暴露问题**
+ 在某些情况下,可能需要访问代理对象本身,例如在同一个类的不同方法中调用被代理的方法。这时,需要确保在@EnableAspectJAutoProxy注解中设置了exposeProxy属性为true以便将代理对象暴露为ThreadLocal。
4. **Spring配置问题**
+ 配置类中的@EnableAspectJAutoProxy注解可能会被错误地放置在不正确的位置或者与其他注解冲突。因此需要确保@EnableAspectJAutoProxy注解正确地放置在@Configuration类上并且没有与其他注解冲突。

View File

@ -0,0 +1,14 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-enableAspectJAutoProxy</artifactId>
</project>

View File

@ -0,0 +1,20 @@
package com.xcs.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public FooService fooService() {
return new FooService();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}

View File

@ -0,0 +1,15 @@
package com.xcs.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class EnableAspectJAutoProxyDemo {
public static void main(String[] args) {
// 创建基于注解的应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从应用上下文中获取FooService bean
FooService fooService = context.getBean(FooService.class);
// 调用FooService的方法
fooService.foo();
}
}

View File

@ -0,0 +1,8 @@
package com.xcs.spring;
public class FooService {
public void foo() {
System.out.println("foo");
}
}

View File

@ -0,0 +1,13 @@
package com.xcs.spring;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(* FooService+.*(..))")
public void advice() {
System.out.println("Before method execution");
}
}

View File

@ -0,0 +1,491 @@
## @EnableLoadTimeWeaving
- [@EnableLoadTimeWeaving](#@EnableLoadTimeWeaving)
- [一、基本信息](#一基本信息)
- [二、基本描述](#二基本描述)
- [三、主要功能](#三主要功能)
- [四、注解源码](#注解源码)
- [五、最佳实践](#五最佳实践)
- [六、源码分析](#六源码分析)
- [七、常见问题](#七常见问题)
### 一、基本信息
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、基本描述
`@EnableLoadTimeWeaving` 是 Spring 框架提供的注解用于启用加载时编织Load Time WeavingLTW允许在类加载过程中动态织入切面逻辑以实现诸如日志记录、性能监控等横切关注点的功能。
### 三、主要功能
1. **启用加载时编织LTW**
+ 允许在类加载的过程中动态地织入切面逻辑,而无需修改源代码或者使用特定的编译器。
2. **支持横切关注点的实现**
+ 通过加载时编织,可以将横切关注点与应用的核心业务逻辑分离,提高代码的模块化和可维护性。
3. **灵活性和可配置性**
+ 可以通过 AspectJ 提供的丰富语法和功能,灵活地定义切点和切面逻辑,以满足各种复杂的应用场景。
4. **不依赖源代码**
+ 加载时编织不依赖于源代码的修改或特殊的编译器,因此可以在已有的应用中轻松地引入切面逻辑,而无需对现有代码进行重构。
### 四、注解源码
注解 `@EnableLoadTimeWeaving`,用于激活 Spring 应用上下文中的加载时编织Load Time Weaving。通过该注解可以方便地配置加载时编织类似于 Spring XML 配置中的 `<context:load-time-weaver>` 元素。同时,还可以通过 `aspectjWeaving()` 属性控制是否启用基于 AspectJ 的编织,提供了灵活的配置选项。
```java
/**
* 激活一个 Spring {@link LoadTimeWeaver} 用于该应用程序上下文,可作为一个名为 "loadTimeWeaver" 的 bean 使用,
* 类似于 Spring XML 中的 {@code <context:load-time-weaver>} 元素。
*
* <p>要在 @{@link org.springframework.context.annotation.Configuration Configuration} 类上使用;
* 最简单的示例如下
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableLoadTimeWeaving
* public class AppConfig {
*
* // 应用特定的 &#064;Bean 定义...
* }</pre>
*
* 上面的示例等价于以下的 Spring XML 配置
*
* <pre class="code">
* &lt;beans&gt;
*
* &lt;context:load-time-weaver/&gt;
*
* &lt;!-- 应用特定的 &lt;bean&gt; 定义 --&gt;
*
* &lt;/beans&gt;
* </pre>
*
* <h2>{@code LoadTimeWeaverAware} 接口</h2>
* 任何实现 {@link org.springframework.context.weaving.LoadTimeWeaverAware LoadTimeWeaverAware} 接口的 bean
* 都将自动接收到 {@code LoadTimeWeaver} 引用例如Spring 的 JPA 启动支持。
*
* <h2>定制 {@code LoadTimeWeaver}</h2>
* 默认的 weaver 将自动确定参见 {@link DefaultContextLoadTimeWeaver}。
*
* <p>要定制使用的 weaver{@code @Configuration} 类可以实现 {@link LoadTimeWeavingConfigurer} 接口,并通过
* {@code #getLoadTimeWeaver} 方法返回一个自定义的 {@code LoadTimeWeaver} 实例
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableLoadTimeWeaving
* public class AppConfig implements LoadTimeWeavingConfigurer {
*
* &#064;Override
* public LoadTimeWeaver getLoadTimeWeaver() {
* MyLoadTimeWeaver ltw = new MyLoadTimeWeaver();
* ltw.addClassTransformer(myClassFileTransformer);
* // ...
* return ltw;
* }
* }</pre>
*
* 上面的示例可与以下 Spring XML 配置进行比较
*
* <pre class="code">
* &lt;beans&gt;
*
* &lt;context:load-time-weaver weaverClass="com.acme.MyLoadTimeWeaver"/&gt;
*
* &lt;/beans&gt;
* </pre>
*
* 代码示例与 XML 示例的区别在于它实际上实例化了 {@code MyLoadTimeWeaver} 类型,这意味着它还可以配置实例,
* 例如调用 {@code #addClassTransformer} 方法。这展示了基于代码的配置方法通过直接编程访问更加灵活。
*
* <h2>启用基于 AspectJ 的编织</h2>
* 可通过 {@link #aspectjWeaving()} 属性启用 AspectJ 加载时编织,这将导致通过 {@link LoadTimeWeaver#addTransformer}
* 注册 {@linkplain org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter AspectJ 类转换器}。如果类路径中存在
* "META-INF/aop.xml" 资源,则默认情况下将激活 AspectJ 编织。示例
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableLoadTimeWeaving(aspectjWeaving=ENABLED)
* public class AppConfig {
* }</pre>
*
* 上面的示例可与以下 Spring XML 配置进行比较
*
* <pre class="code">
* &lt;beans&gt;
*
* &lt;context:load-time-weaver aspectj-weaving="on"/&gt;
*
* &lt;/beans&gt;
* </pre>
*
* 这两个示例是等价的,但有一个重要的例外在 XML 的情况下,当 {@code aspectj-weaving} 是 "on" 时,
* {@code <context:spring-configured>} 的功能将自动启用。在使用 {@code @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)}
* 时,这种情况不会发生。相反,您必须显式添加 {@code @EnableSpringConfigured}(包含在 {@code spring-aspects} 模块中)。
*
* @author Chris Beams
* @since 3.1
* @see LoadTimeWeaver
* @see DefaultContextLoadTimeWeaver
* @see org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LoadTimeWeavingConfiguration.class)
public @interface EnableLoadTimeWeaving {
/**
* 是否启用 AspectJ 编织。
*/
AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;
/**
* AspectJ 编织启用选项。
*/
enum AspectJWeaving {
/**
* 启用基于 Spring 的 AspectJ 加载时编织。
*/
ENABLED,
/**
* 关闭基于 Spring 的 AspectJ 加载时编织(即使类路径上存在 "META-INF/aop.xml" 资源)。
*/
DISABLED,
/**
* 如果类路径上存在 "META-INF/aop.xml" 资源,则启用 AspectJ 加载时编织。
* 如果没有此类资源,则关闭 AspectJ 加载时编织。
*/
AUTODETECT;
}
}
```
### 五、最佳实践
使用加载时编织Load Time Weaving功能。首先它创建了一个基于注解的 Spring 应用程序上下文,并通过 `AppConfig` 类配置了应用程序的相关组件。然后,它从应用程序上下文中获取了一个 `FooService` 的 bean 实例,并调用了其 `foo` 方法。接着,它创建了一个 `FooService` 的普通实例,并再次调用了其 `foo` 方法。
```java
public class EnableLoadTimeWeavingDemo {
public static void main(String[] args) {
// 创建一个基于注解的应用程序上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从上下文中获取 FooService 的 bean 实例
FooService fooService = context.getBean(FooService.class);
// 调用 FooService 的 foo 方法
fooService.foo();
// 换行
System.out.println();
// 创建一个 FooService 的实例
FooService fooService1 = new FooService();
// 调用 FooService 实例的 foo 方法
fooService1.foo();
}
}
```
通过 `@Configuration` 注解表明这是一个配置类,而 `@EnableLoadTimeWeaving` 注解则启用了加载时编织功能。在配置类中,通过 `@Bean` 注解定义了一个名为 `fooService` 的 bean返回一个 `FooService` 的实例。
```java
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
@Bean
public FooService fooService(){
return new FooService();
}
}
```
定义了一个切面类 `MyLTWAspect`用于实现加载时编织Load Time Weaving功能。通过 `@Aspect` 注解标记该类为一个切面,并在其中定义了一个环绕通知方法 `around`,用于在目标方法调用前后执行特定逻辑。在该方法中,首先输出了目标方法的名称,然后调用了原始方法,并输出了方法返回值。同时,通过 `@Pointcut` 注解定义了一个切点 `ltwPointcut()`,指定了需要被切入的目标方法,这里是 `com.xcs.spring.FooService` 类中的所有公共方法。
```java
@Aspect
public class MyLTWAspect {
@Around("ltwPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 在方法调用之前执行的逻辑
System.out.println("Method " + pjp.getSignature().getName() + " is called.");
// 调用原始方法
Object result = pjp.proceed();
// 在方法调用之后执行的逻辑
System.out.println("Method " + pjp.getSignature().getName() + " returns " + result);
return result;
}
@Pointcut("execution(public * com.xcs.spring.FooService.*(..))")
public void ltwPointcut(){}
}
```
定义了加载时编织Load Time Weaving的规则和切面配置。在 `<weaver>` 元素中指定了仅对应用程序特定包中的类进行编织,这里是 `com.xcs.spring` 包及其子包下的所有类。然后,在 `<aspects>` 元素中指定了要编织的切面,即 `com.xcs.spring.MyLTWAspect`
```java
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.xcs.spring..*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="com.xcs.spring.MyLTWAspect"/>
</aspects>
</aspectj>
```
`FooService` 类定义了一个简单的方法 `foo()`
```java
public class FooService {
public String foo() {
System.out.println("foo");
return "this is a foo method";
}
}
```
运行结果,加载时编织成功地拦截了通过 Spring 容器获取的 `FooService` bean以及直接使用 `new` 操作符创建的 `FooService` 对象。在每次调用 `foo()` 方法时,都会先打印方法被调用的消息,然后执行原始的方法逻辑(打印 "foo"),最后打印方法返回值,并返回给调用方。这证明切面 `MyLTWAspect` 中定义的逻辑在目标方法调用前后得到了执行,不论是从 Spring 容器中获取的 bean 还是直接创建的对象,都受到了拦截。
```java
Method foo is called.
foo
Method foo returns this is a foo method
Method foo is called.
foo
Method foo returns this is a foo method
```
### 六、源码分析
`LoadTimeWeavingConfiguration` 类,负责注册一个 `LoadTimeWeaver` bean用于启用加载时编织Load-Time Weaving功能。在应用中使用 `@EnableLoadTimeWeaving` 注解时,这个配置类会被自动导入。它通过检查 `EnableLoadTimeWeaving` 注解的属性来决定是否启用 AspectJ 编织功能,并根据配置创建相应的 `LoadTimeWeaver` 实例。如果用户提供了自定义的 `LoadTimeWeavingConfigurer` 实例,则会使用用户提供的实例;否则,会创建一个默认的 `DefaultContextLoadTimeWeaver` 实例作为 `LoadTimeWeaver`。根据 `EnableLoadTimeWeaving` 注解中的配置,决定是否启用 AspectJ 编织功能,并根据情况调用 `AspectJWeavingEnabler` 中的方法来实现编织。
```java
/**
* {@code @Configuration} 类,注册一个 {@link LoadTimeWeaver} bean。
*
* <p>当使用 {@link EnableLoadTimeWeaving} 注解时,这个配置类会自动导入。
* 完整的使用详情请参阅 {@code @EnableLoadTimeWeaving} 的 javadoc。
*
* <p>作者Chris Beams
*
* @since 3.1
* @see LoadTimeWeavingConfigurer
* @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
*/
@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) {
// 获取 @EnableLoadTimeWeaving 注解的属性
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) {
// 设置用户自定义的 LoadTimeWeavingConfigurer 实例
this.ltwConfigurer = ltwConfigurer;
}
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader) {
// 设置类加载器
this.beanClassLoader = beanClassLoader;
}
/**
* 注册 LoadTimeWeaver bean。
*/
@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) {
// 用户提供了自定义的 LoadTimeWeaver 实例
loadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver();
}
if (loadTimeWeaver == null) {
// 没有提供自定义的 LoadTimeWeaver -> 使用默认的
loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);
}
if (this.enableLTW != null) {
// 获取启用 AspectJ 编织的配置
AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");
switch (aspectJWeaving) {
case DISABLED:
// AspectJ 编织被禁用 -> 什么也不做
break;
case AUTODETECT:
if (this.beanClassLoader.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) == null) {
// 类路径上没有 aop.xml -> 视为 'disabled'
break;
}
// 类路径上有 aop.xml -> 启用编织
AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
break;
case ENABLED:
// 启用 AspectJ 编织
AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
break;
}
}
return loadTimeWeaver;
}
}
```
`AspectJWeavingEnabler` 的后处理器,它实现了多个接口,包括 `BeanFactoryPostProcessor`、`BeanClassLoaderAware`、`LoadTimeWeaverAware` 和 `Ordered`。它的主要作用是在 Spring 应用程序上下文中注册 AspectJ 的 `ClassPreProcessorAgentAdapter`,并与默认的 `LoadTimeWeaver` 进行关联。其中,`enableAspectJWeaving` 方法用于启用 AspectJ 编织功能,而 `AspectJClassBypassingClassFileTransformer` 类则实现了一个用于绕过 AspectJ 类处理的装饰器,以避免潜在的链接错误。
```java
/**
* 后处理器,将 AspectJ 的 {@link org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter}
* 注册到 Spring 应用程序上下文的默认 {@link org.springframework.instrument.classloading.LoadTimeWeaver} 中。
* 用于启用 AspectJ 的编织功能。
*
* <p>作者Juergen HoellerRamnivas Laddad
*
* @since 2.5
*/
public class AspectJWeavingEnabler
implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {
/**
* {@code aop.xml} 资源位置。
*/
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 {
// 启用 AspectJ 编织
enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
}
/**
* 使用给定的 {@link LoadTimeWeaver} 启用 AspectJ 编织。
*
* @param weaverToUse 要应用的 LoadTimeWeaver或 {@code null} 表示使用默认的 weaver
* @param beanClassLoader 如果需要,为其创建默认 weaver 的类加载器
*/
public static void enableAspectJWeaving(
@Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {
if (weaverToUse == null) {
if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
// 如果可以使用 Instrumentation创建 InstrumentationLoadTimeWeaver
weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
}
else {
throw new IllegalStateException("No LoadTimeWeaver available");
}
}
// 添加一个 ClassFileTransformer用于绕过 AspectJ 类的处理,以避免潜在的 LinkageErrors
weaverToUse.addTransformer(
new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
}
/**
* ClassFileTransformer 的装饰器,用于禁止处理 AspectJ 类,以避免潜在的 LinkageErrors。
*
* @see org.springframework.context.annotation.LoadTimeWeavingConfiguration
*/
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")) {
// 如果是 AspectJ 类,则直接返回原始字节码
return classfileBuffer;
}
// 否则,调用委托的 ClassFileTransformer 处理字节码
return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
}
}
}
```
### 七、常见问题
1. **AspectJ Weaver 未配置或不可用**
+ 如果未正确配置 AspectJ Weaver或者 Weaver 不可用,可能导致加载时编织无法正常工作。这通常需要检查项目的依赖配置和 AspectJ Weaver 的集成情况。
2. **ClassLoader 冲突**
+ 在复杂的应用程序中,可能会存在 ClassLoader 冲突,特别是当使用多个模块或库时。这可能会影响加载时编织的行为,导致意外的结果。
3. **AOP 切面匹配问题**
+ 当使用 AspectJ 编写切面时,可能会遇到切面匹配不准确或不完全的问题。这可能需要检查切面表达式以确保其正确匹配目标方法或类。
4. **调试困难**
+ 在某些情况下,加载时编织可能会导致调试困难,特别是当与其他 AOP 技术或动态代理结合使用时。在调试时,可能需要额外的注意和技巧来定位和解决问题。
5. **版本兼容性问题**
+ 加载时编织的实现可能与 Spring Framework 或 AspectJ 的特定版本不兼容,这可能会导致运行时错误或不一致的行为。在使用时需要注意版本兼容性,并确保选择适当的版本。

View File

@ -0,0 +1,28 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xcs.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<packaging>jar</packaging>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop-enableLoadTimeWeaving</artifactId>
<properties>
<aspectj.version>1.9.7</aspectj.version>
<spring-boot.version>2.3.5.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,15 @@
package com.xcs.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
@Bean
public FooService fooService(){
return new FooService();
}
}

View File

@ -0,0 +1,16 @@
package com.xcs.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class EnableLoadTimeWeavingDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
FooService fooService = context.getBean(FooService.class);
fooService.foo();
// 换行
System.out.println();
FooService fooService1 = new FooService();
fooService1.foo();
}
}

View File

@ -0,0 +1,10 @@
package com.xcs.spring;
public class FooService {
public String foo() {
System.out.println("foo");
return "this is a foo method";
}
}

View File

@ -0,0 +1,24 @@
package com.xcs.spring;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyLTWAspect {
@Around("ltwPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 在方法调用之前执行的逻辑
System.out.println("Method " + pjp.getSignature().getName() + " is called.");
// 调用原始方法
Object result = pjp.proceed();
// 在方法调用之后执行的逻辑
System.out.println("Method " + pjp.getSignature().getName() + " returns " + result);
return result;
}
@Pointcut("execution(public * com.xcs.spring.FooService.*(..))")
public void ltwPointcut(){}
}

View File

@ -0,0 +1,14 @@
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.xcs.spring..*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="com.xcs.spring.MyLTWAspect"/>
</aspects>
</aspectj>