" />
文章

必学!Java切面两种实现方式和实际使用场景

Spring AOPAspectJ 都是 Java 中常用的面向切面编程(AOP, Aspect-Oriented Programming)框架,它们有相似的概念,但在实现方式、功能特性、以及使用场景上有一些不同。

1. 概念与实现方式的区别

Spring AOP:

  • 实现方式:Spring AOP 是基于 代理模式 实现的,主要通过 JDK 动态代理CGLIB 动态代理 来实现。
    • 对于 实现了接口的类,Spring AOP 使用 JDK 动态代理。
    • 对于 没有实现接口的类,Spring AOP 使用 CGLIB 动态代理,通过生成子类来拦截方法。
  • 功能特性:Spring AOP 主要支持 运行时的代理,即 运行时增强,只能在方法级别进行增强,不支持对构造器、静态方法或类的增强。
  • 适用场景:Spring AOP 通常用于较轻量的 AOP 需求,像事务管理、日志记录、权限控制等。

AspectJ:

  • 实现方式:AspectJ 是一个功能更强大的 AOP 框架,它支持 编译时增强(Compile-Time Weaving)类加载时增强(Load-Time Weaving) 以及 运行时增强(Runtime Weaving)。它直接操作字节码,可以增强任何类型的连接点(不仅仅是方法)。
    • 编译时增强:通过 AspectJ 编译器(ajc)在编译时将切面织入目标类。
    • 加载时增强:在类加载时通过代理类织入切面。
    • 运行时增强:与 Spring AOP 类似,但功能更强大。
  • 功能特性:AspectJ 可以增强 构造器静态方法字段,可以定义 切入点表达式 来精确控制切入逻辑。
  • 适用场景:AspectJ 适合用于需要更多切面功能、性能优化、以及对类的更深层次控制的场景。

2. Spring AOP 和 AspectJ 的使用方式

Spring AOP 使用方式

Spring AOP 是 Spring 框架自带的 AOP 功能,配置和使用非常简单。以下是一个 Spring AOP 的简单示例:

1. 添加 AOP 依赖:

pom.xml 中添加 Spring AOP 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义业务类:
import org.springframework.stereotype.Service;

@Service
public class MyService {
    public void performTask() {
        System.out.println("Performing task in MyService");
    }
}
3. 定义切面类:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.MyService.performTask(..))")
    public void logBeforeTask() {
        System.out.println("Logging before performing task");
    }
}
4. 启动 Spring Boot 应用:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AopApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopApplication.class, args);
    }
}

AspectJ 使用方式

AspectJ 可以通过两种方式使用:一种是与 Spring AOP 集成,另一种是使用独立的 AspectJ 编译器(ajc)或加载时编织器(LTW)。

1. 使用 AspectJ 结合 Spring AOP

Spring AOP 可以通过 @EnableAspectJAutoProxy 注解启用 AspectJ 注解风格的切面,这种方式是通过 Spring AOP 实现的,但是使用了 AspectJ 的注解风格语法。

1.1 添加 AOP 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.2 启用 AspectJ 支持:

在主配置类或启动类中启用 AspectJ 自动代理:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class AspectJApplication {
    public static void main(String[] args) {
        SpringApplication.run(AspectJApplication.class, args);
    }
}
1.3 定义切面类(使用 AspectJ 注解):
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectJLoggingAspect {

    @Before("execution(* com.example.MyService.performTask(..))")
    public void logBefore() {
        System.out.println("AspectJ: Logging before performing task");
    }
}
2. 使用独立的 AspectJ(编译时增强或加载时增强)

如果你需要更多的控制,使用独立的 AspectJ 是一个选择,具体可以通过 ajc 编译器或者类加载时织入(LTW)来增强类。

2.1 添加 AspectJ 依赖:
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.7</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>
2.2 使用 ajc 编译器编译:

使用 AspectJ 编译器 (ajc) 将你的代码编译成带有切面的类。你可以在构建工具(如 Maven 或 Gradle)中集成 AspectJ 编译器。

编译时增强的示例

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspect {
    @Before("execution(* com.example.MyService.performTask(..))")
    public void logBeforeTask() {
        System.out.println("AspectJ: Logging before performing task");
    }
}

然后使用 ajc 编译这个类,织入切面。

3. Spring AOP 与 AspectJ 的对比总结

特性Spring AOPAspectJ
代理机制基于 JDK 动态代理或 CGLIB 动态代理基于字节码操作,支持编译时、加载时、运行时织入
增强的范围方法级别方法、构造函数、字段、静态方法等
性能适合小型增强,开销较低更高效,适合复杂、大规模增强
织入时机运行时编译时、类加载时、运行时
适用场景轻量级 AOP 需求,如事务管理、日志记录等高性能、复杂 AOP 需求,如安全、事务、性能优化
配置复杂性简单,通过注解配置即可较复杂,需要 AspectJ 编译器或加载时增强
支持的增强内容方法调用前后方法、构造函数、字段、静态方法、类加载时增强

4. 选择使用的建议

  • Spring AOP:适用于 轻量级 AOP 场景,比如对方法的前后增强,常用于 事务管理日志记录权限控制 等功能。它与 Spring 框架集成简单,基于代理模式,可以在运行时使用代理进行增强。

  • AspectJ:如果你需要 更强大的功能,如 静态方法、字段、构造函数 的增强,或需要在 编译时或类加载时织入切面,AspectJ 是更好的选择。AspectJ 性能更高,功能更强大,但配置稍微复杂,适合大型应用或需要高性能的场景。

总结:

  • Spring AOP 是 Spring 框架内置的 AOP 实现,基于代理模式,主要用于方法增强。
  • AspectJ 是功能更强大的 AOP 框架,可以增强方法、构造函数、静态方法、字段等,支持编译时和类加载时织入,适合复杂和高性能场景。
License:  CC BY 4.0