
AspectJ 详解AspectJ 是 Java 生态中最完整、最成熟的 AOP 框架。Spring AOP 虽然也支持Aspect注解和 AspectJ 切点表达式但底层实现是动态代理只支持方法执行连接点。AspectJ 则通过字节码织入支持字段访问、构造器调用、静态初始化等更细粒度的连接点功能远超 Spring AOP。一、AspectJ 是什么AspectJ 是一个基于 Java 的 AOP 框架它扩展了 Java 语言提供了一套完整的 AOP 语法和织入工具。它可以在编译期、编译后或类加载期将切面织入到目标类中不限于 Spring 容器管理的 Bean。AspectJ 的核心能力支持方法执行、方法调用、字段读写、构造器调用、异常处理等多种连接点。支持编译期织入CTW、编译后织入BTW、加载期织入LTW。切点表达式比 Spring AOP 更丰富。可以作用于任何 Java 类不依赖 Spring 容器。二、AspectJ 与 Spring AOP 的区别对比维度Spring AOPAspectJ实现方式动态代理JDK / CGLIB字节码织入编译期 / 加载期连接点类型仅方法执行方法执行、方法调用、字段读写、构造器调用、异常处理等织入时机运行时编译期、编译后、类加载期作用范围仅 Spring Bean任意 Java 对象性能反射调用有开销编译期织入运行时无额外开销配置复杂度低中到高依赖Spring 容器AspectJ 编译器 / LTW切点表达式AspectJ 的子集完整支持Spring AOP 使用了 AspectJ 的注解和切点表达式语法但底层是代理。如果只需要方法级别的增强Spring AOP 足够。如果需要字段访问、构造器调用等更细粒度的增强必须用 AspectJ。三、AspectJ 的核心概念AspectJ 的概念与通用 AOP 一致但连接点类型更丰富概念说明切面Aspect横切关注点的模块化用Aspect标注连接点Join Point程序执行中的可拦截点如方法执行、字段访问、构造器调用切点Pointcut匹配连接点的表达式通知Advice在切点上执行的动作有 Before、After、Around 等织入Weaving把切面应用到目标类的过程目标对象Target被增强的对象代理Proxy织入后生成的对象四、AspectJ 的织入方式AspectJ 支持三种织入时机1. 编译期织入Compile-Time Weaving, CTW在编译 Java 源文件时AspectJ 编译器ajc同时编译切面和目标类把切面逻辑织入到目标类的字节码中。特点运行时无额外开销。需要替换 javac 为 ajc。适合新项目从编译阶段就支持 AOP。Maven 配置示例plugingroupIdorg.codehaus.mojo/groupIdartifactIdaspectj-maven-plugin/artifactIdversion1.14.0/versionconfigurationcomplianceLevel17/complianceLevelsource17/sourcetarget17/targetshowWeaveInfotrue/showWeaveInfo/configurationexecutionsexecutiongoalsgoalcompile/goalgoaltest-compile/goal/goals/execution/executionsdependenciesdependencygroupIdorg.aspectj/groupIdartifactIdaspectjtools/artifactIdversion1.9.21/version/dependency/dependencies/plugin2. 编译后织入Post-Compile Weaving, BTW目标类已经编译成.class文件后用 AspectJ 工具对字节码进行织入。特点不需要修改源码构建流程。适合第三方 jar 包无法重新编译的场景。需要额外配置织入任务。3. 加载期织入Load-Time Weaving, LTW在类加载到 JVM 时通过 Java Agent 动态修改字节码织入切面。特点不需要编译期或编译后处理。需要配置aop.xml和 JVM 参数-javaagent。适合 Spring 项目Spring 提供了LoadTimeWeaver抽象。配置示例META-INF/aop.xmlaspectjaspectsaspectnamecom.example.aspect.LogAspect//aspectsweaveroptions-verboseincludewithincom.example.service..*//weaver/aspectj启动参数java-javaagent:aspectjweaver.jar-jarmyapp.jar五、AspectJ 切点表达式AspectJ 的切点表达式比 Spring AOP 更丰富。Spring AOP 只支持execution、annotation、within等一部分AspectJ 还支持call、get、set、initialization、handler等。指示器说明Spring AOP 支持execution方法执行✅call方法调用❌get字段读取❌set字段写入❌initialization构造器初始化❌preinitialization构造器执行前❌staticinitialization静态初始化❌handler异常处理❌within指定类型内✅within标注指定注解的类型✅annotation标注指定注解的方法✅args参数标注指定注解✅this代理对象类型✅target目标对象类型✅args参数类型✅beanBean 名称✅Spring 特有常用示例// 方法执行execution(*com.example.service.*.*(..))// 方法调用调用方视角call(*com.example.service.*.*(..))// 字段读取get(Stringcom.example.entity.User.name)// 字段写入set(Stringcom.example.entity.User.name)// 构造器调用call(com.example.entity.User.new(..))// 构造器执行execution(com.example.entity.User.new(..))// 异常处理handler(Exception)// 静态初始化staticinitialization(com.example.service.*)// 组合execution(*com.example.service.*.*(..))annotation(com.example.annotation.Log)六、AspectJ 通知类型AspectJ 的通知类型与 Spring AOP 一致但语法略有不同。通知注解说明前置通知Before目标方法执行前后置通知After目标方法执行后无论是否异常返回通知AfterReturning目标方法正常返回后异常通知AfterThrowing目标方法抛出异常后环绕通知Around目标方法执行前后AspectpublicclassLogAspect{Before(execution(* com.example.service.*.*(..)))publicvoidbefore(JoinPointjp){System.out.println(执行前jp.getSignature().getName());}AfterReturning(pointcutexecution(* com.example.service.*.*(..)),returningresult)publicvoidafterReturning(JoinPointjp,Objectresult){System.out.println(返回result);}AfterThrowing(pointcutexecution(* com.example.service.*.*(..)),throwingex)publicvoidafterThrowing(JoinPointjp,Exceptionex){System.out.println(异常ex.getMessage());}Around(execution(* com.example.service.*.*(..)))publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{longstartSystem.currentTimeMillis();Objectresultpjp.proceed();System.out.println(耗时(System.currentTimeMillis()-start)ms);returnresult;}}七、完整示例编译期织入1. 目标类packagecom.example.service;publicclassUserService{publicvoidregister(Stringname){System.out.println(注册用户name);}}2. 切面packagecom.example.aspect;importorg.aspectj.lang.JoinPoint;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;AspectpublicclassLogAspect{Before(execution(* com.example.service.*.*(..)))publicvoidbefore(JoinPointjp){System.out.println(【AspectJ】调用方法jp.getSignature().getName());}}3. Maven 配置plugingroupIdorg.codehaus.mojo/groupIdartifactIdaspectj-maven-plugin/artifactIdversion1.14.0/versionconfigurationcomplianceLevel17/complianceLevelsource17/sourcetarget17/target/configurationexecutionsexecutiongoalsgoalcompile/goal/goals/execution/executionsdependenciesdependencygroupIdorg.aspectj/groupIdartifactIdaspectjtools/artifactIdversion1.9.21/version/dependency/dependencies/plugin4. 运行编译后UserService的字节码中已经织入了切面逻辑。运行register方法时控制台输出【AspectJ】调用方法register 注册用户张三不需要 Spring 容器不需要代理AspectJ 在编译期就把切面织入了。八、Spring 中集成 AspectJSpring 项目中使用 AspectJ 有两种方式1. 使用 Spring AOP 的 Aspect代理方式这是最常见的方式。Aspect注解来自 AspectJ但由 Spring AOP 解析底层用动态代理实现。AspectComponentpublicclassLogAspect{Around(execution(* com.example.service.*.*(..)))publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{// ...returnpjp.proceed();}}特点只支持方法执行连接点只对 Spring Bean 生效配置简单。2. 使用 AspectJ LTW加载期织入如果需要完整的 AspectJ 能力字段访问、构造器调用等可以在 Spring 中启用 LTW。配置类ConfigurationEnableLoadTimeWeaving(aspectjWeavingEnableLoadTimeWeaving.AspectjWeaving.ENABLED)publicclassAppConfig{}META-INF/aop.xmlaspectjaspectsaspectnamecom.example.aspect.LogAspect//aspectsweaverincludewithincom.example.service..*//weaver/aspectj启动参数java-javaagent:aspectjweaver.jar-jarmyapp.jar这种方式下切面可以作用于非 Spring 管理的对象支持更细粒度的连接点。九、AspectJ 的优缺点优点功能完整支持所有连接点类型。编译期织入运行时无反射开销性能好。不依赖 Spring 容器可作用于任意 Java 对象。切点表达式丰富控制粒度细。缺点配置复杂需要 AspectJ 编译器或 LTW 支持。编译期织入需要替换 javac 为 ajc影响构建流程。LTW 需要 JVM 参数部署时容易遗漏。调试难度大字节码被修改后栈跟踪不直观。学习曲线比 Spring AOP 陡峭。十、适用场景场景推荐方法级别的日志、事务、缓存Spring AOP需要字段访问、构造器调用AspectJ需要增强第三方 jar 包中的类AspectJ BTW / LTW需要增强非 Spring 管理的对象AspectJ对性能有极致要求AspectJ CTW快速开发配置简单Spring AOP十一、总结维度AspectJSpring AOP定位完整 AOP 框架基于代理的轻量 AOP连接点方法、字段、构造器、异常等仅方法执行织入编译期、编译后、加载期运行时作用范围任意 Java 对象Spring Bean性能编译期织入无开销反射调用有开销配置复杂简单典型应用细粒度增强、第三方类增强方法级日志、事务、缓存AspectJ 是 Java AOP 的完整实现Spring AOP 是它的轻量替代。Spring AOP 借鉴了 AspectJ 的注解和切点表达式但底层用动态代理实现只支持方法执行连接点。如果需要更细粒度的控制或者要增强非 Spring 管理的对象就需要使用 AspectJ 的编译期或加载期织入。理解两者的区别能帮你在不同场景下做出正确的技术选择。