精通Spring Framework (4) 深入掌握Spring AOP(二)

二、 AspectJ 编译实现AOP

AspectJ 是静态代理的增强,所谓的静态代理就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强。
AspectJ 是 Java 语言的一个 AOP 实现,其主要包括两个部分:

第一个部分定义了如何表达、定义 AOP 编程中的语法规范,通过这套语言规范,我们可以方便地用 AOP 来解决 Java 语言中存在的交叉关注点问题
另一个部分是工具部分,包括编译器、调试工具等。
AspectJ 是最早、功能比较强大的 AOP 实现之一,对整套 AOP 机制都有较好的实现,很多其他语言的 AOP 实现,也借鉴或采纳了 AspectJ 中很多设计。在 Java 领域,AspectJ 中的很多语法结构基本上已成为 AOP 领域的标准。

2.1 idea 配置AspectJ

jdk 版本: 11.05
aspecj : aspectj1.9
idea 版本: 201903

虽然aspectJ 起始于eclipse 但是idea 也方便的集成了aspectJ 插件。

aspectJ 使用起来并不复杂,在编译期,织入了aop代理,编译后的代码

package com.fancv.aspectj;

import org.aspectj.runtime.internal.AroundClosure;

public class HelloWorld {
    public HelloWorld() {
    }

    public void sayHello() {
        System.out.println("Hello AspectJ");
    }

    public static void main(String[] args) {
        HelloWorld hello = new HelloWorld();
        sayHello_aroundBody1$advice(hello, TxAspect.aspectOf(), (AroundClosure)null);
    }
}
package com.fancv.aspectj;

import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.runtime.internal.AroundClosure;

@Aspect
public class TxAspect {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }

    public TxAspect() {
    }

    @Around(
        value = "call(void HelloWorld.sayHello())",
        argNames = "ajc$aroundClosure"
    )
    public void ajc$around$com_fancv_aspectj_TxAspect$1$8852f95(AroundClosure ajc$aroundClosure) {
        System.out.println("开始事务。。。");
        ajc$around$com_fancv_aspectj_TxAspect$1$8852f95proceed(ajc$aroundClosure);
        System.out.println("结束事务。。。");
    }

    public static TxAspect aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("com_fancv_aspectj_TxAspect", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

分析代码我们轻易可以得出一个简单的结论:aspectj 通过编译器 编译出一套实现AOP的逻辑代码,具体逻辑就不分析了。

2.2spring aop 对比 AspectJ

Spring-AOP Pros

It is simpler to use than AspectJ, since you don’t have to use LTW (load-time weaving) or the AspectJ compiler.

It uses the Proxy pattern and the Decorator pattern

Spring-AOP Cons

This is proxy-based AOP, so basically you can only use method-execution joinpoints.
Aspects aren’t applied when calling another method within the same class.
There can be a little runtime overhead.
Spring-AOP cannot add an aspect to anything that is not created by the Spring factory
AspectJ Pros

This supports all joinpoints. This means you can do anything.
There is less runtime overhead than that of Spring AOP.
AspectJ Cons

Be careful. Check if your aspects are weaved to only what you wanted to be weaved.
You need extra build process with AspectJ Compiler or have to setup LTW (load-time weaving)

Spring AOP 使用更简单,易于上手,使用了同动态代理和装饰器模式

缺点是 spring AOP 只能使用方法切点,运行时有一些开销,只能在spring factory 创建的bean 使用。

spring aop 的代理调试 困难,unreadable long stacktraces because of the proxy-based approach。

Aspects aren’t applied when calling another method within the same class.
在调用同一个类的另一个方法时,切面不生效。

这点不能理解,spring aop 切点可以是类吗?

execution是一种使用频率比较高比较主要的一种切点指示符,用来匹配方法签名,方法签名使用全限定名,包括访问修饰符(public/private/protected)、返回类型,包名、类名、方法名、参数,其中返回类型,包名,类名,方法,参数是必须的,如下面代码片段所示:

@Pointcut("execution(public String org.baeldung.dao.FooDao.findById(Long))")

上面的代码片段里的表达式精确地匹配到FooDao类里的findById(Long)方法,但是这看起来不是很灵活。假设我们要匹配FooDao类的所有方法,这些方法可能会有不同的方法名,不同的返回值,不同的参数列表,为了达到这种效果,我们可以使用通配符。如下代码片段所示:

@Pointcut("execution(* org.baeldung.dao.FooDao.*(..))")

第一个通配符匹配所有返回值类型,第二个匹配这个类里的所有方法,()括号表示参数列表,括号里的用两个点号表示匹配任意个参数,包括0个

ASpectJ 优点:支持更精细的切点,运行时开销小

缺点:学习成本,额外的编译配置。

2.3 什么时候选择AspectJ

spring 采用了动态代理实现AOP,那么在一般情况下spring AOP 完全可以满足开发需要,这里一般情况可以了简单的理解为 业界99% 的中小型项目。

如果你开发的是一个生命周期很长的项目,至少超过20年,那么可以选择使用AspectJ,根据以往的经验,框架是会改变的。(这勉强算是一个考虑点,目前spring 在国内还是很火的,目测未来10年也会持续) AspectJ 更加偏向底层实现。不受框架的影响。

spring framework 给出了Use the simplest thing that can work.
建议,除非你需要在非 spring 框架管理的Bean 使用AOP

参考资料:

  1. 《spring 实战》(第四版)
  2. idea中配置 aspect编译
  3. spring aop 动态代理原理
  4. spriing aop 获取连接点入参出参
  5. idea配置aspectJ
  6. Spring AOP 切点(pointcut)表达式
  7. Introduction to Pointcut Expressions in Spring

猜你喜欢

转载自blog.csdn.net/keep_learn/article/details/115618459