其他分享
首页 > 其他分享> > AOP 概念篇

AOP 概念篇

作者:互联网

Spring AOP 系列的第一篇

先介绍一下 AOP 相关的一些概念。

出现的契机

在现实中、我们经常需要记录重要操作的流水以及打印相关的日志到日志文件

// 微信公众号:CoderLi
public class BizService01 {
    public void dealBiz(BizDto bizDto) {
       // 脱敏打印 + 统计上报到运营系统
        record(bizDto);
        // 业务操作
    }
  private void record(BizDto bizDto){
    // .....
  }
}

当存在 n 多个这样的 Service 的时候、我们进一步的操作可能是:将其抽取到一个公共的地方进行记录维护

// 微信公众号:CoderLi
public class BizService01 {
    public void dealBiz(BizDto bizDto) {
        // 脱敏打印 + 统计上报到运营系统
        RecordUtils.record(bizDto);
        // 业务操作
    }    
}

再进一步、我们可能使用模板方法来设计。子类继承该基础服务

// 微信公众号:CoderLi
public abstract class BaseBizService {

    public void dealBiz(BizDto bizDto){
        // 脱敏打印 + 统计上报到运营系统
        if (isRecord()) {
            RecordUtils.record(bizDto);
        }
        readDealBiz(bizDto);
    }
    protected abstract void readDealBiz(BizDto bizDto);
    protected boolean isRecord(){
        return true;
    }
}

这样子貌似是能解决统一处理非核心业务、但是如果我们还需要进行其他的一些非核心业务处理的时候、比如说、权限检验、性能监控等、并且这些非核心业务他们的顺序、在每一个业务场景中、顺序可能不一样、是否执行这些非核心业务也是不确定的、那么单纯靠模板方法来解决的话、会显得非常费力、甚至可以说、这种变化的流程不适合模板方法了已经。

这个时候 AOP 出现了。而第一个比较流行的 Java AOP 框架就是 AspectJ 了。

AspectJ 使用

使用 Idea 来演示一个 Hello World 程序。确保已经安装并且启用了 AspectJ 插件

微信公众号:CoderLi

使用 Ajc 编译器、并配置 AspectJTools jar 的路径

微信公众号:CoderLi

项目 pom 依赖

<!-- 微信公众号:CoderLi-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.7</version>
</dependency>
public class Main {
    // 微信公众号:CoderLi
    void sayHi(){
        System.out.println("hello world");
    }
    public static void main(String[] args) {
        Main main = new Main();
        main.sayHi();
    }
}
// 切面。微信公众号:CoderLi
public aspect MainAspect {
    // 切点
    pointcut sayHiPointcut():call(* Main.sayHi());
    // 前置通知
    before():sayHiPointcut(){
        System.out.println("say Hi Before");
    }
    // 后置通知
    after ():sayHiPointcut(){
        System.out.println("say Hi After");
    }
}

微信公众号:CoderLi

我们使用了关键字 aspect 创建了一个切面。在这个切面里面、我们使用了 pointcut 定义了一个切点。而所谓的切点就是切面需要应用的方法、而这些方法我们称之为目标方法。在这个切面里面我们还定义了两个通知、一个是目标方法执行前的通知、一个是执行后的通知。

切面就是切点和通知的组合体。

我们看下 Main class 反编译的内容

public static void main(String[] args) {
    Main main = new Main();
    Main var10000 = main;

    try {
      // 前置通知
        MainAspect.aspectOf().ajc$before$com_demo_aspectj_MainAspect$1$acd0869f();
        var10000.sayHi();// 目标方法
    } catch (Throwable var3) {
      // 后置通知
        MainAspect.aspectOf().ajc$after$com_demo_aspectj_MainAspect$2$acd0869f();
        throw var3;
    }
		// 后置通知
    MainAspect.aspectOf().ajc$after$com_demo_aspectj_MainAspect$2$acd0869f();
}

后置通知在 catch 代码块中、确保异常的时候也能被通知到。

通知的类型:

  1. 前置通知
  2. 后置通知
  3. 异常通知
  4. 后置返回通知
  5. 环绕通知

相关概念

Introduction:引介是一种特殊的通知,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

织入

前面我们也看到编译之后的 Main class 类、可以看到它是实际性的改变了我们原有的 class 文件的。这称之为静态织入。

主要是 ajc 编译器在编译期将 aspect 类编译成 class 字节码之后、然后织入到目标类中。

微信公众号:CoderLi

还有一种是更加常见的织入方式--动态织入。动态织入是在运行期间将要增强的代码织入到目标类中、这样往往是通过动态代理完成的。比如说 Java JDK 的动态代理或者 CGLib 的动态代理。Spring AOP 就是采用动态织入的。

Pointcut 表达式

切点的表达式。Spring Aop只支持其中的9种,外加Spring Aop自己扩充的一种一共是10种类型的表达式

execution:一般用于指定方法的执行,用的最多。
within:指定某些类型的全部方法执行,也可用来指定一个包。
this:Spring Aop是基于代理的,生成的bean也是一个代理对象,this就是这个代理对象,当这个对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
target:当被代理的对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
args:当执行的方法的参数是指定类型时生效。
@target:当代理的目标对象上拥有指定的注解时生效。
@args:当执行的方法参数类型上拥有指定的注解时生效。
@within:与@target类似,看官方文档和网上的说法都是@within只需要目标对象的类或者父类上有指定的注解,则@within会生效,而@target则是必须是目标对象的类上有指定的注解。而根据笔者的测试这两者都是只要目标类或父类上有指定的注解即可。
@annotation:当执行的方法上拥有指定的注解时生效。
bean:当调用的方法是指定的bean的方法时生效

这个后续的文章再详细介绍

标签:Spring,通知,bizDto,切点,概念,织入,AOP,public
来源: https://www.cnblogs.com/-coder-li/p/15705445.html