Spring AOP原理以及原生AspectJ原理探究

前言

前两天看了一些关于spring AOP以及AspectJ的文章,但是总是感觉非常的乱,有的说spring aop跟aspectj相互独立,有的说spring aop依赖于aspectj,有的甚至直接把两者混为一谈。甚至很多实用Spring Aop的朋友并不知道是依赖于AspectJ来开启AspectJ模式,进而简化配置的。

为什么用AspectJ

为什么用AspectJ,我的理解是两个字”方便“。我们知道面向切面编程(Aspect Oriented Programming)有诸多好处,但是在使用AspectJ之前我们一般是怎么编写切面的呢?我想一般来说应该是三种吧:静态代理,jdk动态代理,cglib动态代理。但是我们知道,静态代理的重用性太差,一个代理不能同时代理多种类;动态代理可以做到代理的重用,但是即使这样,他们调用起来还是比较麻烦,除了写切面代码以外,我们还需要将代理类耦合进被代理类的调用阶段,在创建被代理类的时候都要先创建代理类,再用代理类去创建被代理类,这就稍微有点麻烦了。比如我们想在现有的某个项目里统一新加入一些切面,这时候就需要创建切面并且侵入原有代码,在创建对象的时候添加代理,还是挺麻烦的。
说到底,这种麻烦出现的本质原因是,代理模式并没有做到切面与业务代码的解耦。虽然将切面的逻辑独立进了代理类,但是决定是否使用切面的权利仍然在业务代码中。这才导致了上面这种麻烦。
而Spring AOP里面的代理实现方式就是spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。
Spring的这个处理确实让动态代理简洁了很多,但是,AspectJ框架对于代理的处理更加简洁,我们无需关注切点和切面(即被代理对象),只需关注我们需要织入逻辑就ok。这也是为什么Spring引用他的原因。

实现原理

关于静态代理和动态代理的实现方式及原理,不了解的或者有兴趣的可以点我
而AspectJ的实现原理其实比较简单,首先是使用切面语法,这套东西做到了将决定是否使用切面的权利还给了切面。在写切面的时候就可以决定哪些类的哪些方法会被代理,从而从逻辑上不需要侵入业务代码。其次,最重要的就是他的织入工具aspectjweaver,他的作用就是在编译期(或者类加载期)将切面代码通过某种形式插入到业务代码中,也就是我们生成的.class文件其实就是相当于我们动态代理中的代理对象了

AspectJ相关jar包

AspectJ其实是eclipse基金会的一个项目,官网就在eclipse官网里。官网里提供了一个aspectJ.jar的下载链接,但其实这个链接只是一个安装包,把安装包里的东西解压后就是一个文档+脚本+jar包的程序包,其中比较重要的是如下部分:

myths@pc:~/aspectj1.8$ tree bin/ lib/
bin/
├── aj
├── aj5
├── ajbrowser
├── ajc
└── ajdoc
lib/
├── aspectjrt.jar
├── aspectjtools.jar
├── aspectjweaver.jar
└── org.aspectj.matcher.jar

当然,这些jar包并不总是需要从官网下载,很多情况下在maven等中心库中直接找会更方便。
这当中重点的文件是四个jar包中的前三个,bin文件夹中的脚本其实都是调用这些jar包的命令。

  • aspectjrt.jar包主要是提供运行时的一些注解,静态方法等等东西,通常我们要使用aspectJ的时候都要使用这个包。
  • aspectjtools.jar包主要是提供赫赫有名的ajc编译器,可以在编译期将将java文件或者class文件或者aspect文件定义的切面织入到业务代码中。通常这个东西会被封装进各种IDE插件或者自动化插件中。
  • aspectjweaverjar包主要是提供了一个java agent用于在类加载期间织入切面(Load time weaving)。并且提供了对切面语法的相关处理等基础方法,供ajc使用或者供第三方开发使用。这个包一般我们不需要显式引用,除非需要使用LTW。

上面的说明其实也就指出了aspectJ的几种标准的使用方法(参考文档):

  • 编译时织入,利用ajc编译器替代javac编译器,直接将源文件(java或者aspect文件)编译成class文件并将切面织入进代码。
  • 编译后织入,利用ajc编译器向javac编译期编译后的class文件或jar文件织入切面代码。
  • 加载时织入,不使用ajc编译器,利用aspectjweaver.jar工具,使用java agent代理在类加载期将切面织入进代码。

示例

首先,引入AspectJ依赖:
在这里插入图片描述
创建App.java文件:

package com.mythsman.test;

public class App {

    public void say() {
        System.out.println("App say");
    }

    public static void main(String[] args) {
        App app = new App();
        app.say();
    }
}

建切面类AnnoAspect.java:

package com.mythsman.test;

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

@Aspect
public class AnnoAspect {

    @Pointcut("execution(* com.mythsman.test.App.say(..))")
    public void jointPoint() {
    }

    @Before("jointPoint()")
    public void before() {
        System.out.println("AnnoAspect before say");
    }


    @After("jointPoint()")
    public void after() {
        System.out.println("AnnoAspect after say");
    }

}

我们编译之后,可以看见App.class如下:

package com.mythsman.test;

import java.io.PrintStream;

public class App
{
  public void say()
  {
    try
    {
      AnnoAspect.aspectOf().before();System.out.println("App say");
    }
    catch (Throwable localThrowable)
    {
      AnnoAspect.aspectOf().after();throw localThrowable;
    }
    AnnoAspect.aspectOf().after();
  }
  
  public static void main(String[] args)
  {
    App app = new App();
    app.say();
  }
}

可以看见,代码被织入到了App.class中。正如我们上面所说,aspectJ的实现和Spring AOP是不一样的,他是直接将代码织入到编译后的class文件中。这里,也引申出一个新的问题,那Spring AOP引用aspectJ这个框架,是不是表示Spring现在也是通过织入的方式实现AOP的呢?其实spring aop还是通过动态代理来实现aop的,虽然spring aop采用了aspectj语法来定义切面,但是在实现切面逻辑的时候还是采用CGLIB来进行动态代理的方法。

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值