使用拦截器统一处理通用检查

栏目: IT技术 · 发布时间: 5年前

内容简介:在平时的业务开发中,相信大家都有很多这样的代码:那么,如果代码还有其他通用的校验,而且每加一个接口都要加这些校验逻辑,久而久之,代码会显得较臃肿,看起来会有很多重复的代码,那么有没有办法精简这部分代码呢?有!先上代码

繁琐的检查

在平时的业务开发中,相信大家都有很多这样的代码:

public void login(Parameter parameter) {
  if (!validateXXX(parameter)) {
    throw new BizException(ErrCode.PAMRM_ERROR);
  }
  
  // 真正的逻辑代码
}

那么,如果代码还有其他通用的校验,而且每加一个接口都要加这些校验逻辑,久而久之,代码会显得较臃肿,看起来会有很多重复的代码,那么有没有办法精简这部分代码呢?有!

Spring的HandlerInterceptor

先上代码

拦截器定义

public class CheckXXXHandlerInterceptor extends HandlerInterceptorAdapter {

    final Map<Method, Boolean> methodCache = new IdentityHashMap<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        /**
        这个是双重判断锁单例
        外层的判断,为了避免在实例已经创建好的情况下再次加锁获取,影响性能;
        里层的判断,考虑在多线程环境下,多个线程同时过掉外层判断,也就是都已经判断变量为空,如果不加一重判断,还是有可能重复创建。
        */
        Method method = handlerMethod.getMethod();
        if (!methodCache.containsKey(method)) {
            synchronized (methodCache) {
                if (!methodCache.containsKey(method)) {
                    boolean check = false;
                    if (method.isAnnotationPresent(CheckXXX.class)) {
                        check = method.getAnnotation(CheckXXX.class).value();
                    } else if (method.getDeclaringClass().isAnnotationPresent(CheckXXX.class)) {
                        check = method.getDeclaringClass().getAnnotation(CheckXXX.class).value();
                    }
                    methodCache.put(method, check);
                }
            }
        }
        if (methodCache.get(method)) {
            // do check
        }

        return true;
    }
}

注解定义

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface CheckXXX {
    boolean value() default true;
}

注解使用

@CheckXXX
public class XXXController {

    public void login(Parameter parameter) {
      // 真正的逻辑代码
    }
}

这样,就能抽离出通用的逻辑,精简通用的代码。那么,这个拦截器是什么时候执行的呢?它的实现原理是什么?

执行时机

使用拦截器统一处理通用检查

通过查看自定义拦截器的UML类图关系,可以看出来,其实是实现了HandlerInterceptor的preHandle方法,通过追踪HandlerInterceptor的调用链路,最终是在请求进入分发器,执行 doDispatch 方法用的,而处理器是在初始化的时候就加载好。

整体的流程如下:

使用拦截器统一处理通用检查

核心代码:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}


boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

拦截器数组interceptors是在Spring容器启动的时候初始化好的,实现原理比较简单,就是取出请求处理器的map,遍历调用注册好的拦截器。

实现原理

通过拦截器处理通用检查,背后的编程思想其实是AOP, 面向切面编程

  • 使用切面的优点:首先,现在每个关注点都集中于一个地 方,而不是分散到多处代码中;其次,服务模块更简洁,因为它们只包含主要关注点(或核 心功能)的代码,而次要关注点的代码被转移到切面中了。—-摘自《Spring实战》

关于AOP,网上有很多资料解释,看维基百科的描述也很清晰,,笔者就不多赘述了。

在这个例子里面,每个接口的核心功能是响应为业务功能提供服务,但是每个接口需要的参数检查、安全检查,都统一交给切面完成。如下图所示:

使用拦截器统一处理通用检查

总结

代码和原理比较简单,但是里面包含的知识点却不少,通过追朔源码,能了解细节之余,还能掌握某一类问题的实现方案。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

如果本文对你有帮助,请点个赞吧,谢谢^_^

更多精彩内容,请关注个人公众号。

使用拦截器统一处理通用检查


以上所述就是小编给大家介绍的《使用拦截器统一处理通用检查》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Trading and Exchanges

Trading and Exchanges

Larry Harris / Oxford University Press, USA / 2002-10-24 / USD 95.00

This book is about trading, the people who trade securities and contracts, the marketplaces where they trade, and the rules that govern it. Readers will learn about investors, brokers, dealers, arbit......一起来看看 《Trading and Exchanges》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码