• spring切面编程 之 注解实战


    实现目标

    1. 判定重复请求

    场景: 用户手太快了,可能出现重复点击按钮
    	   或有耗时较长请求,用户等不及一直刷新
    
    • 1
    • 2

    2. 记录方法执行状态,是否出现异常

    场景:给运维用的,记录方法执行时间、执行状态等内容,提高运维效率
    
    • 1

    如何实现

    1. 分析

    判定重复请求,可以在处理controller之前,对一些重复的请求直接拦截,推荐使用拦截器(interceptor) 实现。
    记录方法执行状态,需要在很多场景使用,推荐使用Aspect实现。

    2. 实现

    (1) 判定重复请求【interceptor】

    下文是注解,是切面开始执行的定位坐标
    
    • 1
    /**
     * 自定义注解防止表单重复提交
     */
    @Inherited
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RepeatSubmit
    {
    	/**
    	 *  间隔时间(ms),小于此时间视为重复提交
    	 */
    	public int interval() default 5000;
    	
    	/**
    	 * 提示消息
    	 */
    	public String message() default "不允许重复提交";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    下文是拦截器(interceptor),如何实现注解对应的功能
    
    • 1
    /**
     * 防止重复提交拦截器
     */
    @Component
    public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
    {    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
        {
            if (handler instanceof HandlerMethod)
            {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                // 拿到注解对象 
                RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
                if (annotation != null)
                {
                    if (this.isRepeatSubmit(request)) // 判定请求是否重复的代码
                    {
                    	// 这里获取注解中的mes
                        AjaxResult ajaxResult = AjaxResult.error(annotation.message()); 
        				response.setContentType("application/json");
        				response.setCharacterEncoding("utf-8");
        				response.getWriter().print(ajaxResult);
                        return false;
                    }
                }
                return true;
            }
            else
            {
                return super.preHandle(request, response, handler);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    (2) 记录方法执行状态【Aspect】

    下文是注解,是切面开始执行的定位坐标
    
    • 1
    /**
     * 自定义操作日志记录注解
     */
    @Target({ ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log
    {
        /*模块*/
        public String title() default "";
        /**  功能 */
        public BusinessType businessType() default BusinessType.OTHER;
        /**  操作人类别 */
        public OperatorType operatorType() default OperatorType.MANAGE;
        /**  是否保存请求的参数 */
        public boolean isSaveRequestData() default true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    下文是拦截器(Aspect),如何实现注解对应的功能
    
    • 1
    /**
     * 操作日志记录处理
     */
    @Aspect // 切面注解
    @Component
    public class LogAspect
    {
    
        // 配置织入点 当有这个注解的时候触发此切面
        @Pointcut("@annotation(com.common.annotation.Log)")
        public void logPointCut(){}
    
        /**
         * 处理完请求后执行
         *
         * @param joinPoint 切点
         */
        @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
        public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
        {
                String className = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                System.out.println(className +"函数执行");
                //TODO 持久化进数据库也可以
        }
    
        /**
         * 
         * @param joinPoint 切点
         * @param e 异常
         */
        @AfterThrowing(value = "logPointCut()", throwing = "e")
        public void doAfterThrowing(JoinPoint joinPoint, Exception e)
        {
        	// 获取注解对象 begin
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();
            if (method != null)
            {
                return method.getAnnotation(Log.class); // 获取注解对象成功
            }
            // 获取注解对象 end
            /* 上文 单纯记录 一下如何获取 注解对象*/
    
    		// 当发生错误时进行处理
    		String methodName = joinPoint.getSignature().getName();
            System.out.println(className +"函数执行失败,失败内容:"+e.getMessage());
            //TODO 持久化进数据库也可以
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  • 相关阅读:
    Shell第四章《正则表达式》
    DOM节点(节点查找、节点创建、节点克隆、节点删除)
    react immutable
    经典Java面试题汇总及答案解析
    visual studio开发过程中常见操作
    modbus通信协议
    Haproxy搭建Web群集
    动手学深度学习_全卷积网络 FCN
    SpringSecurity系列——基于SpringBoot2.7的登录接口(内有惊喜)day2-1
    算法|每日一题|从数量最多的堆取走礼物|最大堆
  • 原文地址:https://blog.csdn.net/yao_1996/article/details/126246954