AOP入门-Final

翻译一篇入门教程:https://www.javatpoint.com/spring-aop-tutorial

Aspect Oriented Programming (AOP)
AOP 将程序逻辑打破成不同的部分(称为关注点)其通过横向关注点增加了程序的模块化。
一个横向关注点是一种可以影响整个应用程序的并且应该尽量集中到一处代码的关注点,比如像事务管理,认证,日志和安全等。

为什么使用AOP?

它提供了一种横向编程的方法,通常我们是方法1-方法2-方法3 怎么怎么样,按照向下的流程,而AOP定义了一种在方法2前插入逻辑,在方法3后插入逻辑的机制。这里的方法1并不只是局限在具体的方法1,而是具有某种共性的方法,比如它都以test开头,它们都在包package1下;这种机制下,如果要给这些个具有某种相同特征的方法增加通用的逻辑时,只需加入一个函数定义,而不是多个;当然,按照通常的程序编写逻辑,这个很难实现,或者还是要把这些方法再某个统一调用处包一层,然而AOP一般利用编译器或者语言特性预留的功能点,很容易实现这个功能。

AOP概念和术语

AOP术语有如下:

  • Join point
  • Advice
  • Pointcut
  • Introduction
  • Target Object
  • Aspect
  • Interceptor
  • AOP Proxy
  • Weaving
Join point

Join point is any point in your program such as method execution, exception handling, field access etc. Spring supports only method execution join point.
Join point是你程序中的任何点,像方法的执行,异常处理,域访问等等。Spring只支持方法执行join point

Advice

Advice(通知)表示一个aspect在一个特定join point 要执行的动作,这里有多种不同的advices:

  • Before Advice:在一个join point前执行
  • After Returning Advice:在一个join point正常完成后执行
  • After Throwing Adivce:在方法抛异常退出时执行
  • After(finall)Advice:在一个join point之后执行,无论它是正常退出还是异常返回
  • Around Advice:在一个join point之前和之后执行
Pointcut

用来匹配Join point的断言,Spring默认使用AspectJ表达式;PointCut匹配到JoinPoint是AOP的核心

Introduction

它表示为一个类型介绍一个额外的方法或者域,它允许你为一个任何被通知的对象引入一个新接口。
It means introduction of additional method and fields for a type. It allows you to introduce new interface to any advised object.

Target Object

它是一个对象,被一个或者多个aspect通知的对象。也就是我们知道的Spring中的代理对象,因为Spring AOP是通过运行时代理实现的

Aspect

它是一个包含了advices,joinpoints等的类

Interceptor

它是一个只包含了一个advice的aspect

AOP Proxy

通常用来实现aspect契约,AOP框架创建的,框架将是JDK动态代理或者Spring框架中的CGLIB代理。

Weaving

TBD

AOP实现

以下三个提供了AOP的实现:AspectJ SpringAOP JBoss AOP
SpringAOP存在self-invocation issue,因为它是基于代理的,而AspectJ没有,AspectJ不是基于Proxy

However, once the call has finally reached the target object (the SimplePojo reference in this case), any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to run.

图中Pojo是接口
SpringAOP Proxying
AOP入门-Final_第1张图片

Spring AOP

可以用下面三个方法使用Spring AOP,但是通常广泛使用的是注解风格,三种分别是:

  1. 通过Spring1.2老式风格(基于dtd)(Spring3也支持)
  2. 通过AspectJ注解风格
  3. 通过Spring XML配置风格(基于schema)
区别与对比
this与target

https://stackoverflow.com/questions/11924685/spring-aop-target-vs-this
https://docs.spring.io/spring-framework/reference/core/aop/proxying.html#page-title
Spring AOP is a proxy-based system and differentiates between the proxy object itself (bound to ‘this’) and the target object behind the proxy (bound to ‘target’).
this 指向代理对象 target指向代理后面的对象

this(AType) this instanceOf AType 被调用的方法的类是AType时起作用;
target(AType) means all join points where anObject instanceof AType . If you are calling a method on an object and that object is an instanceof AccountService, that will be a valid joinpoint.
To summarize a different way - this(AType) is from a receivers perspective, and target(AType) is from a callers perspective.

就起作用来说,没有区别

withincode和 execution的区别 callink呢 类 ReflectiveAspectJAdvisorFactory

SpringAOP不支持 withincode 和call 完结
https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html
execution不多说了,就是可以使用通配符匹配方法,需要注意的是target类名不支持通配符

call和execution:实际上,call和execution是两回事,方法被调用,方法被执行,就像一个是jmp指令,一个是这个方法的执行指令
At a call join point, the enclosing code is that of the call site. call(void m()) && withincode(void m()) 就表示一个直接递归调用
At anexecution join point, the program is already executing the method, so the enclosing code is the method itself.
execution(void m()) && withincode(void m()) = execution(void m())
看起来withincode就是表示 enclosing code的,封闭代码?
The call join point does not capture super calls to non-static methods
上面来自 https://eclipse.dev/aspectj/doc/released/progguide/language-joinPoints.html

AOP中重试会死循环吗?

我认为是会的,想象观察一个方法的调用,它异常时,你再调用,问题是这个时候,它是否会再次进入AOP,会进入就存在死循环的可能,反之就不会死循环。那么会再次进入AOP吗?会,因为目标方法确实是再次执行了,只不过被 包装了。

但是实测没有,并不是想的那样,"包装"都多种形式:Proxy代理是包装,把一段逻辑或者一个函数通过Method类处理成一个对象,也是包装。
代码如下:

@Around("execution(* org.jimmy.democode.controller.Controller1.* (..)) || target(org.jimmy.democode.controller.Controller1)")
public Object logWriteAround(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        log.info("Controller1Aspect before method");
        return joinPoint.proceed();
    } catch (Exception e) {
        return joinPoint.proceed();
    }
}

来看下函数栈,没有走Proxy类

AOP入门-Final_第2张图片

如下代码,getThis()会死循环,getTarget()不会死循环,也对应了上面说的this是Proxy,target是被代理的类;代理类会对hello()又会进行重复的拦截

    @Around("execution(* org.jimmy.democode.controller.Controller1.* (..)) || target(org.jimmy.democode.controller.Controller1)")
    public Object logWriteAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            log.info("Controller1Aspect before method");
            return joinPoint.proceed();
        } catch (Exception e) {
            Controller1 controller1 = (Controller1) joinPoint.getThis();
            // Controller1 controller1 = (Controller1) joinPoint.getTarget();    // getTarget时函数栈显示栈顶两个函数直接就是这行和controller行了
            return controller1.hello();
        }
    }

死循环时的堆栈,显然看到调用的方法是代理类的方法org.jimmy.democode.controller.Controller1$$SpringCGLIB$$0.hello()
AOP入门-Final_第3张图片

代码组织AOP

通常在单测中能用到
https://docs.spring.io/spring-framework/reference/core/aop/aspectj-programmatic.html

// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied
// must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();

你可能感兴趣的:(java,spring)