拼多多二面:Spring AOP 和 AspectJ 的区别是什么?

开发
本文我们分析了 Spring AOP 和 AspectJ 的实现原理,并且通过示例展示了两者如如何使用它们。

Spring AOP和 AspectJ 是工作中经常使用的两个的 AOP框架,那么,它们是如何工作的?两者之间有什么区别?我们该如何选择?这篇文章来聊一聊。

一、实现原理

1. Spring AOP 的实现原理

Spring AOP,全称 Aspect-Oriented Programming,中文翻译为面向切面编程,主要是基于代理模式来实现面向切面编程。其核心原理包括 3个步骤:

  • 动态代理:Spring AOP 使用 JDK 动态代理或 CGLIB(Code Generation Library)生成代理对象。当被代理的目标对象实现了接口时,Spring 默认使用 JDK 动态代理;否则,使用 CGLIB 生成子类代理。
  • 织入机制:在运行时,通过代理对象拦截方法调用,根据配置的切面(Aspect)和通知(Advice)执行相应的增强逻辑(如前置、后置、环绕等)。
  • 代理链:如果有多个切面,需要按照一定的顺序对目标对象进行多层代理。

2. AspectJ 的实现原理

AspectJ 是一个功能更强大的 AOP 框架,提供了更丰富的切面功能和更灵活的织入机制。其实现原理也包括 3个步骤:

织入时机:

  • 编译时织入(Compile-time Weaving):在源代码编译成字节码时,将切面逻辑织入目标类。
  • 类加载时织入(Load-time Weaving):在类被加载到 JVM 时,通过特定的类加载器将切面逻辑织入目标类。
  • 二进制织入(Binary Weaving):对已经编译好的字节码进行后期修改,加入切面逻辑。
  • 字节码操作:AspectJ 直接操作字节码,允许对更细粒度的连接点(如字段赋值、构造方法调用等)进行拦截和增强。
  • 丰富的切点表达式:支持更复杂和精确的切点定义,涵盖更多的连接点类型。

二、两者区别

在分析完 Spring AOP和 AspectJ 的工作原理之后,我们来看看两者的区别。关于 Spring AOP和 AspectJ的区别,可以总结成下表:

特性

Spring AOP

AspectJ

实现方式

基于动态代理(JDK代理或CGLIB)

基于字节码织入(编译时、类加载时、二进制)

切点范围

主要面向方法级别的连接点

支持方法、构造方法、字段、异常等多种连接点

织入时机

运行时通过代理实现

编译时、类加载时或二进制后期织入

性能

由于使用代理,性能开销相对较小,但功能有限

由于织入在编译或类加载时完成,运行时性能更优,功能更强大

功能丰富度

提供基本的AOP功能,如前置、后置、环绕通知

提供更丰富的AOP功能,包括更复杂的切点表达式和连接点类型

使用复杂度

易于集成和使用,特别是在Spring应用中

相对复杂,需要了解更多的织入机制和配置

适用场景

适合大多数常见的AOP需求,如事务管理、日志记录等

适合需要更深入和复杂AOP功能的场景,如底层框架开发、对非Spring管理对象进行增强等

三、代码示例

为了更好地理解 Spring AOP 和 AspectJ,下面我们以如何进行日志记录为例,展示两者的实现。

1. 使用 Spring AOP

步骤:

(1) 添加依赖(以 Maven 为例):

<dependencies>
    <!-- Spring AOP -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.23</version>
    </dependency>
    <!-- AspectJ Weaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.19</version>
    </dependency>
</dependencies>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

(2) 定义业务类:

package com.yuanjava.service;

public class UserService {
    public void addUser(String name) {
        System.out.println("Add user: " + name);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

(3) 定义切面类:

package com.yuanjava.aspect;

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

@Aspect
public class LoggingAspect {

    @Before("execution(* com.yuanjava.service.UserService.addUser(..))")
    public void logBefore() {
        System.out.println("Add log before method");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

(4) 配置 Spring 容器(使用 Java 配置):

package com.yuanjava.config;

import com.example.aspect.LoggingAspect;
import com.example.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy// 启用AOP自动代理
publicclass AppConfig {

    @Bean
    public UserService userService() {
        returnnew UserService();
    }

    @Bean
    public LoggingAspect loggingAspect() {
        returnnew LoggingAspect();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

(5) 运行测试:

package com.yuanjava;

import com.yuanjava.config.AppConfig;
import com.yuanjava.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AOPSpringDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.addUser("猿java");
        context.close();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

输出:

Add log before method
Add user: 猿java
  • 1.
  • 2.

2. 使用 AspectJ

步骤:

(1) 添加依赖(以 Maven 为例):

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.19</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId>
        <version>1.9.19</version>
    </dependency>
</dependencies>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

(2) 定义业务类:

package com.yuanjava.service;

public class UserService {
    public void addUser(String name) {
        System.out.println("Add user: " + name);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

(3) 定义切面类:

package com.yuanjava.aspect;

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

@Aspect
public class LoggingAspect {

    @Before("execution(* com.yuanjava.service.UserService.addUser(..))")
    public void logBefore() {
        System.out.println("Add log before method");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

(4) 编译时织入(使用 AspectJ 编译器 ajc):

  • 假设项目结构如下:
src/
├── com/yuanjava/service/UserService.java
├── com/yuanjava/aspect/LoggingAspect.java
  • 1.
  • 2.
  • 3.
  • 运行以下命令进行编译和织入:
ajc -1.8 -d bin -sourcepath src src/com/yuanjava/service/UserService.java src/com/yuanjava/aspect/LoggingAspect.java
  • 1.

(5) 运行测试:

package com.yuanjava;

import com.yuanjava.service.UserService;

public class AspectJDemos {
    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.addUser("猿java");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

输出:

Add log before method
Add user: 猿java
  • 1.
  • 2.

四、使用场景

1. Spring AOP

从整体来看,Spring AOP的使用场景可以包含以下几个方面:

  • 事务管理:在业务方法执行前后开启和提交事务。
  • 日志记录:记录方法执行的日志,如进入、退出、异常等。
  • 权限检查:在方法调用前进行权限验证。
  • 缓存管理:在方法执行前后进行缓存的查询与更新。
  • 性能监控:监控方法的执行时间,进行性能分析。

2. AspectJ

从整体来看,AspectJ的使用场景可以包含以下几个方面:

  • 底层框架开发:对第三方库或应用程序进行更深入的字节码增强。
  • 跨库的数据访问:在数据访问层进行统一的拦截和处理。
  • 复杂的业务逻辑拦截:需要对构造方法、字段赋值等进行拦截的场景。
  • 无Spring环境的项目:在不使用 Spring 框架的项目中实现 AOP 功能。
  • 性能优化:需要高性能的字节码级别的增强,减少运行时开销。
  • 适用理由:AspectJ 提供更强大的 AOP 功能和更灵活的织入机制,适用于需要精细控制切面织入时机和范围的复杂应用。

五、总结

本文,我们分析了 Spring AOP 和 AspectJ 的实现原理,并且通过示例展示了两者如如何使用它们。

  • Spring AOP:Spring AOP 集成简单,适用于大多数基于 Spring 的应用,易于集成和配置,适合实现常见的横切关注点,如事务管理和日志记录。
  • AspectJ:AspectJ 提供更强大的 AOP 功能和更灵活的织入机制,适用于需要深入字节码级别操作或在非 Spring 环境中实现 AOP 的场景。

在实际业务中,选择哪种 AOP框架取决于项目的具体需求和团队的技术栈选择,作为 Java程序员,强烈建议掌握两者的工作原理。

责任编辑:赵宁宁 来源: 猿java
相关推荐

2024-10-15 10:59:18

Spring MVCJava开发

2023-10-23 11:07:37

HTTPRPC

2022-11-15 10:03:34

2024-10-30 16:12:14

2016-03-21 10:40:53

RDDSpark SQL数据集

2024-12-30 07:20:00

Redis数据库MySQL

2025-02-06 08:44:11

MySQLEXISTSIN

2018-05-21 21:26:59

Apache HiveHbaseSQL

2021-10-27 08:54:11

Pythonencodeencoding

2015-02-26 10:29:41

Google百度

2017-11-21 22:49:10

2022-09-03 08:03:14

UbuntuDebian

2012-12-10 09:44:04

路由器本地回路

2021-02-17 00:30:41

机器学习深度学习人工智能

2022-11-18 16:10:03

云计算虚拟机

2020-09-18 15:10:51

Web前端技术

2023-10-24 09:07:14

CookieSessionHTTP

2023-08-29 09:50:42

Unix shellLinux

2024-10-22 16:26:11

2023-06-09 09:10:06

nftablesiptables
点赞
收藏

51CTO技术栈公众号