• 责任链模式之Tomcat Filter应用


    责任链模式之Tomcat Filter应用

    责任链模式是一种对象的行为型模式。

    其定义为:很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象处理此请求。发出这个请求的客户端并不知道链上哪个对象最终会处理这个请求,这使得系统可以在不影响客户端的情况下动态的重新组织和分配责任。

    结构:

    1. 抽象处理者(Handler) 角色:定义出一个处理请求的接口。

    2. 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。【持有对下家的引用】

    优点:

    1. 降低耦合,

    2. 简化对象,

    3. 增强对象职责灵活性,

    4. 方便新增处理类。

    缺点:

    1. 不能保证请求被接收,

    2. 系统性能受影响,容易造成循环调用。

    例:dubbo里调用链/过滤链 的创建就是 责任链模式,tomcat filter也使用了责任链模式。

    分析Tomcat Filter

    核心逻辑入口:org.apache.catalina.core.StandardWrapperValve#invoke。

    StandardWrapperValve 是 StandardWrapper的基础阀,其作用是加载Servlet,创建Filter过滤器,最终会调用Servlet的service方法。

    1. //StandardWrapperValve类
    2. @Override
    3. public final void invoke(Request request, Response response)
    4.     throws IOException, ServletException {
    5.     省略代码......
    6.     
    7.     //分配一个Servlet实例来处理这个请求
    8.     if (!unavailable) {
    9.         servlet = wrapper.allocate();
    10.     }
    11.     
    12.     //为此请求创建过滤器链
    13.     ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    14.     
    15.     //为此请求调用过滤器链,也会调用Servlet的service()方法
    16.     filterChain.doFilter(request.getRequest(), response.getResponse());
    17.     
    18.     ......
    19. }

    分配Servlet实例

    本文基于springboot 2.2.2.RELEASE 框架代码分析。

    servlet = wrapper.allocate(); 此行代码是分配一个Servlet实例来处理请求。

    由DispatcherServletAutoConfiguration自动配置类 向容器中注入 DispatcherServlet Bean。所有的请求由该类分发。

    1. ......
    2. public class DispatcherServletAutoConfiguration {
    3.    protected static class DispatcherServletConfiguration {
    4.     ......
    5.   @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    6.   public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
    7.     DispatcherServlet dispatcherServlet = new DispatcherServlet();
    8.     dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
    9.     dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
    10.     dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
    11.     dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
    12.     dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
    13.     return dispatcherServlet;
    14.   }
    15. }

    创建过滤器链

    ServletWebServerApplicationContextSpringApplicationContext容器的一个子类。

    selfInitialize 方法会调用 getServletContextInitializerBeans 来获取ServletContextInitializer 实例对象集合。包含所有的ServletContextInitializer Bean对象(还适用于Servlet、Filter、EventListener Bean)。

    ServletContextInitializer接口源码注释:

    用于以编程方式配置 Servlet 3.0+ ServletContext的接口。

    接口(并且不实现 WebApplicationInitializer} 将不会被SpringServletContainerInitializer 检测到,因此不会由 Servlet 容器自动引导。

    此接口设计方式类似于 ServletContainerInitializer, 但其由 Spring 管理其生命周期而不是Servlet 容器。

    addAdaptableBeans()

    实例化并注册了所有实现Servlet、Filter及EventListener接口的类。所以此处就会加载Filter过滤器类(包括我们自定义的实现)。

    1. //ServletContextInitializerBeans类
    2. protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
    3.   MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
    4.   addAsRegistrationBean(beanFactory, Servlet.classnew ServletRegistrationBeanAdapter(multipartConfig));
    5.   addAsRegistrationBean(beanFactory, Filter.classnew FilterRegistrationBeanAdapter());
    6.   for (Class listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
    7.    addAsRegistrationBean(beanFactory, EventListener.class, (Class) listenerType,
    8.      new ServletListenerRegistrationBeanAdapter());
    9.   }
    10.  }

    调用过滤器链

    执行过滤器链,从ApplicationFilterConfig[] 数组中取出过滤器链执行,直到所有过滤器执行完毕。

    1. private void internalDoFilter(ServletRequest request,
    2.                               ServletResponse response)
    3.     throws IOException, ServletException {
    4.     if (pos < n) {
    5.         ApplicationFilterConfig filterConfig = filters[pos++];
    6.         Filter filter = filterConfig.getFilter();
    7.         filter.doFilter(requestresponse, this);
    8.         省略......
    9.     }
    10.     servlet.service(requestresponse);
    11.     ......
    12. }

    最后会调用org.springframework.web.servlet.DispatcherServlet#doService。

  • 相关阅读:
    redis安装与使用
    HTML与CSS的初步解析及实践案例
    用指针和动态内存分配的方法输入10,2,30, 4,5,按输入顺序逆置排序,输出排序后的元素,即输出5,4,30,2,10
    Linux命令之sed批量替换字符串
    web前端期末大作业 html+css+javascript汽车销售网站 学生网页设计实例 企业网站制作
    ASP.NET 中验证的自定义返回和统一社会信用代码的内置验证实现
    Java基础考试题,来测试下自己能拿多少分?提供答案
    缺陷之灵魂操作bug
    2022R2移动式压力容器充装考试试题模拟考试平台操作
    【RocketMQ】sendDefaultImpl call timeout 问题及其解决办法
  • 原文地址:https://blog.csdn.net/u011385940/article/details/126375881