Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 522|回复: 0

为spring的controller做一个打印日志、验证参数、提供默认异常处理的切面

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-8-29 12:32:04 | 显示全部楼层 |阅读模式

    大纲:

    1. 需求
    2. 实现
    3. 使用

     

    一、需求

     使用spring的controller时候,有很多重复性操作,可以做一个业务轮子统一实现这些功能。

    1.打印日志:调用接口所属的类、方法名称、接口入参、出参、异常、接口调用时间等信息,出入参对象需要重写toString方法
    2.请求参数校验,无需使用@valid注解,支持分组校验
    3.提供系统业务异常处理,其他异常默认响应500,服务器异常。

    二、实现

    首先定义一个用于方法注解,用于controller的方法上

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RestRequestHelper {
        Class[] validGroups() default {}; //参数校验的分组
    }

    然后定义切面around这个自定义的注解

    代码中Result(响应结果)、BaseException(自定义业务异常)都是系统内自己定义的,根据业务需求设计即可。

    @Aspect
    @Component
    @Slf4j
    public class RequestHelperAspect {
    
        @Autowired
        private SmartValidator validator;
    
        @Around("@annotation(restRequestHelper)")
        public Object process(ProceedingJoinPoint pjp, RestRequestHelper restRequestHelper) {
            Class<?> targetClass = pjp.getTarget().getClass();
            String className = targetClass.getSimpleName();
            String methodName = pjp.getSignature().getName();
            Object[] args = pjp.getArgs();
            Object[] arguments = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args instanceof ServletRequest || args instanceof ServletResponse || args instanceof MultipartFile) {
                    //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                    //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
                    continue;
                }
                arguments = args;
            }
            log.info("processing request:{}.{} begin,params:{}", className, methodName, arguments);
            long startTime = System.currentTimeMillis();
            Object proceed = null;
            if ((proceed = validParams(arguments,restRequestHelper.validGroups())) == null) {
                try {
                    proceed = pjp.proceed();
                } catch (Throwable throwable) {
                    log.error("processing request error:", throwable);
                    //异常处理
                }
            }
            long endTime = System.currentTimeMillis();
            //响应参数toString
            String response = null;
            if (proceed instanceof Result) {
                Result result = (Result) proceed;
                response = result.toJson();
            } else if (proceed!=null){
                response = proceed.toString();
            }
            log.info("processing request:{}.{} end,params:{},response:{},use:{}ms", className, methodName, arguments, response, endTime - startTime);
            return proceed;
        }
    
        /**
         * 参数校验
         *
         * @param args
         * @param classes
         * @return
         */
        private Result validParams(Object[] args, Class[] classes) {
            Result errorResult = null;
            for (Object arg : args) {
                if(arg==null){
                    continue;
                }
                BindingResult bindingResult = new BeanPropertyBindingResult(arg, arg.getClass().getSimpleName());
                if(classes.length>0){
                    validator.validate(arg, bindingResult,classes);
                }else {
                    validator.validate(arg, bindingResult);
                }
                StringBuilder errorMsg = null;
                Map<String, String> errorMap = null;
                if (bindingResult.hasErrors()) {
                    errorMsg = new StringBuilder();
                    errorMsg.append(//异常信息);
                    final List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                    errorMap = new HashMap<>(fieldErrors.size());
                    for (FieldError fieldError : fieldErrors) {
                        errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
                    }
                    errorMsg.append(JSON.toJSON(errorMap));
                    errorResult = new Result(...异常);
                }
            }
            return errorResult;
        }
    }

     

     

    三、使用

    使用时候只要在controller中的方法上加上这个注解就可以了

        @PostMapping("/create")
        @RestRequestHelper( validGroups = {XxxForm.Create.class})//validGroups为需要校验的分组,如果不需要分组校验,不写这个参数即可
        public Result create(@RequestBody XxxForm form) {
            //业务处理
        }

    分组校验不会的参考我另一篇文章:https://www.cnblogs.com/liuboyuan/p/11093383.html

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-23 04:53 , Processed in 0.092008 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表