SpEL + AOP实现注解的动态赋值
一、自定义注解
先聊聊这个需求,我需要根据用户的权限对数据进行一些处理,但是痛点在哪里呢?用户的权限是在请求的时候知道的,我怎么把用户的权限传递给处理规则呢?想了以下几种方案:
- Mybatis 拦截器:如果你的权限参数可以渗透到 Dao 层,那么这是最好的处理方式,直接在 Dao 层数据返回的时候,根据权限做数据处理。
- Dubbo 过滤器:如果 Dao 层没办法实现的话,只好考虑在 service 层做数据处理了。
- ResponseBodyAdvice :要是 service 层也没办法做到,只能在访问层数据返回的时候,根据权限做数据处理。(以下介绍的正是这种方式)
那么现在有个难点就是:我怎么把 request 的权限参数传递到 response 中呢?当然可以在 Spring 拦截器中处理,但是我不想把这段代码侵入到完整的鉴权逻辑中。突然想到,我能不能像 spring-data-redis 中 @Cacheable 一样,利用注解和 SpEL 表达式动态的传递权限参数呢?然后在 ResponseBodyAdvice 读取这个注解的权限参数,进而对数据进行处理。
首先,我们需要有个自定义注解,它有两个参数:key 表示 SpEL 表达式;userType 表示权限参数。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseSensitiveOverride {
/**
* SPEL 表达式
*
* @return
*/
String key() default "";
/**
* 1:主账号、2:子账号
*/
int userType() default 1;
}
然后,把这个注解放在路由地址上,key 写入获取权限参数的 SpEL 表达式:
@ResponseSensitiveOverride(key = "#driverPageParam.getUserType()")
@RequestMapping(value = "/queryPage", method = RequestMethod.POST)
public ResponseData<PageVo<AdminDriverVo>> queryPage(@RequestBody AdminDriverPageParam driverPageParam) {
return driverService.queryPageAdmin(driverPageParam);
}
二、SpEl + AOP 注解赋值
现在 SpEL 表达式是有了,怎么把 SpEL 表达式的结果赋值给注解的 userType 参数呢?这就需要用 Spring AOP 、Java 反射 和 动态代理 的知识。
@Aspect
@Component
public class SensitiveAspect {
private SpelExpressionParser spelParser = new SpelExpressionParser();
/**
* 返回通知
*/
@AfterReturning("@annotation(com.yungu.swift.base.model.annotation.ResponseSensitiveOverride) && @annotation(sensitiveOverride)")
public void doAfter(JoinPoint joinPoint, ResponseSensitiveOverride sensitiveOverride) throws Exception {
//获取方法的参数名和参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
List<String> paramNameList = Arrays.asList(methodSignature.getParameterNames());
List<Object> paramList = Arrays.asList(joinPoint.getArgs());
//将方法的参数名和参数值一一对应的放入上下文中
EvaluationContext ctx = new StandardEvaluationContext();
for (int i = 0; i < paramNameList.size(); i++) {
ctx.setVariable(paramNameList.get(i), paramList.get(i));
}
// 解析SpEL表达式获取结果
String value = spelParser.parseExpression(sensitiveOverride.key()).getValue(ctx).toString();
//获取 sensitiveOverride 这个代理实例所持有的 InvocationHandler
InvocationHandler invocationHandler = Proxy.getInvocationHandler(sensitiveOverride);
// 获取 invocationHandler 的 memberValues 字段
Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
// 因为这个字段是 private final 修饰,所以要打开权限
hField.setAccessible(true);
// 获取 memberValues
Map memberValues = (Map) hField.get(invocationHandler);
// 修改 value 属性值
memberValues.put("userType", Integer.parseInt(value));
}
}
通过这种方式,我们就实现了为注解动态赋值。
三、ResponseBodyAdvice 处理数据
现在要做的事情就是在 ResponseBody 数据返回前,对数据进行拦截,然后读取注解上的权限参数,从而对数据进行处理,这里使用的是 SpringMVC 的 ResponseBodyAdvice 来实现:
@Slf4j
@RestControllerAdvice
@Order(-1)
public class ResponseBodyAdvice implements org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice {
private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return SysUserDto.USER_TYPE_PRIMARY;
}
};
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
if (returnType.hasMethodAnnotation(ResponseSensitiveOverride.class)) {
ResponseSensitiveOverride sensitiveOverride = returnType.getMethodAnnotation(ResponseSensitiveOverride.class);
threadLocal.set(sensitiveOverride.userType());
return true;
}
return false;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body != null && SysUserDto.USER_TYPE_SUB.equals(threadLocal.get())) {
// 业务处理
}
return body;
}
}
题外话,其实我最后还是摈弃了这个方案,选择了 Dubbo 过滤器的处理方式,为什么呢?因为在做数据导出的时候,这种方式没办法对二进制流进行处理呀!汗~ 但是该方案毕竟耗费了我一个下午的心血,还是在此记录一下,可能有它更好的适用场景!
SpEL + AOP实现注解的动态赋值的更多相关文章
- AOP获取方法注解实现动态切换数据源
AOP获取方法注解实现动态切换数据源(以下方式尚未经过测试,仅提供思路) ------ 自定义一个用于切换数据源的注解: package com.xxx.annotation; import org. ...
- Java注解如何对属性动态赋值
学而不思则罔,思而不学则殆 前言 大家都用过Spring的@Value("xxx")注解,如果没有debug过源码的同学对这个操作还是一知半解,工作一年了学了反射学了注解,还是不会 ...
- 利用Spring AOP自定义注解解决日志和签名校验
转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...
- Java注解--实现动态数据源切换
当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了Abstract ...
- 总结切面编程AOP的注解式开发和XML式开发
有段日子没有总结东西了,因为最近确实有点忙,一直在忙于hadoop集群的搭建,磕磕碰碰现在勉强算是能呼吸了,因为这都是在自己的PC上,资源确实有点紧张(搭建过程后期奉上),今天难得大家都有空(哈哈哈~ ...
- Spring 代理对象,cglib,jdk的问题思考,AOP 配置注解拦截 的一些问题.为什么不要注解在接口,以及抽象方法.
可以被继承 首先注解在类上是可以被继承的 在注解上用@Inherited /** * Created by laizhenwei on 17:49 2017-10-14 */ @Target({Ele ...
- 二)Spring AOP编程思想与动态代理
一.aop编程思想 1.面向切面,就是能够不动源码的情况下,从横切面切入新的代码功能. 2.实现原理是动态代理 动态代理的步骤 a.写生产厂家,实现接口,代理只能代理接口 b.动态代理类实现Invoc ...
- spring AOP自定义注解方式实现日志管理
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...
- spring AOP自定义注解 实现日志管理
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...
随机推荐
- Service Cloud 零基础(一)Case 浅谈
本片参考:https://resources.docs.salesforce.com/222/latest/en-us/sfdc/pdf/salesforce_case_implementation_ ...
- 抛弃Eclipse,投入IDEA 的独孤求败江湖
"工欲善其事,必先利其器" 出处:孔子<论语> 两年了,这是我的 IDEA 实用技巧总结,从前我是一个 Eclipse 忠实用户,直到某天我用上了 IntelliJ I ...
- [Luogu3065][USACO12DEC]第一!First!
题目描述 Bessie has been playing with strings again. She found that by changing the order of the alphabe ...
- [BZOJ1694/1742/3074]The Cow Run 三倍经验
Description John养了一只叫Joseph的奶牛.一次她去放牛,来到一个非常长的一片地,上面有N块地方长了茂盛的草.我们可 以认为草地是一个数轴上的一些点.Joseph看到这些草非常兴奋, ...
- 详解立即执行函数(function(){}()),(function(){})()
要知道这几种写法之间的区别,我们要先聊些题外话——js中函数的两种命名方式,即表达式和声明式. 函数的声明式写法为:function foo(){/*...*/},这种写法会导致函数提升,所有func ...
- opencv::点多边形测试
点多边形测试 测试一个点是否在给定的多边形内部,边缘或者外部 double pointPolygonTest( InputArray contour, // 输入的轮廓 Point2f pt, // ...
- go-go协程
package main import ( "fmt" "runtime" "time" ) func test() { defer fmt ...
- vue3.0 + ueditor
公司有个需求,需要做个发送邮件的模版(富文本对于模版的扩展性更好吧) 关于富文本,也找了一些好看且支持vue的,但是功能都没有百度全面 反正这个系统也是自己人用,颜值无所谓了 关于vue2.0+ued ...
- Js极客之路 - 优化操作(性能优化)
1.因为每次For循环都会计算一次arr.length,所以有必要存储数组长度以减少计算.针对这篇文章(http://www.crimx.com/2015/04/21/should-array-len ...
- django-表单之新增字段和设置css属性(四)
要注意是模板元素 和 表单元素的对应.