广告
返回顶部
首页 > 资讯 > 后端开发 > Python >使用Springboot自定义注解,支持SPEL表达式
  • 916
分享到

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

2024-04-02 19:04:59 916人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录SpringBoot自定义注解,支持SPEL表达式1.自定义注解2.使用aop拦截方法,解析注解参数自定义注解结合切面和spel表达式自定义一个注解自定义一个service类,在

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)
@Component
public 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表达式的语法)

实现步骤:

1、自定义一个注解Log

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

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

难点:

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

自定义一个注解

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


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

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

写一个自定义切面

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


@Component
@Aspect
public 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>

测试

增加内容

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 使用Springboot自定义注解,支持SPEL表达式

本文链接: https://www.lsjlt.com/news/139490.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • 使用Springboot自定义注解,支持SPEL表达式
    目录Springboot自定义注解,支持SPEL表达式1.自定义注解2.使用AOP拦截方法,解析注解参数自定义注解结合切面和spel表达式自定义一个注解自定义一个service类,在...
    99+
    2022-11-13
  • 如何使用Springboot自定义注解并支持SPEL表达式
    这篇文章主要介绍了如何使用Springboot自定义注解并支持SPEL表达式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Springboot自定义注解,支持SPEL表达式举...
    99+
    2023-06-29
  • 支持SpEL表达式的自定义日志注解@SysLog介绍
    目录序言预期思路过程结果序言 之前封装过一个日志注解,打印方法执行信息,功能较为单一不够灵活,近来兴趣来了,想重构下,使其支持表达式语法,以应对灵活的日志打印需求。 该注解是方法层面...
    99+
    2022-11-13
  • 在@Value注解内使用SPEL自定义函数方式
    目录@Value注解内使用SPEL自定义函数自定义注解支持SpEL表达式1.定义日志注解2.定义spel解析工具类3.定义切面类4.方法上使用日志注解@Value注解内使用SPEL自...
    99+
    2022-11-13
  • SpringAOP如何在注解上使用SPEL表达式注入对象
    目录在注解上使用SPEL表达式注入对象场景描述具体案例补充Spring属性注入方式之SPEL表达式在注解上使用SPEL表达式注入对象 场景描述 在平时开发中,我们经常通过定义一些注解...
    99+
    2022-11-13
  • 如何在SpringBoot环境下使得自定义的注解能够使用${xxx}表达式
    如何在SpringBoot环境下使得自定义的注解能够使用${xxx}表达式,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。相关依...
    99+
    2022-10-19
  • springboot使用自定义注解实现aop切面日志
    平时我们在开发过程中,代码出现bug时为了更好的在服务器日志中寻找问题根源,会在接口的首尾打印日志,看下参数和返回值是否有问题。但是手动的logger.info() 去编写时工作量较...
    99+
    2022-11-13
  • springboot中如何使用自定义注解实现加解密及脱敏方式
    这篇文章主要介绍springboot中如何使用自定义注解实现加解密及脱敏方式,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!自定义注解实现加解密及脱敏定义自定义注解@Documented@Target({Element...
    99+
    2023-06-22
  • SpringBoot如何使用自定义注解实现接口限流
    目录使用自定义注解实现接口限流1、自定义限流注解2、限流类型枚举类3、限流 Lua 脚本4、限流切面处理类 5、使用与测试SpringBoot工程中限流方式1、google...
    99+
    2022-11-13
  • 使用自定义注解实现redisson分布式锁
    目录自定义注解实现redisson分布式锁自定义注解aop解析注解service中使用注解加锁使用redisson分布式锁应用应用场景Redisson管理类分布式锁测试类自定义注解实...
    99+
    2022-11-13
  • SpringBoot使用自定义注解实现权限拦截的示例
    本文介绍了SpringBoot使用自定义注解实现权限拦截的示例,分享给大家,具体如下:HandlerInterceptor(处理器拦截器)常见使用场景 日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算PV(page ...
    99+
    2023-05-31
    springboot 权限拦截
  • springboot中shiro使用自定义注解屏蔽接口鉴权实现
    目录传统做法使用自定义注解屏蔽接口鉴权拓展内容:关于spring中的派生注解传统做法 spring boot整合shiro后,如果某些接口需要屏蔽鉴权的话(比如登录)接口,我们一般会...
    99+
    2022-11-13
  • 使用Spring自定义实现IOC和依赖注入(注解方式)
    目录大致思路:注解实现方式:xml实现方式:1. 引入相关jar2. 定义注解类ExtService是注解类的, ExtResource是注解属性的3.定义一个借口4. 接口和使用注...
    99+
    2022-11-12
  • SpringBoot使用自定义注解实现数据脱敏过程详细解析
    目录前言一、引入hutool工具类二、定义常用需要脱敏的数据类型的枚举三、定义脱敏方式枚举四、自定义脱敏的注解五、自定义Jackson的序列化方式六、使用七、脱敏效果前言 对于某些接...
    99+
    2023-02-15
    SpringBoot数据脱敏 SpringBoot自定义注解实现数据脱敏
  • 使用自定义注解实现加解密及脱敏方式
    目录自定义注解实现加解密及脱敏定义自定义注解构造AOP逻辑测试脱敏逻辑自定义一种字符串的加密与解密自定义注解实现加解密及脱敏 定义自定义注解 @Documented @Targe...
    99+
    2022-11-12
  • 如何使用自定义注解实现redisson分布式锁
    这篇文章主要讲解了“如何使用自定义注解实现redisson分布式锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用自定义注解实现redisson分布式锁”吧!自定义注解实现rediss...
    99+
    2023-06-29
  • 使用自定义参数解析器同一个参数支持多种Content-Type
    目录一堆废话探究Springmvc参数解析器工作流程不想看废话的可以直接进结果补充一堆废话 事出有因, 原先上线的接口现在被要求用Java重写,按照原暴露出去的文档然后毫无疑问的,按...
    99+
    2022-11-12
  • SpringBoot使用自定义注解+AOP+Redis实现接口限流的实例代码
    目录为什么要限流限流背景实现限流1、引入依赖2、自定义限流注解3、限流切面4、写一个简单的接口进行测试5、全局异常拦截6、接口测试为什么要限流 系统在设计的时候,我们会有一个系统的预...
    99+
    2022-11-13
  • 使用自定义注解+springAop实现参数非空校验方式
    目录自定义注解+springAop参数非空校验新建注解类@interface ParamsVerify利用springAop来实现切面新建一个切面类使用注解统一校验参数非空1. 待校...
    99+
    2022-11-12
  • 使用自定义注解进行restful请求参数的校验方式
    目录自定义注解进行restful请求参数的校验1、首先我们使用@interface定义一个注解2、实现注解实现类(和@interface定义的注解在同一个包下)3、在需要校验的对象的...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作