首页 > 编程语言 >利用JAVA的AOP运行时注解实现请求VO的复杂组装

利用JAVA的AOP运行时注解实现请求VO的复杂组装

时间:2023-06-29 15:32:00浏览次数:34  
标签:JAVA AuthField value VO strValue AOP 注解 else class

背景

上一篇介绍了关于JAVA的AOP编译时注解的开发经验,因此,这里则接着来讲讲AOP运行时注解,摘取的是利用运行时注解实现微服务对请求对象(VO)中的指定字段特殊处理的统一封装,典型场景就是SpringCloud Gateway对用户请求经过鉴权、过滤、路由等处理之后,将必要信息添加到消息头中,转发到相应的微服务。


设计要求

仅就本文讨论的运行时注解而言,业务开发者在编写服务接口时,主要考虑的几点是:

  • 从业务角度出发,定义完整的VO,省去各个接口实现中的VO拼接处理;
  • 重要数据(如归属关系、角色权限等)防假冒。


代码

  1. 注解定义

1)AuthInjected注解

/**
 * Auth
 * 运行时注解,用于Controller成员方法的参数
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthInjected {
}

2)AuthField注解

/**
 * AuthField
 * 运行时注解,用于VO类的成员属性
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthField {
    /**
     * header
     * Auth头的键
     */
    String header();
    /**
     * rewrite
     * 如果原始请求中已赋值,是否强制重写为Auth头的值。
     * 用于阻止用户请求中的假冒数据。
     */
    boolean rewrite() default false;
}
  1. 运行时注解的处理实现
@Component
@Aspect
public class AuthAspect {

    @Autowired(required = false)
    private HttpServletRequest request;

    @Pointcut(value = "execution(* *.*(.., @AuthInjected (*), ..))")
    private void annoParam() {
    }

    @Around(value = "annoParam()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        // 获取实际方法
        Signature sign = jp.getSignature();
        Method meth = ((MethodSignature) sign).getMethod();
        Method realMethod = jp.getTarget().getClass().getDeclaredMethod(sign.getName(), meth.getParameterTypes());
        // 检查参数注解
        Object[] args = jp.getArgs();
        int i = 0;
        Annotation[][] paramAnnotations = realMethod.getParameterAnnotations();
        for (Annotation[] annotations : paramAnnotations) {
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().getSimpleName().equals("AuthInjected")) {
                    Object arg = args[i];
                    // 检查参数对象的属性
                    for (Field field: arg.getClass().getDeclaredFields()) {
                        AuthField authField = field.getAnnotation(AuthField.class);
                        if (authField == null) {
                            continue;
                        }
                        // 获取消息头键值
                        String strValue = request.getHeader(authField.header());
                        if (strValue == null) {
                            continue;
                        }
                        PropertyDescriptor pd = new PropertyDescriptor(field.getName(), arg.getClass());
                        Method writeMethod = pd.getWriteMethod();
                        // 检查是否要求强制重写
                        if (!authField.rewrite()) {
                            Method readMethod = pd.getReadMethod();
                            Object value = readMethod.invoke(arg);
                            if (value != null) {
                                continue;
                            }
                        }
                        Object value;
                        Class<?> cls = field.getType();
                        if (cls.isAssignableFrom(String.class)) {
                            value = strValue;
                        } else if (cls.isAssignableFrom(Long.class)) {
                            value = Long.valueOf(strValue);
                        } else if (cls.isAssignableFrom(Integer.class)) {
                            value = Integer.valueOf(strValue);
                        } else if (cls.isAssignableFrom(Double.class)) {
                            value = Double.valueOf(strValue);
                        } else if (cls.isAssignableFrom(Float.class)) {
                            value = Float.valueOf(strValue);
                        } else if (cls.isAssignableFrom(Boolean.class)) {
                            value = Boolean.valueOf(strValue);
                        } else {
                            value = null;
                        }
                        writeMethod.invoke(arg, value);
                    }
                    break;
                }
            }
            i = i + 1;
        }
        return jp.proceed();
    }

}

wrtieMethod.invoke之前的值类型转换,不是今天要讨论的重点,所以只是简单粗暴地写了一堆if-else。实际项目中,可以采用策略模式+工厂模式进行优化。


  1. VO定义(以UserVo为例)
@Data
public class UserVo {
    @AuthField(header = "x-auth-userid", rewrite = true)
    private Long id;
    @AuthField(header = "x-auth-role", rewrite = true)
    private Long role;
    private String name;
}

为了阻止用户请求中的假冒数据,Gateway应该清除用户请求中的所有x-auth-*头,通过rewrite=true阻止URL参数或body中的假冒数据。


  1. 接口实现(示例)
    @GetMapping("/getUserName")
    public String getUserName(@AuthInjected UserVo userVo){
        return userVo.getName();
    }

结束语

要用好运行时注解,关键点的是搞清楚切入点(Pointcut)和通知(Advice)。切入点表达式还有很多类型,通常根据注解的目标类型来决定,具体表达式的写法则根据注解的使用场景来编写。选择什么通知,则是根据要在什么时间点、以及要做什么来决定的。一般来说,Around较为常见一些。

最后,也真心欢迎交流,一起提高。

标签:JAVA,AuthField,value,VO,strValue,AOP,注解,else,class
From: https://blog.51cto.com/u_16166196/6583165

相关文章

  • 如何获得 java项目下面所有的文件名
    ​ 要获取Java项目中所有的文件名,可以通过以下步骤实现:使用递归遍历获取项目中所有的文件://获得所有的文件名publicclassgetfilename{//这是一个main方法,是程序的入口:publicstaticvoidmain(String[]args){List<String>fileNames=newA......
  • 【后端面经-Java】公平锁和加锁流程
    目录1.公平锁和非公平锁1.1基本概念1.2ReentrantLock的公平锁和非公平锁2.加锁流程2.1ReentrantLock和AQS的关系2.2公平锁-加锁流程2.3非公平锁-加锁流程2.4加锁流程和性能的关系3.面试问题模拟参考文献1.公平锁和非公平锁1.1基本概念公平锁:线程按照到来的先后......
  • JavaScript 格式化时间
    functionformatDate(date){/***格式化日期*@param{Date|String}date日期或日期字符串*/function_isString(val){returnObject.prototype.toString.call(val)==='[objectString]';}/***精确判断数据是否是Date类型*@param......
  • 一条从Java基础到Java开发各个方向的学习路线(书籍)
    一条从Java基础到Java开发各个方向的学习路线,按照难度递增的顺序排列:Java基础:《Java核心技术卷一》-CayS.Horstmann和GaryCornell《HeadFirstJava》-KathySierra和BertBatesJavaWeb开发:《HeadFirstServletsandJSP》-BryanBasham等《JavaWeb开发......
  • Java 中 BigDecimal 类型的变量的使用
    BigDecimal类的作用:Java的BigDecimal类用于进行高精度的十进制运算,避免了使用浮点数造成的精度丢失问题。 BigDecimal提供了许多方法来执行基本的数学运算,如加减乘除、比较、区域等。 BigDecimal类的常见用法示例:1、创建BigDecimal对象:BigDecimalnumber=......
  • Java 17 新特性
    如题:基于垃圾回收器的内存分配:Java17引入了垃圾回收器接口,允许开发人员实现自定义的垃圾回收器。这样可以提供更大的灵活性和性能优化的机会。  示例代码:1publicclassMyGarbageCollectorimplementsGarbageCollector{2//实现自定义的垃圾回收逻辑......
  • Java or Python?测试开发工程师如何选择合适的编程语言?
    很多测试开发工程师尤其是刚入行的同学对编程语言和技术栈选择问题特别关注,毕竟掌握一门编程语言要花不少时间成本,也直接关系到未来的面试和就业(不同企业/项目对技术栈要求也不一样),根据自身情况做一个相对正确的选择确实要比盲目投入更明智也更高效。目前最常见的情况是纠结选择Jav......
  • docker-compose 部署java微服务项目
    1、准备条件:安装docker,安装docker-compose,docker安装可自行百度,docker-compose安装由于太慢,我这里提供两个版本:win和linux版本的百度网盘版,大家可根据需要自行下载:链接:https://pan.baidu.com/s/10W81TX6cWQqyi92xyeuZQQ提取码:2evg这里一linux系统为例:下载docker-compose-linux-......
  • JavaScript 链表的增删改查
       //节点对象classNode{constructor(data){this.data=data;//存储节点数据this.next=null;//存储下一个节点的引用,默认为null}}//链表对象classLinkedList{constructor(){this.head=null;//链表头节点,默认为null}......
  • java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check t
    问题报错代码org.apache.ibatis.exceptions.PersistenceException:###Errorqueryingdatabase.Cause:java.sql.SQLSyntaxErrorException:YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntax......