java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring AOP 与 AspectJ

Spring AOP 与 AspectJ的使用小结

作者:Full Stack Developme

SpringAOP与AspectJ是两种不同的AOP实现框架,本文就来详细的介绍一下Spring AOP 与 AspectJ的使用,感兴趣的可以了解一下

这两者经常被混淆,很多人误以为 Spring AOP 就是 AspectJ 的实现。实际上它们是两个不同的 AOP 框架,有着本质区别。

一、核心区别总览

对比维度Spring AOPAspectJ
本质基于代理的 AOP 实现基于字节码织入的 AOP 实现
实现机制JDK 动态代理 / CGLIB编译期/类加载期字节码修改
织入时机运行时编译期、类加载期、运行期
连接点粒度仅方法级别方法、字段、构造器、静态初始化等
性能有代理调用开销直接执行增强代码,性能更好
是否需要 Spring 容器✅ 必须❌ 不必须
增强 private 方法❌ 不支持✅ 支持
增强 final 类/方法❌ 不支持(CGLIB 限制)✅ 支持
IDE 支持通用Eclipse AJDT、IntelliJ 插件
学习曲线简单较陡峭

二、实现原理深度对比

Spring AOP 原理

// Spring AOP 实际是代理模式
public class UserServiceProxy extends UserService {
    private UserService target;
    
    @Override
    public void saveUser(User user) {
        // 前置通知
        System.out.println("Before save");
        // 调用目标方法
        target.saveUser(user);
        // 后置通知
        System.out.println("After save");
    }
}

AspectJ 原理

// 原始代码
public class UserService {
    public void saveUser(User user) {
        System.out.println("Saving user");
    }
}

// AspectJ 编译后实际生成的字节码等价于
public class UserService {
    public void saveUser(User user) {
        // 通知代码直接织入
        System.out.println("Before save");
        System.out.println("Saving user");
        System.out.println("After save");
    }
}

三、功能范围对比

连接点支持度

连接点类型Spring AOPAspectJ示例场景
方法调用最常用
方法执行区别在于代理与目标
构造器调用对象创建监控
字段读取/设置属性变更校验
静态初始化类加载时操作
异常处理异常处理器增强
对象初始化构造后初始化逻辑
注解类型匹配基于注解的增强

AspectJ 独有能力示例

@Aspect
public class AspectJUniqueCapabilities {
    
    // 1. 拦截字段赋值
    @Before("set(* com.example.User.name)")
    public void beforeSetName(JoinPoint jp) {
        String newName = (String) jp.getArgs()[0];
        if (newName == null || newName.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
    }
    
    // 2. 拦截字段读取
    @Before("get(* com.example.User.password)")
    public void beforeGetPassword() {
        System.out.println("正在读取密码字段");
    }
    
    // 3. 拦截构造器
    @Before("new(com.example.User)")
    public void beforeConstructor() {
        System.out.println("User 对象即将被创建");
    }
    
    // 4. 拦截静态初始化块
    @Before("staticinitialization(com.example.User)")
    public void beforeStaticInit() {
        System.out.println("User 类正在静态初始化");
    }
    
    // 5. 拦截异常处理器
    @Before("handler(Exception+)")
    public void beforeExceptionHandler() {
        System.out.println("即将处理异常");
    }
    
    // 6. 为类添加新方法(引入)
    @DeclareParents(value = "com.example.User+", 
                    defaultImpl = AuditableImpl.class)
    public static Auditable mixin;
}

四、性能对比测试

// 性能测试示例
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class AOPPerformanceTest {
    
    @Benchmark
    public void springAOP() {
        userService.saveUser(new User());  // Spring AOP 代理
    }
    
    @Benchmark
    public void aspectJ() {
        userService.saveUser(new User());  // AspectJ 织入
    }
    
    // 典型结果(基于 1000 万次调用):
    // Spring AOP: ~450ns/op(有代理开销)
    // AspectJ: ~120ns/op(几乎无额外开销)
}

五、实战选择指南

使用 Spring AOP 的场景

// ✅ 适合 Spring AOP 的场景
@Service
public class OrderService {
    
    // 1. 声明式事务(经典场景)
    @Transactional
    public void createOrder(Order order) { }
    
    // 2. 方法级缓存
    @Cacheable(value = "users", key = "#id")
    public User getUser(Long id) { }
    
    // 3. 权限检查
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteOrder(Long id) { }
    
    // 4. 方法执行时间监控
    @Around("@annotation(com.example.Monitored)")
    public Object monitor(ProceedingJoinPoint pjp) { }
}

必须使用 AspectJ 的场景

// ✅ 必须用 AspectJ 的场景
@Aspect
public class MustUseAspectJ {
    
    // 1. 拦截 private 方法
    private void internalLogic() { }  // Spring AOP 无法增强
    
    // 2. 拦截 static 方法
    public static void staticMethod() { }
    
    // 3. 拦截 final 方法
    public final void finalMethod() { }
    
    // 4. 拦截字段访问(领域驱动设计中的属性校验)
    private String email;
    
    // 5. 不需要 Spring 容器的独立应用
    // 6. 对性能要求极高的场景(如每秒数万次调用)
    // 7. 需要拦截构造器(如单例模式强制校验)
}

六、配置对比

Spring AOP 配置

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringAOPConfig {
    
    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

// application.properties
spring.aop.auto=true
spring.aop.proxy-target-class=true

AspectJ LTW 配置

// JVM 启动参数
// -javaagent:path/to/aspectjweaver-1.9.19.jar
// META-INF/aop.xml
<aspectj>
    <weaver options="-verbose">
        <include within="com.example..*"/>
    </weaver>
    <aspects>
        <aspect name="com.example.aspect.PerformanceAspect"/>
    </aspects>
</aspectj>
// Spring Boot 启用 LTW
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class LTWConfig {
}

七、常见陷阱与解决方案

Spring AOP 陷阱

@Service
public class UserService {
    
    // 陷阱1:内部方法调用不生效
    @Transactional
    public void outer() {
        this.inner();  // ❌ 事务不生效(直接调用,未通过代理)
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void inner() { }
    
    // 解决方案
    public void fixed() {
        ((UserService) AopContext.currentProxy()).inner();  // ✅ 通过代理调用
        // 或注入自身:@Autowired private UserService self;
    }
    
    // 陷阱2:public 方法限制
    @Transactional
    private void privateMethod() { }  // ❌ 事务不生效
    
    // 陷阱3:final 方法
    @Transactional
    public final void finalMethod() { }  // ❌ CGLIB 无法代理 final 方法
}

AspectJ 陷阱

// 陷阱:IDE 编译不支持 AspectJ 语法
// 解决:使用 ajc 编译器或配置 Maven/Gradle 插件
// Maven 配置
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.14.0</version>
    <configuration>
        <complianceLevel>11</complianceLevel>
        <source>11</source>
        <target>11</target>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

八、混合使用策略

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)  // Spring AOP
@EnableLoadTimeWeaving(aspectjWeaving = ENABLED)   // AspectJ LTW
public class HybridConfig {
    
    // 简单场景用 Spring AOP
    @Bean
    public LogAspect logAspect() { return new LogAspect(); }
    
    // 复杂场景(字段拦截)用 AspectJ
    // 通过 META-INF/aop.xml 配置
}

九、选择决策树

是否需要拦截 private/static/final 方法或字段?
├─ 是 → 使用 AspectJ
└─ 否 → 继续

是否需要拦截构造器或静态初始化块?
├─ 是 → 使用 AspectJ
└─ 否 → 继续

是否需要高频率调用(>10万次/秒)?
├─ 是 → 使用 AspectJ
└─ 否 → 继续

是否完全基于 Spring 框架?
├─ 是 → Spring AOP(更简单、易调试)
└─ 否 → AspectJ(独立使用)

是否关注编译/启动速度?
├─ 是 → Spring AOP(无额外织入开销)
└─ 否 → AspectJ(首次启动稍慢,但运行更快)

十、总结

方面Spring AOPAspectJ
推荐度(Spring 项目)⭐⭐⭐⭐⭐⭐⭐⭐
推荐度(非 Spring 项目)⭐⭐⭐⭐⭐
学习成本中高
调试难度低(可见代理类)高(字节码层面)
社区支持极好良好

最佳实践

到此这篇关于Spring AOP 与 AspectJ的使用小结的文章就介绍到这了,更多相关Spring AOP 与 AspectJ内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文