spring的pointcut正则表达式的实现

目录
  • 1.Pointcut表达式各部分的约束规则
  • 2.拆分pointcut表达式
  • 3.过滤

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了;

到此这篇关于spring的pointcut正则表达式的实现的文章就介绍到这了,更多相关spring pointcut正则表达式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring aop Pointcut execution规则介绍

    目录 spring aop Pointcut execution规则 切面注解@Pointcut("execution()")写法 spring aop Pointcut execution规则 任意公共方法的执行: execution(public * *(..)) ##public可以省略, 第一个* 代表方法的任意返回值 第二个参数代表任意包+类+方法 (..)任意参数 任何一个以"set"开始的方法的执行: execution(* set*(..)) Use

  • springboot aop里的@Pointcut()的配置方式

    目录 springboot aop里的@Pointcut()的配置 springboot aop @Pointcut的用法 springboot aop里的@Pointcut()的配置 @Pointcut("execution(public * com.wangzhou.newboot.exception.TestExceptionController.test(String,String))") com.wangzhou.newboot.exception是TestException

  • spring aop pointcut 添加多个execution方式

    目录 spring aop pointcut 添加多个execution spring aop:pointcut--expression--多个execution连接 spring aop pointcut 添加多个execution spring aop添加多个包,用||或者or隔开 <!-- 只对业务逻辑层实施事务 --> <aop:config expose-proxy="true"> <aop:pointcut expression="e

  • 详解Spring 框架中切入点 pointcut 表达式的常用写法

    自从使用 AspectJ 风格切面配置,使得 spring 的切面配置大大简化,但是 AspectJ 是另外一个开源项目,其规则表达式的语法也稍稍有些怪异. 下面给出一些常见示例的写法,例如,下面是一个对 Service 包上所有方法的切面配置: <aop:config> <aop:pointcut id="serviceOperation" expression="execution(* *..service*..*(..))"/> <

  • Java Spring AOP之PointCut案例详解

    目录 一.PointCut接口 二.ClassFilter接口 三.MethodMatcher接口 总结 一.PointCut接口 /* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with

  • SpringBoot AOP @Pointcut切入点表达式排除某些类方式

    目录 SpringBoot AOP @Pointcut切入点表达式排除某些类 场景 使用以下方法满足了开发需求 AOP排除某些类型不拦截 SpringBoot AOP @Pointcut切入点表达式排除某些类 场景 希望给service包下的所有public方法添加开始和结束的info log,但是需要排除和数据库相关的service 其他博文都推荐了 @Pointcut("execution(* com.demo.service.*.*(..)) && !execution(*

  • spring的pointcut正则表达式的实现

    目录 1.Pointcut表达式各部分的约束规则 2.拆分pointcut表达式 3.过滤 1.Pointcut表达式各部分的约束规则 在spring中配置切面或者数据库的事务会要求:对具体方法或者是一类特征相同的方法添加日志,事务,或者其他对原方法的增强.这时候就会用到pointcut表达式对方法进行过滤,筛选出符合要求的方法: 既然会涉及到筛选具体的方法,那pointcut一定要匹配出完整的方法路径:全限定类名+方法名:在同一个类中,方法可能被重写而区分重写的方法就是:参数列表:因此poin

  • Spring MVC请求参数接收的全面总结教程

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentResolver,此接口有众多子类,分别处理不同(注解类型)的参数,下面只列举几个子类: RequestParamMethodArgumentResolver:解析处理使用了@RequestParam注解的参数.MultipartFile类型参数和Simple类型(如long.int)参数. Reques

  • 基于Ant路径匹配规则AntPathMatcher的注意事项

    目录 AntPathMatcher前言 基本规则 注意事项 测试用例 spring url匹配工具类----AntPathMatcher 具体使用场景 请求body的二次写入 AntPathMatcher前言 (1)SpringMVC的路径匹配规则是依照Ant的来的,实际上不只是SpringMVC,整个Spring框架的路径解析都是按照Ant的风格来的: (2)AntPathMatcher不仅可以匹配Spring的@RequestMapping路径,也可以用来匹配各种字符串,包括文件路径等. 基

  • 实例讲解Java的Spring框架中的AOP实现

    简介 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切多个类型和对象的事务管理. (这些关注点术语通常称作 横切(crosscutting) 关注点.) Spring的一个关键的组件就是 AOP框架. 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善

  • 详解spring注解式参数校验

    一般入参我们都会转为vo对象.那么直接在对象的属性上注解即可. 其实spring用的是hibernate的validator. 步骤 1.配置spring.xml <mvc:annotation-driven /> 2.配置自己的validate类. <bean id="validateArgsAOP" class="com.my.validate.aop.ValidateArgsAOP"/> <aop:config> <a

  • Spring Boot使用AOP实现REST接口简易灵活的安全认证的方法

    本文将通过AOP的方式实现一个相对更加简易灵活的API安全认证服务. 我们先看实现,然后介绍和分析AOP基本原理和常用术语. 一.Authorized实现 1.定义注解 package com.power.demo.common; import java.lang.annotation.*; /* * 安全认证 * */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Doc

随机推荐