怎么理解面向切面编程(AOP)?

面向切面编程(Aspect-Oriented Programming,AOP)

面向切面编程(Aspect-Oriented Programming,AOP)是一种软件开发的编程范式,它旨在通过将横切关注点(cross-cutting concerns)从主要的业务逻辑中分离出来,使代码更具模块化、可维护性和可重用性。

传统面向对象编程的做法

在传统的面向对象编程中,程序的功能通常被划分为多个对象,每个对象负责特定的业务逻辑。

AOP的做法

横切关注点

然而,有些功能可能涉及多个对象,而这些功能通常不是核心业务逻辑,被称为横切关注点

例如,日志记录、性能监测、事务管理等都是横切关注点,它们会跨越多个模块或对象。

AOP 引入了横切关注点的概念,允许开发人员在程序中定义横切关注点,然后通过特殊的语法或配置将这些关注点插入到主要的业务逻辑中。这样,开发人员可以更好地管理横切关注点,而无需将它们分散在业务逻辑中。

AOP关键概念

AOP 中的关键概念包括:

  1. 切面(Aspect): 定义了横切关注点以及在何处插入这些关注点的逻辑。切面通常包含了一组连接点和通知。

  2. 连接点(Join Point): 在程序执行过程中定义的点,例如方法调用、对象的创建等。切面通过连接点指定在何处插入横切关注点。

  3. 通知(Advice): 定义了在连接点处执行的代码逻辑,包括前置通知(在连接点之前执行)、后置通知(在连接点之后执行)、环绕通知(替代连接点的代码执行)、异常通知(在连接点抛出异常时执行)等。

  4. 切入点(Pointcut): 定义了一组连接点的集合,切面通过切入点指定在哪些连接点上插入通知。

通过使用AOP,我们可以更清晰地关注主要的业务逻辑,而将横切关注点的处理交给专门的切面。
这提高了代码的模块性、可维护性和可重用性。

web开发中的Spring框架就是一个广泛使用AOP的例子,它允许开发人员通过配置或注解来定义切面。

举例

假设我们在开发一个Android应用,其中有一个用户管理模块,需要在用户进行登录操作时记录登录时间。

使用面向切面编程,我们可以创建一个切面,将日志记录的逻辑与用户登录的业务逻辑分离开来。

首先,创建一个简化的用户管理类:

// 用户管理类
public class UserManager {

    public void login(String username) {
        // 模拟用户登录的业务逻辑
        System.out.println(username + " 已登录");
    }

    // 其他用户管理方法...

}

然后,创建一个日志切面:

// 日志切面
@Aspect
public class LoggingAspect {

    @Before("execution(* com.example.UserManager.login(..)) && args(username)")
    public void logLogin(String username) {
        System.out.println("用户 " + username + " 登录,登录时间:" + getCurrentTime());
    }

    private String getCurrentTime() {
        // 获取当前时间的逻辑
        // 注意:在实际应用中,你可能会使用更复杂的日期格式化逻辑
        return LocalDateTime.now().toString();
    }

}

在上述例子中,UserManager是用户管理类,而LoggingAspect是日志切面。通过使用@Before注解,日志切面定义了在调用UserManagerlogin方法之前执行的日志记录逻辑。这里使用了args参数,以便在切面中获取用户登录的参数,例如用户名。

APO开发框架简介

这种方式使得日志记录逻辑与用户管理类的业务逻辑分离开来,提高了代码的模块性和可维护性。

在实际的Java相关开发中,我们可以使用类似的AOP机制,比如AspectJ,来实现类似的功能。

地址:

官网地址
Android插件地址

AspectJ 在Android上面的应用

AspectJ 是一种基于 Java 语言的切面编程的工具和扩展。它提供了一种强大的方式来定义和使用切面,允许我们更灵活地管理横切关注点。

AspectJ 的语法和概念更为丰富,相比于 Spring AOP 提供了更多的功能和精确度。

以下是一个简单的使用 AspectJ 的 Android 示例,假设我们想在应用中记录网络请求的时间:

  1. 引入 AspectJ: 在你的 Android 项目中引入 AspectJ,可以使用 Gradle 插件。在 app 模块的 build.gradle 文件中添加:
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.9.7'
        classpath 'org.aspectj:aspectjweaver:1.9.7'
    }
}

apply plugin: 'android-aspectj'

dependencies {
    implementation 'org.aspectj:aspectjrt:1.9.7'
}
  1. 创建一个切面: 创建一个 AspectJ 切面,用于在网络请求前后记录时间。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;

@Aspect
public class NetworkAspect {

    private long startTime;

    @Before("execution(* okhttp3.Call.execute(..))")
    public void beforeNetworkCall() {
        startTime = System.currentTimeMillis();
        System.out.println("Network request started at: " + startTime);
    }

    @After("execution(* okhttp3.Call.execute(..))")
    public void afterNetworkCall() {
        long endTime = System.currentTimeMillis();
        System.out.println("Network request completed in: " + (endTime - startTime) + " milliseconds");
    }
}

这个切面定义了在执行 okhttp3.Call.execute() 方法前后分别执行的通知,用于记录网络请求的开始时间和结束时间。

  1. 在 Application 类中初始化切面:Application 类中初始化 AspectJ 切面。
import android.app.Application;

import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.NoAspectBoundExceptionThrown;

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            // 注册 AspectJ 切面
            AspectJHelpers.registerAspect(NetworkAspect.class);
        } catch (NoAspectBoundException | NoAspectBoundExceptionThrown e) {
            e.printStackTrace();
        }
    }
}

这里使用了 AspectJHelpers.registerAspect() 方法来注册切面。

执行切面

在 AspectJ 中定义的切面类,通常并不是直接通过代码进行调用的,而是在运行时通过 AOP(面向切面编程)的方式来织入到目标代码中。

示例中,NetworkAspect 是一个切面类,它定义了在执行 okhttp3.Call.execute() 方法前后执行的通知。

在构建项目时,AspectJ 插件会在编译时织入切面的逻辑。确保应用中包含了使用了 okhttp3.Call.execute() 方法的代码,以触发切面的执行。

不需要直接调用 NetworkAspect 类的方法。AspectJ 会在符合切面条件的情况下,在目标代码中自动织入 beforeNetworkCall() 和 afterNetworkCall() 方法的逻辑。

例子中的切入点是 okhttp3.Call.execute() 方法的执行。

  • 19
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林树杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值