• Spring 使用介绍(六)—— AOP(二)


    一、切入点语法

    1)通配符

    AOP支持的通配符:

    *:匹配任何数量字符

    ..:匹配任何数量字符的重复,在类型模式中匹配任何数量子包,在方法参数模式中匹配任何数量参数

    +:匹配指定类型的子类型,仅能作为后缀放在类型模式后边

    实例:

    java.lang.String    匹配String类型
    java.*.String      匹配java包下的任何“一级子包”下的String类型,如匹配java.lang.String,但不匹配java.String或java.lang.ss.String
    java..*           匹配java包及任何子包下的任何类型,如java.String、java.lang.String
    java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型
    java.lang.Number+   匹配任何java.lang.Number的子类型,如java.lang.Integer、java.math.BigInteger

    2)类型匹配,语法如下:

    注解?  类的全限定名

    • 注解:可选,类型上持有的注解
    • 类的全限定名:必填,任何类全限定名

    3)方法匹配,语法如下:

     注解?  修饰符?  返回值类型  类型声明?方法名(参数列表)   异常列表?

    • 注解:可选,方法上持有的注解
    • 修饰符:可选,如public、protected
    • 返回值类型:必填,可以是任何类型模式,“*”表示所有类型
    • 类型声明:可选,可以是任何类型模式
    • 方法名:必填,“*”表示任何方法名
    • 参数列表:必填,如:
      • ()  表示方法没有任何参数
      • (..)  表示方法接受任意个参数
      • (..,java.lang.String)    表示方法接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数
      • (*,java.lang.String)    表示方法接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数
    • 异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割,如throws cn.matt.Exception1,cn.matt.Exception2

    4)切入点表达式逻辑运算符

    AOP使用 且(&&)、或(||)、非(!)来组合切入点表达式

    在XML中,“&&”须使用转义字符“&&”代替,不方便,因此Spring提供了and、or、not来代替&&、||、!

    注意:替代符(and、or、not)仅在xml中可用,注解方式使用时会解析错误

    5)execution命令  使用“execution(方法表达式)”匹配方法执行

    实例:

    public * *(..)    任何公共方法的执行
    * cn.javass..*.*(..)    cn.javass包及所有子包下任何类的任何方法
    * cn.javass..IPointcutService.*(*)    cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法
    * cn.javass..IPointcutService+.*()    cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法
    * (cn.javass..IPointcutService+&& java.io.Serializable+).*(..)    任何实现了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的类型的任何方法
    @java.lang.Deprecated @cn.javass..Secure  * *(..)    任何持有@java.lang.Deprecated和@cn.javass..Secure注解的方法
    * (@cn.javass..Secure *).*(..)    任何定义方法的类型持有@cn.javass..Secure的方法

    二、通知参数

    获取被通知方法参数,并传递给通知方法,有两种方式:

    1)使用JoinPoint获取

    对于任何通知方法,当第一个参数是JoinPoint或JoinPoint.StaticPart,这些类型对象会自动传入

    接口定义说明:

    public interface JoinPoint {  
        String toString();         //连接点所在位置的相关信息  
        String toShortString();     //连接点所在位置的简短相关信息  
        String toLongString();     //连接点所在位置的全部相关信息  
        Object getThis();         //返回AOP代理对象  
        Object getTarget();       //返回目标对象  
        Object[] getArgs();       //返回被通知方法参数列表  
        Signature getSignature();  //返回当前连接点签名  
        SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
        String getKind();        //连接点类型  
        StaticPart getStaticPart(); //返回连接点静态部分  
    }
    public interface ProceedingJoinPoint extends JoinPoint {  
        public Object proceed() throws Throwable;  
        public Object proceed(Object[] args) throws Throwable;  
    } 
    public interface StaticPart {  
        Signature getSignature();    //返回当前连接点签名  
        String getKind();          //连接点类型  
        int getId();               //唯一标识  
        String toString();         //连接点所在位置的相关信息  
        String toShortString();     //连接点所在位置的简短相关信息  
        String toLongString();     //连接点所在位置的全部相关信息  
    }  

    注意:环绕通知使用的ProceedingJoinPoint,继承自JoinPoint 

    使用实例:

    @Before(value="execution(* sayBefore(*))")  
    public void before(JoinPoint jp) {..}  
       
    @Before(value="execution(* sayBefore(*))")  
    public void before(JoinPoint.StaticPart jp) {..}  

    2)自动获取

     自动获取通过execution及args实现,如

    @Before(value="execution(* test(*)) && args(param)", argNames="param")  
    public void before1(String param) {  
        System.out.println("===param:" + param);  
    }  

    参数传递步骤:

    • 首先execution(* test(*))匹配任何方法名为test,且有一个任何类型的参数
    • args(param)将首先查找通知方法上同名的参数,并在方法执行时(运行时)匹配传入的参数是使用该同名参数类型,即java.lang.String;如果匹配将把该被通知参数传递给通知方法上同名参数

    这两种方式可一起使用:

    @Before(value=" args(param)", argNames="param")   
    public void before1(JoinPoint jp, String param) {  
        System.out.println("===param:" + param);  
    }  

    注意:第一个参数类型是JoinPoint或JoinPoint.StaticPart时,应该从“argNames”属性省略掉该参数名(可选,写上也对)

    三、通知顺序

    1)同一个切面中有多个相同类型的通知,执行顺序不确定,且不可指定

    2)不同切面中的相同类型通知,可指定执行顺序

    XML方式:

    <aop:aspect ref="dataSourceAspect" order="1">
    ...
    </aop:aspect>

    注解方式:

    @Aspect
    @Order(1)
    public class HelloAspect {
    ...
    }

    四、AOP代理机制

    Spring AOP通过代理模式实现,支持两种代理:

    1)JDK动态代理:使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口,然后对接口创建AOP代理

    2)CGLIB代理:CGLIB代理不仅能进行接口代理,也能进行类代理,但不能通知final方法(CGLIB通过生成子类来创建代理,final方法不能被覆盖)

    默认情况下,Spring AOP首先使用JDK动态代理,如果目标对象没有实现任何接口,将使用CGLIB代理

    可强制使用CGLIB代理,配置如下:

    XML方式:

    <aop:config proxy-target-class="true">  
    </aop:config>  

    注解:

    <aop:aspectj-autoproxy proxy-target-class="true"/>  

    AOP使用实例: 使用Spring的AbstractRoutingDataSource实现多数据源切换

    参考:

    第六章  AOP 之 6.5 AspectJ切入点语法详解 ——跟我学spring3

    第六章  AOP 之 6.6 通知参数 ——跟我学spring3

    第六章  AOP 之 6.7 通知顺序 ——跟我学spring3

    第六章  AOP 之 6.9 代理机制 ——跟我学spring3

  • 相关阅读:
    BEM(Block–Element-Modifier)
    http://element.eleme.io/#/zh-CN/component/quickstart
    Commit message 的写法规范。本文介绍Angular 规范(
    好的commit应该长啥样 https://github.com/torvalds/linux/pull/17#issuecomment-5654674
    代码管理
    if you have content fetched asynchronously on pages where SEO is important, SSR might be necessary
    Martin Fowler’s Active Record design pattern.
    The Zen of Python
    Introspection in Python How to spy on your Python objects Guide to Python introspection
    Object-Oriented Metrics: LCOM 内聚性的度量
  • 原文地址:https://www.cnblogs.com/MattCheng/p/8960944.html
Copyright © 2020-2023  润新知