Spring Boot结合AspectJ:能否拦截java.util.ArrayList.size()方法调用?
关于Spring Boot AOP拦截第三方库方法的疑问与解决
兄弟,我来给你把这个问题掰明白:第三方库的方法完全可以被拦截,但你用Spring AOP默认的动态代理机制根本做不到,这就是你写的切面没生效的核心原因。
为啥你的切面没起作用?
Spring AOP默认依赖JDK动态代理或者CGLIB代理,但这俩有个致命前提:被代理的对象必须是Spring容器管理的Bean。你想想,java.util.ArrayList这种JDK自带的类,你平时都是直接new ArrayList<>()创建实例吧?这种对象根本不在Spring的管控范围内,Spring AOP的切面自然碰不到它。
那该咋整?用AspectJ——这是真正的字节码增强框架,能直接修改类的字节码,不管对象是不是Spring Bean,只要加载到JVM里就能拦截。
下面给你一步步说具体实现:
1. 先把依赖加全
在你的Maven pom.xml里添上这俩依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
2. 开启AspectJ的织入机制
AspectJ有两种织入方式,选一个适合你的:
方式一:编译时织入(CTW)
这种方式会在编译阶段就把切面逻辑织入到目标类的字节码里,需要给Maven加个编译插件:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <executions> <execution> <goals> <goal>compile</goal> <goal>test-compile</goal> </goals> </execution> </executions> <configuration> <complianceLevel>17</complianceLevel> <!-- 改成你用的JDK版本 --> <source>17</source> <target>17</target> <aspectLibraries> <aspectLibrary> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </aspectLibrary> </aspectLibraries> </configuration> </plugin> </plugins> </build>
方式二:加载时织入(LTW)
这种更灵活,不用改编译配置,只要在运行时把切面织入到类中:
- 先在
application.properties里开启配置:
spring.aop.proxy-target-class=true spring.aspectj.weaver.auto=true
- 然后在项目的
src/main/resources/META-INF下新建aop.xml文件,配置要织入的目标类和切面:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd"> <aspectj> <weaver> <!-- 指定要拦截的包,这里包含java.util --> <include within="java.util..*"/> <!-- 你的切面所在的包 --> <include within="com.yourproject..*"/> </weaver> <aspects> <!-- 把你的切面类全路径写上 --> <aspect name="com.yourproject.aspect.ListSizeInterceptor"/> </aspects> </aspectj>
3. 修正你的切面代码
你的原切面有俩坑:
- 返回值处理错了:
@Around方法如果目标方法有返回值,必须返回proceed()的结果,不然调用方拿不到size()的返回值 - 切面类没加Spring的注解,要让Spring扫描到它才能生效
修正后的代码如下:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class ListSizeInterceptor { @Around("execution(int java.util.ArrayList.size())") public Object interceptListSize(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("[Before] About to call ArrayList.size()"); // 执行原方法并保存返回值 Object result = joinPoint.proceed(); System.out.println("[After] Called ArrayList.size(), result: " + result); // 一定要返回结果,不然调用方会拿到null return result; } }
最后提几个注意点
- 拦截JDK核心类的方法要慎重!比如
ArrayList.size()被调用得非常频繁,切面逻辑一定要尽量简单,别影响性能 - 如果用LTW,某些场景下可能需要给JVM加启动参数:
-javaagent:path/to/aspectjweaver.jar,不过Spring Boot大部分场景已经帮你处理好了 - 如果你只是想拦截Spring Bean里用到的ArrayList,也可以把ArrayList包装成Spring Bean(比如用
@Bean方法创建),然后用Spring AOP拦截,但这种方式只能拦截Bean实例的调用,管不了所有ArrayList对象
内容的提问来源于stack exchange,提问作者prometheusnoob
相关产品推荐
相关产品推荐

