第三十六章 Spring之假如让你来写MVC——拦截器篇

Spring源码阅读目录

第一部分------IOC篇

第一章 Spring之最熟悉的陌生人------IOC
第二章 Spring之假如让你来写IOC容器------加载资源篇
第三章 Spring之假如让你来写IOC容器------解析配置文件篇
第四章 Spring之假如让你来写IOC容器------XML配置文件篇
第五章 Spring之假如让你来写IOC容器------BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器------Scope和属性填充
第七章 Spring之假如让你来写IOC容器------属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器------拓展篇
第九章 Spring之源码阅读------环境搭建篇
第十章 Spring之源码阅读------IOC篇

第二部分------AOP篇

第十一章 Spring之不太熟的熟人------AOP
第十二章 Spring之不得不了解的内容------概念篇
第十三章 Spring之假如让你来写AOP------AOP联盟篇
第十四章 Spring之假如让你来写AOP------雏形篇
第十五章 Spring之假如让你来写AOP------Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP------Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP------Advice(通知)上篇
第十八章 Spring之假如让你来写AOP------Advice(通知)下篇
第十九章 Spring之假如让你来写AOP------番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP------Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP------Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP------Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP------融入IOC容器篇
第二十四章 Spring之源码阅读------AOP篇

第三部分------事务篇

第二十五章 Spring之曾经的老朋友------事务
第二十六章 Spring之假如让你来写事务------初稿篇
第二十七章 Spring之假如让你来写事务------铁三角篇
第二十八章 Spring之假如让你来写事务------属性篇
第二十九章 Spring之假如让你来写事务------状态篇
第三十章 Spring之假如让你来写事务------管理篇
第三十一章 Spring之假如让你来写事务------融入IOC容器篇
第三十二章 Spring之源码阅读------事务篇

第四部分------MVC篇

第三十三章 Spring之梦开始的地方------MVC
第三十四章 Spring之假如让你来写MVC------草图篇
第三十五章 Spring之假如让你来写MVC------映射器篇
第三十六章 Spring之假如让你来写MVC------拦截器篇
第三十七章 Spring之假如让你来写MVC------控制器篇
第三十八章 Spring之假如让你来写MVC------适配器篇
第三十九章 Spring之假如让你来写MVC------番外篇:类型转换
第四十章 Spring之假如让你来写MVC------ModelAndView篇
第四十一章 Spring之假如让你来写MVC------番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC------视图篇
第四十三章 Spring之假如让你来写MVC------上传文件篇
第四十四章 Spring之假如让你来写MVC------异常处理器篇
第四十五章 Spring之假如让你来写MVC------国际化篇
第四十六章 Spring之假如让你来写MVC------主题解析器篇
第四十七章 Spring之假如让你来写MVC------闪存管理器篇
第四十八章 Spring之假如让你来写MVC------请求映射视图篇
第四十九章 Spring之假如让你来写MVC------番外篇:属性操作
第五十章 Spring之假如让你来写MVC------融入IOC容器篇
第五十一章 Spring之源码阅读------MVC篇


文章目录


前言

对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了

所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


书接上回,在上篇 第三十五章 Spring之假如让你来写MVC------映射器篇 中,A君 已经实现了 映射器 部分的功能了。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

出场人物:A君 (苦逼的开发)、老大(项目经理)

背景:老大 要求 A君 在一周内开发个简单的 IOC容器

前情提要:A君 已经实现了 映射器 部分的功能了 。。。

第三十二版 拦截器

今天,刚一上班,A君 就屁颠屁颠的跑到 老大 的办公室去,炫耀自己的成果

"嗯。做的不错。不过还需要加点料?" 老大 看着满脸兴奋的 A君 。悠悠说道

"加点料,要加什么??" A君 兴奋地逐渐消失,一脸懵逼的问到

"你听说过 过滤器 吗?" 老大 微笑着问道

"听说过,过滤器 是 Servlet 规范中的一部分,所有 Servlet容器 都必须实现。请求在到达 Servlet 之前,或者响应返回客户端之前,都会经过 过滤器 进行处理。如果 过滤器 处理不通过,它可以阻止请求继续往下处理!" A君 回答道

"不错,现在要加的料和 过滤器 效果差不多,只是是框架层面的。叫做 拦截器 。" 老大 说到

"为什么有 过滤器 之后还需要 拦截器 呢?" A君 提出疑问

"问得好!原因其实也很简单。过滤器 是 Servlet容器 的行为,发生在 Servlet 之前,那么就以为这它无法获取框架中的内容,无法进行更细致的拦截。" 老大 笑着说道

"原来如此!" A君 恍然,之前一直存在的疑问,被 老大 三言两语就解开了

"去吧!这东西并不难,我希望今天就能看到成果!" 老大 大手一挥,开始下逐客令

拦截器接口

"OK!" A君 也爽快的回答道,离开办公室,回到自己的工位上。A君 想都没想,就开始撸代码,因为像这种提供拓展的功能,A君 只需要提供接口就行,具体内容由用户实现即可。A君 新增 HandlerInterceptor接口,代码如下:

java 复制代码
/**
 * 拦截器接口
 */
public interface HandlerInterceptor {
    /**
     * 方法执行前调用
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    /**
     * 方法执行后调用
     *
     * @param request
     * @param response
     * @param handler
     * @throws Exception
     */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }

    /**
     * 请求完成时调用,不管成功还是失败
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

好了,接口定义了完成了。不过要如何执行它的实现呢?老大 之前提到和 过滤器 类似,说起 过滤器 ,A君 其实并不算陌生,之前有折腾过 Tomcat ,知道其大致的运行流程。这里又得涉及到一个设计模式------责任链 。这个模式也好理解,就像 A君 平时想请个假,OA上需要经过层层审批,层层回复一样:
请假 同意 同意 同意 回复 回复 回复 回复 A君 项目经理 部门经理 技术总监 结束

每一层都得同意,这个假才算请成功,但凡有一个不同意,这个假就算是请失败了。值得注意的是:请假流程申请的时候是从前往后,而回复的时候却是从后往前的 。责任链 与之类似,既然如此,那么 拦截器 也就好办了:只要把 拦截器 整合成一个链表就可以了。A君 添加HandlerExecutionChain类,代码如下:

java 复制代码
/**
 * 请求处理链
 */
@Getter
public class HandlerExecutionChain {
    /**
     * 控制器
     */
    private final Object handler;
    /**
     * 拦截器集合
     */
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();

    private int interceptorIndex = -1;
	/**
     * 正向处理,类似与请假申请流程
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for (int i = 0; i < this.interceptorList.size(); i++) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
            	//返回false,直接调用完成方法
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
        return true;
    }

    /**
     * 反向处理,类似与请假回复流程
     *
     * @param request
     * @param response
     * @throws Exception
     */
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            interceptor.postHandle(request, response, this.handler);
        }
    }
    /**
     * 反向处理,类似与请假回复流程
     *
     * @param request
     * @param response
     * @throws Exception
     */
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            } catch (Throwable ex2) {
                ex2.printStackTrace();
            }
        }
    }
	//其他方法省略
}
改造映射器

确实如 老大 所说,拦截器 就这么点东西,没啥难度的。现在还需要改下 映射器 的返回值了,之前是直接返回HandlerMethod,现在得返回HandlerExecutionChain了,改动如下:


AbstractHandlerMapping也做个简单的改动,需要把配置的 拦截器 添加到HandlerExecutionChain中,如下:

改造DispatcherServlet

现在基本改造完了,还需要个添加 拦截器 的入口,只需要扫描类是否实现了对应接口就行了。DispatcherServlet改动如下:

测试

好嘞,现在一切都准备就绪了。可以开始准备测试了,其他内容还是不需要改动。只需要新增一个 拦截器 即可,A君 新增MyInterceptor。代码如下:

java 复制代码
public class MyInterceptor implements HandlerInterceptor {

    // 在请求处理前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Pre-handle: " + request.getRequestURI());
        request.setAttribute("message", "Add Interceptor");
        return true;
    }

    // 在请求处理后,视图渲染前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Post-handle: " + request.getRequestURI());
    }

    // 在请求完全处理完后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) throws Exception {
        System.out.println("After completion: " + request.getRequestURI());
    }
}

添加测试代码如下:

java 复制代码
	 @Test
    public void v32() throws Throwable {
        System.out.println("############# 第三十二版: 拦截器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v32").getAbsolutePath();
        tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

测试结果如下:

前台成功返回,后台也成功打印。拦截器 也就这么完成啦。OK!起码今天可以交差了,看看 老大 明天还有什么想法吧


总结

正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

相关推荐
wuminyu1 小时前
C++协程实现接收端的零拷贝Buffer管理原理剖析
java·linux·c语言·jvm·c++
蜗牛互联网3 小时前
WSL Containers GA:本地AI容器的生命周期、网络与治理验收
java·网络·人工智能·后端
龙亘川4 小时前
铭记英烈守初心,数字强基促服务:亘川智城退役军人服务系统的实践与价值
java·数据库·人工智能
OxYGC4 小时前
[AI工程] Spring AI 第廿一篇:存量 REST 接口的 MCP 化改造实录——工具从哪条路径注册、参数描述怎么写、身份怎么过去
java·人工智能·spring
Zhou1411365 小时前
SpringBoot_02_自动配置原理
java·spring boot·后端
FYKJ_20105 小时前
SSM校园失物招领系统41452-计算机课程设计、毕业设计
java·vue.js·spring boot·python·mysql·typescript·spark
kv1105 小时前
Android studio国内开发有关
java·数据库·android studio
卓怡学长5 小时前
w198基于springboot始于足下健康打卡平台的设计与实现
java·spring boot·spring·intellij-idea
阿狗童鞋5 小时前
Java并发编程实战指南
java·开发语言
子兮曰6 小时前
Node.js 50个优势场景盘点:一个人单干,为啥我多数时候只用它
前端·后端