本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
spring的pointcut正则表达式的实现
程序猿小军 · 197浏览 · 发布于2022-08-03 +关注

本文主要介绍了spring的pointcut正则表达式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.Pointcut表达式各部分的约束规则

在spring中配置切面或者数据库的事务会要求:对具体方法或者是一类特征相同的方法添加日志,事务,或者其他对原方法的增强。这时候就会用到pointcut表达式对方法进行过滤,筛选出符合要求的方法;

既然会涉及到筛选具体的方法,那pointcut一定要匹配出完整的方法路径:全限定类名+方法名;在同一个类中,方法可能被重写而区分重写的方法就是:参数列表;因此pointcut表达式中必须包含这3部分:全限定类名+方法名+参数列表;在spring中还有:访问修饰符,返回值类型;这2个不是必须的;pointcut整体结构:

有了pointcut的整体结构之后就可以根据自己的规则,分别写这几部分的正则表达式了;

  • execution()这部分是固定写法,它包含了完整的表达式;

  • 访问修饰符,返回值,classPath,methodName之间用空格分开;

  • methodName,paramList用()分开;methodName(paramList);

1.1 ACCESS_MODIFIER 访问修饰符

访问修饰符有四种取值:public,private,protected,defalut; " * " ,对访问修饰符不加限制;

1.2 RETURN_TYPE 返回值类型

返回值类型可以是任意类型:8个基础类型 + 对象 + 数组 + void;

  • 基础类型的匹配 BASIC_TYPE: (byte|char|boolean|short|int|long|double|float)

  • 对象匹配:[A-Z]\\w*

  • 返回类型可以是以上2种类型的数组(可以是2维,3维,4维.。。);PARAM_TYPE = (BASIC_TYPE|[A-Z]\\w)(\\[\\])**

最后结合返回值:void, * (表示任意类型);返回值的所有可能取值的正则表达式:(void|PARAM_TYPE|\\*)

1.3 CLASS_PATH 全限定类名

有一个特点:(packageName + “.”) * + className;包名,类名都可以用同一个正则表达式;

CLASS_PATH = "((\\*?\\w+\\*?|\\*)\\.)*(\\*?\\w+\\*?|\\*)";

1.4 EXECUTION 表达式

  METHOD_NAME,PARAM_LIST这2个的匹配分别参照:全限定类名,返回值类型的正则表达式;将这些部分分别写完之后,再按顺序组合一下就可以得到完整的表达式了;再考虑到,写表达式的时候,会习惯性的敲空格,因此可以在合适的地方允许空格;

public class PointcutUtils {
    private static final String ACCESS_MODIFIER = "(public|private|protected|default|\\*)";
     private static final String RETURN_TYPE;
     private static final String CLASS_PATH = "((\\*?\\w+\\*?|\\*)\\.)*(\\*?\\w+\\*?|\\*)";
     private static final String METHOD_NAME = "(\\*?\\w+\\*?|\\*)";
     private static final String PARAM_LIST ;
     private static final String EXECUTION ;
    //基础类型
    private static final String BASIC_TYPE="(byte|char|boolean|short|int|long|double|float)";
    //参数类型
    private static final String PARAM_TYPE;
      static{
        //参数类型:基础类型 + Object + 数组类型
        PARAM_TYPE = "("+BASIC_TYPE+"|[A-Z]\\w*)(\\[\\])*";
        //返回值类型:void  + 参数类型
        RETURN_TYPE="(void|"+PARAM_TYPE+"|\\*)";
        //参数列表
        PARAM_LIST = "(\\.\\.|"+PARAM_TYPE+"(\\s*,\\s*"+PARAM_TYPE+")*" +"|)";
        //execution表达式
        EXECUTION = "\\s*execution\\s*\\(\\s*"+ACCESS_MODIFIER+"\\s+"+RETURN_TYPE+"\\s+"+CLASS_PATH+"\\s+"+METHOD_NAME+"\\(\\s*"+PARAM_LIST+"\\s*\\)\\s*"+"\\s*\\)\\s*";
     }
    //检测pointcut是否是正确的
    static boolean  checkPointcut(String pointcutReg){
        return pointcutReg == null ? false : pointcutReg.matches(EXECUTION);
    }
 }

得到的EXECUTION表达式可以用来检测pointcut表达式是否写正确;
测试:

String pointcut = "execution ( default int[][] * rr( .. ) ) ";
//default访问修饰符,int[][]二维数组 ;
// * 不限定类名; rr 方法名; .. 任意类型的参数列表;符合定义的规则,预期结果为 true
boolean correct = PointcutUtils.checkPointcut(pointcut );
System.out.println(correct);//结果:true


2.拆分pointcut表达式

拆分流程:

代码:

//拆分pointcut
public static Pointcut parsePointcut(String pointcut){
    if(!checkPointcut(pointcut))throw new IllegalArgumentException("execution grammar format error.");
    String exeReg = getBracketStr(pointcut);//获取execution();中括号包裹的部分;
    String paramList = getBracketStr(exeReg).replaceAll(" ","");
    int start = exeReg.indexOf("(");
    exeReg = exeReg.substring(0,start);
    String[] regs = exeReg.split("\\s+");
    String accessModifier = regs[0];
    String returnType = regs[1];
    String classPath = regs[2];
    String methodName = regs[3];
    return new Pointcut(accessModifier,returnType,classPath,methodName,paramList);
}
static String getBracketStr(String str){
    int start = str.indexOf("(");
    int end = str.lastIndexOf(")");
    return str.substring(start+1,end).trim();
}

3.过滤

  分别匹配class和method;在拿到pointcut的时候,如何匹配class和method呢?这个时候要对类路径的正则表达式做一下处理;比如:pointcut的classpath部分:

"*weqq*.dgdfgfg.df*"

*weqq*:我们希望能匹配到包名含有 weqq的包;直接使用这个作为正则表达式去匹配类路径肯定是不行的;

 * ====》 重复匹配0次或多次前一个字符或者表达式;
 
如何能达到要求呢?只需要做一下简单处理就好了:*weqq* ====》 \\w*weqq\\w*;将 * 替换成 \\w* 就可以匹配字符了

还有需要注意的是 ".",在正则表达式中表示匹配任意字符;

而我们希望它只是包的分割符,它只表示" . ",而不需要有任何其他的含义,因此需要将 "."转换成普通字符    :  .  ===>   \\.

匹配class的类名

public static boolean matchClass(Pointcut pointcut,Class cla){
       if(pointcut.getClassPath().equals("*"))return true;
       return cla.getTypeName().matches(pointcut.getClassPath().replaceAll("\\*","\\\\w*").replaceAll("\\.","\\\\."));
   }

匹配方法:访问修饰符,返回值,方法名,参数列表

public static boolean matchMethod(Pointcut pointcut, Method method){
    return matchModifier(pointcut.getAccessModifier(),method) &&
           matchReturnType(pointcut.getReturnType(),method) &&
           matchMethodName(pointcut.getMethodName(),method) &&
           matchParamList(pointcut.getParamList(),method);
  }
 static boolean matchModifier(String modifier,Method method){
    int modifiers = method.getModifiers();
     switch (modifier){
         case "default"  :return method.isDefault();
         case "public"   :return Modifier.isPublic(modifiers);
         case "private"  :return Modifier.isPrivate(modifiers);
         case "protected":return Modifier.isProtected(modifiers);
         case "*"        :return true;
         default:
             return false;
     }
}
 static boolean matchReturnType(String returnType,Method method){
     if(returnType.equals("*"))return true;
    return returnType.equals(method.getReturnType().getSimpleName());
}
 static boolean matchMethodName(String name,Method method){
     if(name.equals("*"))return true;
     return method.getName().matches(name.replaceAll("\\*","\\\\w*"));
}
 static boolean matchParamList(String paramList,Method method){
      if(paramList.equals(".."))return true;
     Class<?>[] parameterTypes = method.getParameterTypes();
    if((paramList.equals("")) ){
        if( parameterTypes.length == 0 )return true;
        else return false;
    }
    StringBuilder methodParamList = new StringBuilder();
    for (Class<?> parameterType : parameterTypes) {
        methodParamList.append(","+parameterType.getSimpleName());
    }
     String methodParam =  methodParamList.toString().substring(1);
     return methodParam.equals(paramList);
 }

同时匹配上类和方法,就可以对方法增强了;其实可以看到在实现pointcut表达式只用到了少量的正则表达式的知识;execution整体拼凑起来有点多,但是分开来看每部分还是简单的;只需要了解简单的正则和反射,就可以自定义一个pointcut过滤器了。有了这个之后,就可以自己定义实现选择性AOP了;


相关推荐

PHP实现部分字符隐藏

沙雕mars · 1325浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 908浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 970浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 962浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1086浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
我是来自差了一点掉完头发的程序猿,小军,希望在这里可以向各位大佬们学习。
分类专栏
小鸟云服务器
扫码进入手机网页