欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AOP和spring AOP学习记录

程序员文章站 2022-04-14 16:36:49
AOP基本概念的理解 面向切面AOP主要是在编译期或运行时,对程序进行织入,实现代理, 对原代码毫无侵入性,不破坏主要业务逻辑,减少程序的耦合度。 主要应用范围: 日志记录,性能统计,安全控制,事务处理,异常处理等等 名词性概念 切面(Aspect) 通常是一个类,在里面可以定义切入点和通知,即 。 ......

aop基本概念的理解

面向切面aop主要是在编译期或运行时,对程序进行织入,实现代理,

对原代码毫无侵入性,不破坏主要业务逻辑,减少程序的耦合度。

  • 主要应用范围:
    日志记录,性能统计,安全控制,事务处理,异常处理等等

名词性概念

  • 切面(aspect)
    通常是一个类,在里面可以定义切入点和通知,即切面=切入点+通知
  • 连接点(joint point)
    被拦截到的点,因为 spring 只支持方法类型的连接点,所以在 spring 中连接点指的就是被拦截的到的方法,实际上连接点还可以是字段或者构造器。
  • 切入点(pointcut)
    对连接点进行拦截的定义。
  • 通知(advice)
    拦截到连接点之后所要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
  • aop 代理
    aop 框架创建的对象,代理就是目标对象的加强。spring 中的 aop 代理可以使 jdk 动态代理,也可以是 cglib 代理,前者基于接口,后者基于子类。
  • 织入(weaving)
    把切面加入到对象,并创建出代理对象的过程。

动态代理

在运行期间生成对象进行代理,

spring aop就是动态代理。

静态代理

自己编写代理对象,在编译器织入,

aspectj就是在编译期间进行织入,从而减少对运行时效率的影响。

springaop

根据对象是否是实现类选择代理的方法

  • 如果要代理的对象实现了接口,spring aop会根据接口,使用jdk proxy创建代理

  • 如果没有实现接口,则通过cglib代理

当然也可以指定都使用cglib进行切入,而jdk方法不适用于没有接口实现的目标类

对于jdk proxy

jdk proxy会实现目标类的接口,代理逻辑就在新的实现类中

只能对实现类进行代理,性能也优于cglib,所以平时都是分为"接口-实现iml"来设计。

对于cglib

cglib会创建一个代理目标类的子类,而代理逻辑就在这一子类中添加,

然后得到这一子类的对象,也就是代理增强后的对象,一切都是动态的,

具体实现有待学习。

切面类

使用@aspect注解定义切面类

比如这样

@component
@aspect
@oder(99)
public class weblogaspect {}

另外对同一个方法有多个切面进行代理的时候,难免需要区分执行顺序,

这时候可以使用@order注解定义优先级,数字越低,级别越高。

切面advice

执行顺序around -> before -> around -> after -> afterruternning

@after

第一个参数都必须是joinpoint类型

在目标方法完成后通知,

无论方法是以何种方式完成,异常也是如此

@after-returning

第一个参数都必须是joinpoint类型

结束并安全返回时通知,异常不通知

@after-throwing

第一个参数都必须是joinpoint类型

异常时通知

@before

第一个参数都必须是joinpoint类型

顾名思义

@around

最强大的通知

第一个形参必须是proceedingjoinpoint类型,

public interface proceedingjoinpoint extends joinpoint {
    void set$aroundclosure(aroundclosure var1);

    default void stack$aroundclosure(aroundclosure arc) {
        throw new unsupportedoperationexception();
    }

    object proceed() throws throwable;

    object proceed(object[] var1) throws throwable;
}

可以看到它继承自joinpoint,多了proceed方法

注解的方法体内调用proceedingjoinpoint参数的proceed()方法才会执行目标。

调用这个方法时,还可以传入一个object[]对象作为参数

可以通过这个切面决定方法是否执行,改变传入参数,改变返回值,检查异常等

参数方法

  • getargs(): returns the method arguments.

  • getthis(): returns the proxy object.

  • gettarget(): returns the target object.

  • getsignature(): returns a description of the method that is being advised.

  • tostring(): prints a useful description of the method being advised.

切点表达式

有关切点表达式,建议阅读

可以定义一个切点,之后就不用一一指定,直接带入value即可,例如:

@pointcut("execution(* com.hyg.app.controller..*.*(..))")
public void weblog(){}

@before(value = "weblog()")
public void dobefore (joinpoint jp){
  string name = jp.getsignature().getname();
  system.out.println(name+"开始执行");
}

execution

表达式中最常用的方法是execution,粒度最小

对于execution(* com.hyg.app.controller..*.*(..))

  • execution表达式的类型指定
  • 第一个*代表 任意的返回值类型
  • com.jiuxian aop所切的包名
  • 包后面..表示当前包其子包,一个.是当前包,两个.就包括所有子包
  • 第二个* 表示类名,代表所有类
  • .*(..)表示任何方法, 括号代表参数 .. 表示任意参数

后面那串东西,可以记为每一个.都是更深的一个粒度

来个精确点的示例:

execution(* com.hyg.app.service.*service.add*(string))

表示所有类型的,com.hyg.app.service包下的,

所有以service结尾的类,

add开头的,参数类型为string的方法

例子

在官方文档中有很多示例,如下

所有public方法
execution(public * *(..))

所有名字以set开头的方法
execution(* set*(..))

所有accountservice中实现的接口的方法
execution(* com.xyz.service.accountservice.*(..))

所有service包下的方法
execution(* com.xyz.service.*.*(..))

所有在service包以及子包下的方法
execution(* com.xyz.service..*.*(..))

within(com.xyz.service.*)  所有service包下方法
within(com.xyz.service..*) 所有service包和子包下方法
this(com.xyz.service.accountservice) 匹配accountservice代理的对象
target(com.xyz.service.accountservice) 实现了accountservice接口的对象

拥有transactional注解的
@annotation(org.springframework.transaction.annotation.transactional)
传递的参数是serializable的
args(java.io.serializable):

关于execution和within的区别

execution可以指定方法返回类型,类名,方法名和参数名等与方法相关的部件,

而within的最小粒度是类。

关于this和target的区别

this匹配的是代理类,即目标对象被代理后的代理对象

target则是匹配普通的目标对象

以下情况外,二者的效果都是一致的。

this(someobject)或target(someobject),这里someobject实现了某个接口:对于这种情况,虽然表达式中指定的是一种具体的对象类型,但由于其实现了某个接口,因而spring默认会使用jdk代理为其生成代理对象,jdk代理生成的代理对象与目标对象实现的是同一个接口,但代理对象与目标对象还是不同的对象,由于代理对象不是someobject类型的,因而此时是不符合this语义的,而由于目标对象就是someobject类型,因而target语义是符合的,此时this和target的效果就产生了区别;这里如果强制spring使用cglib代理,因而生成的代理对象都是someobject子类的对象,其是someobject类型的,因而this和target的语义都符合,其效果就是一致的。

args

用于指定参数类型,类型必须是全路径的

官网解释

  • execution: for matching method execution join points. this is the primary pointcut designator to use when working with spring aop.
  • within: limits matching to join points within certain types (the execution of a method declared within a matching type when using spring aop).
  • this: limits matching to join points (the execution of methods when using spring aop) where the bean reference (spring aop proxy) is an instance of the given type.
  • target: limits matching to join points (the execution of methods when using spring aop) where the target object (application object being proxied) is an instance of the given type.
  • args: limits matching to join points (the execution of methods when using spring aop) where the arguments are instances of the given types.
  • @target: limits matching to join points (the execution of methods when using spring aop) where the class of the executing object has an annotation of the given type.
  • @args: limits matching to join points (the execution of methods when using spring aop) where the runtime type of the actual arguments passed have annotations of the given types.
  • @within: limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using spring aop).
  • @annotation: limits matching to join points where the subject of the join point (the method being executed in spring aop) has the given annotation.

我的博客: