• AOP和spring AOP学习记录


    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.

    ---官方文档---

    我的博客:https://www.seyana.life/post/12

  • 相关阅读:
    .NET中的一些设计模式
    (原创)无废话C#设计模式之三:Abstract Factory
    一步一步学Linq to sql(一):预备知识
    使用主题来自动包装控件样式
    (原创)无废话C#设计模式之六:Builder
    (原创)无废话C#设计模式之四:Factory Method
    当前匿名用户和当前连接的区别
    单一登陆的例子
    使用rar.exe压缩的例子
    (原创)无废话C#设计模式之九:Proxy
  • 原文地址:https://www.cnblogs.com/mckc/p/12449067.html
Copyright © 2020-2023  润新知