JDK动态代理对final类/方法增强无效,CGLIB因继承机制无法代理final类/方法。当业务场景中必须使用final类(如工具类、第三方依赖类)或final方法时,Spring AOP(动态代理)已无法满足需求,此时需使用 AspectJ编译期织入 实现AOP增强。
本文核心:用规范的开发示例,讲解AspectJ编译期织入的配置、编码、编译运行全流程,解决final类/方法的AOP增强痛点,贴合实际开发规范。
一、核心前提:AspectJ编译期织入原理
与Spring AOP(运行时动态代理)不同,AspectJ编译期织入是 在代码编译阶段,将切面逻辑(通知)直接织入目标类的字节码中,生成包含增强逻辑的class文件。
核心优势:不依赖动态代理,不受final类/方法的限制------无论目标类/方法是否为final,只要匹配切点规则,就能实现增强,且性能优于运行时代理(无反射开销)。
关键区别:Spring AOP是"运行时增强",AspectJ编译期织入是"编译时增强",直接修改目标类字节码,无需代理对象。
二、规范开发示例(SpringBoot + AspectJ 编译期织入)
以"final工具类的方法增强(日志记录)"为实战场景,完整演示从依赖配置、切面编写、编译配置到测试运行的全流程,符合企业开发规范。
2.1 场景定义
假设存在一个final工具类(业务要求必须为final,防止被继承篡改),需对其内部的final方法添加日志增强,记录方法调用参数、返回值和执行耗时。
java
/**
* 业务要求:必须为final类(工具类,禁止继承)
* 内部方法为final,禁止重写
*/
public final class FinalToolUtil {
/**
* final方法:业务核心工具方法,需添加日志增强
* @param param 入参
* @return 处理结果
*/
public final String process(String param) {
// 模拟业务逻辑:参数处理
try {
Thread.sleep(100); // 模拟处理耗时
} catch (InterruptedException e) {
throw new RuntimeException("处理失败", e);
}
return "处理结果:" + param.toUpperCase();
}
}
2.2 第一步:添加依赖(SpringBoot项目)
需添加AspectJ核心依赖和编译期织入插件,确保编译时能将切面逻辑织入目标类。
pom.xml 配置(规范依赖版本,贴合SpringBoot版本):
xml
<!-- SpringBoot 基础依赖(省略,根据自身版本引入) -->
<!-- 1. AspectJ 核心依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.19</version>
</dependency>
<!-- 2. AspectJ 编译期织入插件(关键) -->
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<!-- 指定Java版本,与项目一致 -->
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
<!-- 指定切面类所在包(扫描切面) -->
<aspectLibraries>
<aspectLibrary>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals><!-- 编译时织入 -->
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
说明:aspectj-maven-plugin 是编译期织入的核心,负责在maven编译阶段,将切面逻辑织入目标类字节码。
2.3 第二步:编写AspectJ切面(规范编写)
使用AspectJ注解编写切面,定义切点(匹配final类的final方法)和通知(日志增强逻辑),遵循AOP开发规范。
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* AspectJ 切面类(编译期织入)
* 用于增强 FinalToolUtil 的 final 方法
*/
@Aspect // 标识为AspectJ切面
@Component // 交给Spring管理(可选,若不依赖Spring,可省略)
public class FinalMethodAspect {
private static final Logger log = LoggerFactory.getLogger(FinalMethodAspect.class);
/**
* 切点:匹配 FinalToolUtil 类的所有final方法
* 切点表达式规范:execution(修饰符 返回值 全类名.方法名(参数))
*/
@Pointcut("execution(public final String com.example.demo.util.FinalToolUtil.process(..))")
public void finalMethodPointcut() {}
/**
* 环绕通知:记录方法调用日志、执行耗时
* 环绕通知可控制方法执行,适合记录耗时、异常处理
*/
@Around("finalMethodPointcut()")
public Object aroundFinalMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 前置增强:记录方法调用信息
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("【Final方法增强】开始调用方法:{},入参:{}", methodName, args[0]);
// 2. 记录开始时间,执行目标方法(final方法)
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标final方法
// 3. 后置增强:记录执行耗时和返回值
long costTime = System.currentTimeMillis() - startTime;
log.info("【Final方法增强】方法{}执行完毕,耗时:{}ms,返回值:{}", methodName, costTime, result);
// 4. 返回目标方法结果
return result;
}
}
关键说明:
-
切点表达式必须精准匹配final方法:`execution(public final String com.example.demo.util.FinalToolUtil.process(..))`,明确方法的修饰符(final)、返回值、全类名、方法名和参数。
-
@Around通知:AspectJ的环绕通知与Spring AOP用法一致,可完整控制目标方法的执行流程,适合日志、耗时统计等场景。
-
切面类可交给Spring管理(@Component),也可独立使用(不依赖Spring),本文演示SpringBoot集成场景。
2.4 第三步:编译验证(核心步骤)
AspectJ编译期织入的核心是"编译阶段织入",需通过maven编译,确保切面逻辑被织入FinalToolUtil的字节码中。
执行maven编译命令:
bash
mvn clean compile
编译成功后,可通过反编译工具(如JD-GUI)查看FinalToolUtil.class文件,会发现:切面的日志逻辑已被直接织入process()方法中,而非通过代理实现。
反编译核心片段(示意):
java
public final class FinalToolUtil {
public final String process(String param) {
// 织入的切面逻辑(前置日志)
Logger log = LoggerFactory.getLogger(FinalMethodAspect.class);
String methodName = "process";
Object[] args = new Object[]{param};
log.info("【Final方法增强】开始调用方法:{},入参:{}", methodName, args[0]);
long startTime = System.currentTimeMillis();
// 原业务逻辑
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException("处理失败", e);
}
String result = "处理结果:" + param.toUpperCase();
// 织入的切面逻辑(后置日志)
long costTime = System.currentTimeMillis() - startTime;
log.info("【Final方法增强】方法{}执行完毕,耗时:{}ms,返回值:{}", methodName, costTime, result);
return result;
}
}
可见:编译后,切面逻辑已与目标final方法的业务逻辑融合,无需代理,直接执行。
2.5 第四步:测试运行(实战验证)
编写测试类,调用FinalToolUtil的process()方法,验证AOP增强是否生效(日志是否打印)。
java
import com.example.demo.util.FinalToolUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class FinalToolUtilTest {
// 直接注入final类实例(无需代理,编译后已包含增强逻辑)
@Autowired
private FinalToolUtil finalToolUtil;
@Test
public void testProcess() {
// 调用final方法
String result = finalToolUtil.process("aspectj-test");
System.out.println("测试结果:" + result);
}
}
2.6 运行结果(符合预期)
text
【Final方法增强】开始调用方法:process,入参:aspectj-test
【Final方法增强】方法process执行完毕,耗时:102ms,返回值:处理结果:ASPECTJ-TEST
测试结果:处理结果:ASPECTJ-TEST
结论:final类的final方法成功被增强,日志正常打印,证明AspectJ编译期织入生效。
三、关键注意事项(规范开发必看)
-
依赖版本一致:aspectjrt、aspectj-maven-plugin的版本需匹配,避免编译报错(本文使用稳定版本组合,可直接复用)。
-
切点表达式精准:必须明确匹配final方法的修饰符(final),否则无法织入(AspectJ支持精准匹配修饰符)。
-
编译方式:必须使用maven编译(mvn compile),IDE直接编译可能无法触发AspectJ织入(需配置IDE的AspectJ插件,如IntelliJ IDEA的AspectJ Support)。
-
与Spring AOP区分:AspectJ编译期织入无需依赖Spring AOP,可独立使用;若集成SpringBoot,只需添加@Component将切面交给Spring管理即可。
-
第三方final类增强:若目标final类是第三方依赖(无法修改源码),只需在切面中精准配置切点表达式,编译时同样能织入增强逻辑(核心优势)。
四、总结(开发实战结论)
当遇到final类/方法需要AOP增强时,Spring AOP(JDK/CGLIB)无法解决,此时 AspectJ编译期织入 是最优方案:
-
优势:不受final限制,性能优于动态代理(无反射开销),支持第三方final类增强。
-
实战流程:添加依赖 → 编写切面 → maven编译 → 测试运行,符合企业开发规范。
-
适用场景:工具类、第三方依赖类、业务要求必须为final的类/方法的AOP增强(日志、权限、耗时统计等)。
本文示例可直接复制到项目中复用,只需修改包名、类名和切点表达式,即可快速实现final类/方法的AOP增强。