我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何使用Springboot自定义注解并支持SPEL表达式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何使用Springboot自定义注解并支持SPEL表达式

这篇文章主要介绍了如何使用Springboot自定义注解并支持SPEL表达式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

Springboot自定义注解,支持SPEL表达式

举例,自定义redis模糊删除注解

1.自定义注解

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface CacheEvictFuzzy {        String[] key() default ""; }

2.使用AOP拦截方法,解析注解参数

import org.apache.commons.lang3.StringUtils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.LocalVariableTableParameterNameDiscoverer;import org.springframework.core.annotation.Order;import org.springframework.expression.ExpressionParser;import org.springframework.expression.spel.standard.SpelExpressionParser;import org.springframework.expression.spel.support.StandardEvaluationContext;import org.springframework.stereotype.Component; import java.lang.reflect.Method;import java.util.Set;@Aspect@Order(1)@Componentpublic class CacheCleanFuzzyAspect {    Logger logger = LoggerFactory.getLogger(this.getClass());     @Autowired    private RedisUtil redis;     //指定要执行AOP的方法    @Pointcut(value = "@annotation(cacheEvictFuzzy)")    public void pointCut(CacheEvictFuzzy cacheEvictFuzzy){}      // 设置切面为加有 @RedisCacheable注解的方法    @Around("@annotation(cacheEvictFuzzy)")    public Object around(ProceedingJoinPoint proceedingJoinPoint, CacheEvictFuzzy cacheEvictFuzzy){        return doRedis(proceedingJoinPoint, cacheEvictFuzzy);    }    @AfterThrowing(pointcut = "@annotation(cacheEvictFuzzy)", throwing = "error")    public void afterThrowing (Throwable  error, CacheEvictFuzzy cacheEvictFuzzy){        logger.error(error.getMessage());    }         private Object doRedis (ProceedingJoinPoint proceedingJoinPoint, CacheEvictFuzzy cacheEvictFuzzy){        Object result = null;        //得到被切面修饰的方法的参数列表        Object[] args = proceedingJoinPoint.getArgs();        // 得到被代理的方法        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();        String[] keys = cacheEvictFuzzy.key();        Set<String> keySet = null;        String realkey = "";        for (int i = 0; i < keys.length; i++) {            if (StringUtils.isBlank(keys[i])){                continue;            }            realkey = parseKey(keys[i], method, args);            keySet = redis.keys("*"+realkey+"*");            if (null != keySet && keySet.size()>0){                redis.delKeys(keySet);                logger.debug("拦截到方法:" + proceedingJoinPoint.getSignature().getName() + "方法");                logger.debug("删除的数据key为:"+keySet.toString());            }        }        try {            result = proceedingJoinPoint.proceed();        } catch (Throwable throwable) {            throwable.printStackTrace();        }finally {            return result;        }    }        private String parseKey(String key, Method method, Object [] args){         if(StringUtils.isEmpty(key)) return null;         //获取被拦截方法参数名列表(使用Spring支持类库)        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();        String[] paraNameArr = u.getParameterNames(method);         //使用SPEL进行key的解析        ExpressionParser parser = new SpelExpressionParser();        //SPEL上下文        StandardEvaluationContext context = new StandardEvaluationContext();        //把方法参数放入SPEL上下文中        for(int i=0;i<paraNameArr.length;i++){            context.setVariable(paraNameArr[i], args[i]);        }        return parser.parseExpression(key).getValue(context,String.class);    }}

完事啦!

大家可以注意到关键方法就是parseKey

        private String parseKey(String key, Method method, Object [] args){         if(StringUtils.isEmpty(key)) return null;         //获取被拦截方法参数名列表(使用Spring支持类库)        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();        String[] paraNameArr = u.getParameterNames(method);         //使用SPEL进行key的解析        ExpressionParser parser = new SpelExpressionParser();        //SPEL上下文        StandardEvaluationContext context = new StandardEvaluationContext();        //把方法参数放入SPEL上下文中        for(int i=0;i<paraNameArr.length;i++){            context.setVariable(paraNameArr[i], args[i]);        }        return parser.parseExpression(key).getValue(context,String.class);    }

自定义注解结合切面和spel表达式

在我们的实际开发中可能存在这么一种情况,当方法参数中的某些条件成立的时候,需要执行一些逻辑处理,比如输出日志。而这些代码可能都是差不多的,那么这个时候就可以结合自定义注解加上切面加上spel表达式进行处理。就比如在spring中我们可以使用@Cacheable(key="#xx")实现缓存,这个#xx就是一个spel表达式。

需求:我们需要将service层方法中方法的某个参数的值大于0.5的方法,输出方法执行日志。(需要了解一些spel表达式的语法)

实现步骤:

自定义一个注解Log

自定义一个切面,拦截所有方法上存在@Log注解修饰的方法

写一个service层方法,方法上标注@Log注解

难点:

在切面中需要拿到具体执行方法的方法名,可以使用spring提供的LocalVariableTableParameterNameDiscoverer来获取到

自定义一个注解

注意:注解中的spel的值是必须的,且spel表达式返回的结果应该是一个布尔值

@Target({ ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)public @interface Log {String spel();String desc() default "描述";}

自定义一个service类,在需要拦截的方法上加上@Log注解

如何使用Springboot自定义注解并支持SPEL表达式

写一个自定义切面

注意一下解析spel表达式中context的设值即可

@Component@Aspectpublic class LogAspect {ExpressionParser parser = new SpelExpressionParser();LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();@Around("@annotation(log)")public Object invoked(ProceedingJoinPoint pjp, Log log) throws Throwable {Object[] args = pjp.getArgs();Method method = ((MethodSignature) pjp.getSignature()).getMethod();String spel = log.spel();String[] params = discoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();for (int len = 0; len < params.length; len++) {context.setVariable(params[len], args[len]);}Expression expression = parser.parseExpression(spel);if (expression.getValue(context, Boolean.class)) {System.out.println(log.desc() + ",在" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "执行方法," + pjp.getTarget().getClass() + "." + method.getName()                         + "(" + convertArgs(args) + ")");}return pjp.proceed();}private String convertArgs(Object[] args) {StringBuilder builder = new StringBuilder();for (Object arg : args) {if (null == arg) {builder.append("null");} else {builder.append(arg.toString());}builder.append(',');}builder.setCharAt(builder.length() - 1, ' ');return builder.toString();}}

pom文件的依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

测试

如何使用Springboot自定义注解并支持SPEL表达式

增加内容

当我们想在自己写的spel表达式中调用spring bean 管理的方法时,如何写。spel表达式支持使用 @来引用bean,但是此时需要注入BeanFactory

如何使用Springboot自定义注解并支持SPEL表达式

感谢你能够认真阅读完这篇文章,希望小编分享的“如何使用Springboot自定义注解并支持SPEL表达式”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何使用Springboot自定义注解并支持SPEL表达式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用Springboot自定义注解并支持SPEL表达式

这篇文章主要介绍了如何使用Springboot自定义注解并支持SPEL表达式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Springboot自定义注解,支持SPEL表达式举
2023-06-29

springboot中如何使用自定义注解实现加解密及脱敏方式

这篇文章主要介绍springboot中如何使用自定义注解实现加解密及脱敏方式,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!自定义注解实现加解密及脱敏定义自定义注解@Documented@Target({Element
2023-06-22

如何使用自定义注解实现redisson分布式锁

这篇文章主要讲解了“如何使用自定义注解实现redisson分布式锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用自定义注解实现redisson分布式锁”吧!自定义注解实现rediss
2023-06-29

PHP如何使用回调执行正则表达式搜索,并使用多字节支持替换

PHP正则表达式提供强大的匹配和替换功能,可使用回调函数自定义匹配和替换行为。通过preg_replace_callback函数使用回调函数,开发者可细粒度控制匹配内容。此外,PHP支持多字节字符集,可处理非ASCII字符,通过u修饰符启用多字节支持,可与回调函数结合使用,实现复杂正则表达式搜索和替换操作。
PHP如何使用回调执行正则表达式搜索,并使用多字节支持替换
2024-04-02

Java如何使用回调执行正则表达式搜索,并使用多字节支持替换

Java使用回调执行正则表达式搜索,允许自定义匹配处理。通过Pattern.compile编译正则表达式,Matcher.find应用模式,Matcher.replaceAll执行回调进行自定义替换。多字节支持可处理多字节字符。回调优势包括自定义替换逻辑、多字节支持和代码可重用性。
Java如何使用回调执行正则表达式搜索,并使用多字节支持替换
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录