您的位置:澳门新葡8455最新网站 > 编程教学 > 澳门新葡萄京娱乐场SpringMVC启动原理

澳门新葡萄京娱乐场SpringMVC启动原理

发布时间:2019-10-08 00:35编辑:编程教学浏览(112)

    Spring WebApplicationinitializer位于 Sring web下的 package org.springframework.web;

    容器运营之后会调用该接口的on startup方法;代码如下

    ServletContainerInitializer

    在web容器运行时为提必要第三方组件机缘做一些起初化的办事,比如登记servlet或者filtes等,servlet规范中通过ServletContainerInitializer落到实处此功效。
    各样框架要接纳ServletContainerInitializer就非得在相应的jar包的META-INF/services 目录创造一个名字为javax.servlet.ServletContainerInitializer的文件,文件内容钦命具体的ServletContainerInitializer实现类,
    那便是说,当web容器运行时就能够运营那么些开首化器做一些零件内的初始化职业。

    诚如伴随着ServletContainerInitializer一同利用的还应该有HandlesTypes注解,通过HandlesTypes能够将感兴趣的一些类注入到ServletContainerInitializerdeonStartup措施作为参数字传送入。

    Tomcat容器的ServletContainerInitializer机制的落到实处,首要交由Context容器ContextConfig监听器联机落实,
    ContextConfig监听器担当在容器运维时读取每种web应用的WEB-INF/lib目录下富含的jar包的META-INF/services/javax.servlet.ServletContainerInitializer,以及web根目录下的META-INF/services/javax.servlet.ServletContainerInitializer
    通过反射实现这么些ServletContainerInitializer的实例化,然后再设置到Context容器中,最后Context容器运行时就能够分别调用每一种ServletContainerInitializeronStartup措施,并将感兴趣的类作为参数字传送入。

    image.png

    着力的贯彻机制如图,首先通过ContextConfig监听器遍历每一个jar包或web根目录的META-INF/services/javax.servlet.ServletContainerInitializer文件,依照读到的类路线实例化种种ServletContainerInitializer;然后再各自将实例化好的ServletContainerInitializer设置进Context容器中;最终Context容器运转时分别调用全体ServletContainerInitializer对象的onStartup方法。

    一旦读出来的内容为com.seaboat.mytomcat.CustomServletContainerInitializer,则透过反射实例化叁个CustomServletContainerInitializer对象,这里提到到三个@HandlesTypes注明的管理,被它申明的类需求用作参数值传入到onStartup方法。

    日常来讲例子:

    @HandlesTypes({ HttpServlet.class,Filter.class }) 
    public class CustomServletContainerInitializer implements 
        ServletContainerInitializer { 
      public void onStartup(Set<Class<?>> classes, ServletContext servletContext) 
          throws ServletException {
          for(Class c : classes) 
             System.out.println(c.getName());
      } 
    }
    

    其中@HandlesTypes标明的HttpServletFilter多个class被注入到了onStartup方法。
    所以这一个注明也是索要在ContextConfig监听器中处理

    前段时间早就介绍了讲解的完毕原理,由于有了编写翻译器的声援,大家得以实惠地经过ServletContainerInitializer的class对象中拿走到HandlesTypes对象,从而再获得到注脚表明的类数组,如

    HandlesTypes ht =servletContainerInitializer.getClass().getAnnotation(HandlesTypes.class);
    Class<?>[] types = ht.value();
    

    就可以获得到HttpServletFilter的class对象数组,后边Context容器调用CustomServletContainerInitializer对象的onStartup办法时作为参数字传送入。

    由来,即成功了servlet规范的ServletContainerInitializer初阶化器机制。

    澳门新葡萄京娱乐场 1

    SpringServletContainerInitializer

    地方提到了META-INF/services/javax.servlet.ServletContainerInitializer,在Springspring-web-4.3.0.RELEASE.jar Jar包中得以找到该公文,内容如下:

    org.springframework.web.SpringServletContainerInitializer
    

    下边,我们就来详细解说下SpringServletContainerInitializer


    第一看下API中的描述:

    Servlet 3.0 ServletContainerInitializer被设计为运用Spring的WebApplicationInitializer SPI来支持Servlet容器的听别人讲代码的配置,实际不是思想的依附web.xml的配备(也或许互相结合使用)。

    干什么容器运维现在会调用该接口的on startup 方法,主假使出于位于它同包下的另三个类 SpringServletContainerInitializer

    一、运作体制

    若是类路线中设有spring-web模块的JAR包,SpringServletContainerInitializer将被加载并实例化,何况在容器运营期间由Servlet 3.0容器调用onStartup方法。

    那是经过JA陆风X8 Services API ServiceLoader.load(Class)方法(检测Spring-Web模块的META-INF/services/javax.servlet.ServletContainerInitializer配置文件)实现的。

    澳门新葡萄京娱乐场 2

    二、与web.xml结合使用

    Web应用程序能够采用通过web.xml中的metadata-complete属性(它决定扫描Servlet声明的一坐一起)
    或通过web.xml中的<absolute-ordering>要素(它决定什么web fragments(i.e. jars)被允许实践扫描ServletContainerInitializer)
    来界定Servlet容器在运营时扫描的类路线。

    当使用那几个特点时,能够通过增加"spring_web"到web.xml里的web fragments列表来启用SpringServletContainerInitializer

    如下所示:

    <absolute-ordering>
        <name>some_web_fragment</name>
        <name>spring_web</name>
    </absolute-ordering>
    

    servlet3.X中的metadata-complete属性
    在Servlet3.X的web.xml中能够安装metadata-complete属性,举例:

    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="true">
    </web-app>
    

    借使设置metadata-complete="true",会在启动时不扫描申明(annotation)。若是不扫描证明的话,用证明进行的配置就不能生效,例如:@WebServlet

    能够见见SpringServletContainerInitializer 首假设持续了ServletContainerInitializer 会在容器运营之后调用onStrartUp方法,该措施有多个参数,多少个是Set<Class>,三个是容器上下文ServletContext。Set<Class>的值由类尾部的注释 @HandlesTypes(WebApplicationInitializer.class)调整,会流传全部继续了解说的类的class,将set<class>和ServletContext传入该措施后,会由此放射的方法制造每多个类的实例保存到list中,并调用每一个类的onStartUp方法,试行开端化操作。

    三、与Spring的WebApplicationInitializer的关系

    Spring的WebApplicationInitializer SPI仅由贰个格局结合:WebApplicationInitializer.onStartup(ServletContext)。声明与ServletContainerInitializer.onStartup(Set, ServletContext)杰出相似:简单地说,SpringServletContainerInitializer负责将ServletContext实例化并委托给客商定义的WebApplicationInitializer实现。然后每个WebApplicationInitializer担当达成初阶化ServletContext的实际职业。下面的onStartup文档中详细介绍了寄托的切实可行经过。

    ServletContainerInitializer 是坐落javax.servlet包下的类,容器运营今后会调用该类的onStartup方法。

    四、注意事项

    貌似的话,那么些类应该被视为WebApplicationInitializer SPI的支撑。利用这几个容器伊始化器也是截然可选的:纵然那个初步化器在装有的Servlet 3.0+运转情形下被加载和调用,但顾客能够选拔是或不是提供WebApplicationInitializer落到实处。借使未检查测验到WebApplicationInitializer类型,则此SpringServletContainerInitializer将不起功效。

    请细心,除了那么些连串是在spring-web模块JAENCORE中提供的,使用这些SpringServletContainerInitializerWebApplicationInitializer与Spring MVC未有另外“捆绑”。相反,它们能够被以为是通用的,以便于简化ServletContext据说代码的配置。换句话说,任何servlet, listener, 或者filter都足以在WebApplicationInitializer中注册,而不止是Spring MVC特定的零部件。

    SpringServletContainerInitializer既不是为扩充而规划的。它应有被感觉是一个之中类型,WebApplicationInitializer是面向客户的SPI。


    好啦,现在对SpringServletContainerInitializer有了三个相比较深透的询问,上边我们来看一下独一的onStartup方法。

    ServletContext信托给类路线中的WebApplicationInitializer实现。
    因为这么些类申明了@HandlesTypes(WebApplicationInitializer.class),所以
    Servlet 3.0+容器会自动扫描类路线下Spring的WebApplicationInitializer接口的落到实处,并将兼具这么些类其余集纳提要求那些方法的webAppInitializerClasses参数。
    假若在类路线下找不到WebApplicationInitializer贯彻,则此办法不会有另外操作。将生出INFO级其余日志音信,通告客户ServletContainerInitializer确实已被调用,但从没找到WebApplicationInitializer实现。
    借使检验到三个或三个WebApplicationInitializer花色,它们将被实例化(假设存在@Order解说或促成Ordered接口,则对其开展排序)。然后,将调用每一种实例WebApplicationInitializer.onStartup(ServletContext)方法,并委派ServletContext,以便各样实例都得以挂号和布置Servlet,举例Spring的DispatcherServlet,listeners(如Spring的ContextLoaderListener),可能别的Servlet API组件(如filters)。


    下面是SpringServletContainerInitializer的源码:

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            // WebApplicationInitializer实现如果存在`@Order`注释或实现`Ordered`接口,则对其进行排序,故这里使用LinkedList
            List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
    
            if (webAppInitializerClasses != null) {
                for (Class<?> waiClass : webAppInitializerClasses) {
                    // Be defensive: Some servlet containers provide us with invalid classes,
                    // no matter what @HandlesTypes says...
                    // 有时候,Servlet容器提供给我们的可能是无效的webAppInitializerClass
                    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                        try {
                            initializers.add((WebApplicationInitializer) waiClass.newInstance());
                        }
                        catch (Throwable ex) {
                            throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                        }
                    }
                }
            }
    
            if (initializers.isEmpty()) {
                servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
                return;
            }
    
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);
            }
        }
    }
    

    澳门新葡萄京娱乐场 3

    WebApplicationInitializer

    上边我们来看下WebApplicationInitializerAPI文书档案的相干介绍。

    在Servlet 3.0+蒙受中达成该接口,以便以编程方式布置ServletContext,并不是以古板的依照web.xml的办法。WebApplicationInitializer SPI的兑现将被SpringServletContainerInitializer(它自身是由Servlet 3.0器皿自动引导的)自动物检疫查实验到。

    能够见到类上的笺注,大概意思为:承继这么些接口必需在jar文件的 META-INF/services目录下声诺优能(Nutrilon)(Nutrilon)个文书,文件的名字是以此接口的通通限定类名称,并将被运转时的劳动提供者查找体制照旧被容器中一定的类似机制发掘。在任一景况下,来自web服务器的jar排除的独门命令必需被忽略,开采那几个劳动的一一必需比照管用程序的加载委托情势。

    Example

    去Spring的源码去找,就能意识接口申明的公文

    基于XML的方式

    绝大非常多Spring客户打造Web应用程序时需要登记Spring的DispatcherServlet。作为参谋,经常在WEB-INF/web.xml中按如下格局:

     <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>
          org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    

    澳门新葡萄京娱乐场 4

    依赖代码的点子

    DispatcherServlet挂号逻辑与上述同样

    public class MyWebAppInitializer implements WebApplicationInitializer {
    
          @Override
          public void onStartup(ServletContext container) {
            XmlWebApplicationContext appContext = new XmlWebApplicationContext();
            appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
    
            ServletRegistration.Dynamic dispatcher =
              container.addServlet("dispatcher", new DispatcherServlet(appContext));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
          }
       }
    

    用作上述的代替格局,您还可以够连续自org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
    正如你所见到的,使用Servlet 3.0的ServletContext.addServlet形式,我们报了名了二个DispatcherServlet的实例。

    这种作风老妪能解。不用关切管理init-params等,只是日常的JavaBean风格的习性和构造函数参数。在将其注入到DispatcherServlet事先,您能够依据要求自由成立和应用Spring应用程序上下文。

    大部Spring Web组件已经更新,以支撑这种注册方式。你会开掘DispatcherServletFrameworkServletContextLoaderListenerDelegatingFilterProxy近来都援助构造函数参数。Servlet 3.0 ServletContext API允许以编制程序形式设置init-paramscontext-params等。

    全然依赖代码的布局格局

    在上边的例子中,WEB-INF/web.xmlWebApplicationInitializer款式的代码替换,但dispatcher-config.xml布局依旧是基于XML的。WebApplicationInitializer特别切合与Spring的依据代码的@Configuration类一同使用。以下示例演示了动用Spring的AnnotationConfigWebApplicationContext代替XmlWebApplicationContext进行重构,以及利用顾客定义的@ConfigurationAppConfigDispatcherConfig,而不是Spring XML文件。那几个例子也高于了地点的例证来演示'root' 应用上下文的举世无双配置和ContextLoaderListener的注册:

    public class MyWebAppInitializer implements WebApplicationInitializer {
    
          @Override
          public void onStartup(ServletContext container) {
            // Create the 'root' Spring application context
            AnnotationConfigWebApplicationContext rootContext =
              new AnnotationConfigWebApplicationContext();
            rootContext.register(AppConfig.class);
    
            // Manage the lifecycle of the root application context
            container.addListener(new ContextLoaderListener(rootContext));
    
            // Create the dispatcher servlet's Spring application context
            AnnotationConfigWebApplicationContext dispatcherContext =
              new AnnotationConfigWebApplicationContext();
            dispatcherContext.register(DispatcherConfig.class);
    
            // Register and map the dispatcher servlet
            ServletRegistration.Dynamic dispatcher =
              container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
          }
    
       }
    

    用作上述的代替方式,您还是能够持续自org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer。注意,WebApplicationInitializer的完毕类会被自动物检疫查测量检验到。

    Ordering WebApplicationInitializer execution

    WebApplicationInitializer落实类能够有选择地在类上使用Spring的@Order注解,也足以达成Spring的Ordered接口。倘若是如此,初叶化程序将要调用在此以前排序。那为客商提供了保证Servlet容器最初化顺序的体制。使用此功效的情形非常少,因为规范的应用程序只怕会将具有容器初始化聚集在一个WebApplicationInitializer中。

    注意事项

    web.xml版本

    WEB-INF/web.xmlWebApplicationInitializer的行使不是排斥的; 比如,web.xml能够注册三个servlet,而WebApplicationInitializer能够登记另三个。 Initializer竟然能够通过诸如ServletContext.getServletRegistration(String)等等的措施来修改在web.xml中施行的注册。不过,假设应用程序中设有WEB-INF/web.xml,则其版本属性必得设置为"3.0"或越来越高,不然Servlet容器将忽略ServletContainerInitializer的引导。


    下边大家来看一组WebApplicationInitializer的落实类:

    此伏彼起关系如下:

    AbstractAnnotationConfigDispatcherServletInitializer
            |
            | —— AbstractDispatcherServletInitializer
                       |
                       | —— AbstractContextLoaderInitializer
                                 |
                                 | ——  WebApplicationInitializer
    

    AbstractAnnotationConfigDispatcherServletInitializer

    org.springframework.web.WebApplicationInitializer落到实处类的基类,用于注册配置了@Configuration/@Component评释标记的配置类DispatcherServlet

    具体的落到实处类供给贯彻getRootConfigClasses()getServletConfigClasses()以及getServletMappings()措施。越多的不二等秘书籍由AbstractDispatcherServletInitializer提供。那是使用基于Java配置应用程序的首要推荐办法。

    下面是AbstractAnnotationConfigDispatcherServletInitializer的源码:

    public abstract class AbstractAnnotationConfigDispatcherServletInitializer
            extends AbstractDispatcherServletInitializer {
    
        /**
         * 创建要提供给`ContextLoaderListener`的**根应用程序上下文**。
         * <p>
         * 返回的上下文委托给`ContextLoaderListener.ContextLoaderListener(WebApplicationContext)`,
         * 并将作为`DispatcherServlet`应用程序上下文的父上下文来建立。
         * <p>
         * 因此,它通常包含中间层服务,数据源等。
         * <p>
         * 该方法创建一个`AnnotationConfigWebApplicationContext`,并为其提供由`getRootConfigClasses()`返回的配置类。如果`getRootConfigClasses()`返回Null,则不会创建根应用上下文。
         */
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            Class<?>[] configClasses = getRootConfigClasses();
            if (!ObjectUtils.isEmpty(configClasses)) {
                AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
                rootAppContext.register(configClasses);
                return rootAppContext;
            }
            else {
                return null;
            }
        }
    
        /**
         * 创建一个**Servlet应用上下文**以提供给`DispatcherServlet`。
         * <p>
         * 返回的上下文被委托给Spring的`DispatcherServlet.DispatcherServlet(WebApplicationContext)`方法。
         * <p>
         * 因此,它通常包含控制器,视图解析器,locale解析器和其他Web相关的bean。
         * <p>
         * 该实现创建一个`AnnotationConfigWebApplicationContext`,为其提供由`getServletConfigClasses()`返回的配置类。
         */
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
            Class<?>[] configClasses = getServletConfigClasses();
            if (!ObjectUtils.isEmpty(configClasses)) {
                servletAppContext.register(configClasses);
            }
            return servletAppContext;
        }
    
        /**
         * 指定要提供给根应用上下文的`@Configuration`或`@Component`注解标记的配置类。
         */
        protected abstract Class<?>[] getRootConfigClasses();
    
        /**
         * 指定要提供给Dispatcher Servlet应用上下文的`@Configuration`或`@Component`注解标记的配置类。
         */
        protected abstract Class<?>[] getServletConfigClasses();
    
    }
    

    AbstractDispatcherServletInitializer

    org.springframework.web.WebApplicationInitializer达成的基类,在ServletContext中注册DispatcherServlet

    切实的兑现类必要贯彻createServletApplicationContext()getServletMappings()办法,两个都由registerDispatcherServlet(ServletContext)调用。

    尤为的自定义可以经过重写customizeRegistration(ServletRegistration.Dynamic)方式来促成。

    鉴于此类承袭了AbstractContextLoaderInitializer抽象类,具体达成类也要求贯彻createRootApplicationContext()来安装父级根应用上下文。倘使没有须求根应用上下文,createRootApplicationContext()返回null即可。

    下边看下源码:

    public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
    
        /**
         * The default servlet name. Can be customized by overriding {@link #getServletName}.
         */
        public static final String DEFAULT_SERVLET_NAME = "dispatcher";
    
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            registerDispatcherServlet(servletContext);
        }
    
        /**
         * 针对给定的Servlet上下文注册一个`DispatcherServlet`。
         * <p>
         * 该方法将创建一个名称为由`getServletName()`指定的`DispatcherServlet`,
         * 并使用从`createServletApplicationContext()`返回的Servlet应用上下文对其进行初始化,
         * 并将其映射到从`getServletMappings()`返回的`pattern`。
         * <p>
         * 进一步的自定义可以通过重写`customizeRegistration(ServletRegistration.Dynamic)`或`createDispatcherServlet(WebApplicationContext)`来实现。
         *
         * @param servletContext 注册servlet的上下文
         */
        protected void registerDispatcherServlet(ServletContext servletContext) {
            // DispatcherServlet被注册的名称
            String servletName = getServletName();
            Assert.hasLength(servletName, "getServletName() must not return empty or null");
    
            // 创建Servlet应用上下文,参见AbstractAnnotationConfigDispatcherServletInitializer的实现
            WebApplicationContext servletAppContext = createServletApplicationContext();
            Assert.notNull(servletAppContext,
                    "createServletApplicationContext() did not return an application " +
                            "context for servlet [" + servletName + "]");
    
            // 将Servlet应用上下文以委托给DispatcherServlet
            FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
    
            ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
            Assert.notNull(registration,
                    "Failed to register servlet with name '" + servletName + "'." +
                            "Check if there is another servlet registered under the same name.");
    
            registration.setLoadOnStartup(1);
            registration.addMapping(getServletMappings());
            registration.setAsyncSupported(isAsyncSupported());
    
            Filter[] filters = getServletFilters();
            if (!ObjectUtils.isEmpty(filters)) {
                for (Filter filter : filters) {
                    registerServletFilter(servletContext, filter);
                }
            }
    
            customizeRegistration(registration);
        }
    
        /**
         * 返回`DispatcherServlet`被注册的名称。 默认值为DEFAULT_SERVLET_NAME。
         *
         * @see #registerDispatcherServlet(ServletContext)
         */
        protected String getServletName() {
            return DEFAULT_SERVLET_NAME;
        }
    
        /**
         * 创建一个Servlet应用上下文以提供给`DispatcherServlet`。
         * 返回的上下文被委托给Spring的`DispatcherServlet.DispatcherServlet(WebApplicationContext)`。
         * <p>
         * 因此,它通常包含控制器,视图解析器,locale解析器和其他Web相关的bean。
         *
         * @see #registerDispatcherServlet(ServletContext)
         */
        protected abstract WebApplicationContext createServletApplicationContext();
    
        /**
         * 使用指定的`WebApplicationContext`Servlet应用上下文创建`DispatcherServlet`(或由`FrameworkServlet`派生的其他类型的`dispatcher`)。
         * <p>
         * 注意:从4.2.3开始允许返回任意`FrameworkServlet`的子类。
         * 以前,它必须要返回一个`DispatcherServlet`或其子类。
         */
        protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
            return new DispatcherServlet(servletAppContext);
        }
    
        /**
         * Specify application context initializers to be applied to the servlet-specific
         * application context that the {@code DispatcherServlet} is being created with.
         *
         * @see #createServletApplicationContext()
         * @see DispatcherServlet#setContextInitializers
         * @see #getRootApplicationContextInitializers()
         * @since 4.2
         */
        protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
            return null;
        }
    
        /**
         * 指定`DispatcherServlet`的servlet映射 - 例如"/", "/app"等
         *
         * @see #registerDispatcherServlet(ServletContext)
         */
        protected abstract String[] getServletMappings();
    
        /**
         * 指定添加到`ServletContext`,并映射到`DispatcherServlet`的过滤器。
         *
         * @return an array of filters or {@code null}
         * @see #registerServletFilter(ServletContext, Filter)
         */
        protected Filter[] getServletFilters() {
            return null;
        }
    
        /**
         * 将给定的过滤器添加到`ServletContext`并将其映射到`DispatcherServlet`,如下所示:
         * <ul>
         * <li>1. 根据具体的类型选择默认的过滤器名称;
         * <li>2. 异步支持是根据`asyncSupported`的返回值设置的;
         * <li>3. 使用`dispatcher`类型为REQUEST,FORWARD,INCLUDE和ASYNC(取决于asyncSupported的返回值)创建过滤器映射
         * </ul>
         * <p>
         * 如果上面的默认值不合适,重写这个方法并直接用`ServletContext`注册过滤器。
         *
         * @param servletContext the servlet context to register filters with
         * @param filter         the filter to be registered
         * @return the filter registration
         */
        protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
            // 获取要注册的filter的名称
            String filterName = Conventions.getVariableName(filter);
            Dynamic registration = servletContext.addFilter(filterName, filter);
            if (registration == null) {// 注册失败,名称追加序号并重试
                int counter = -1;
                while (counter == -1 || registration == null) {
                    counter++;
                    registration = servletContext.addFilter(filterName + "#" + counter, filter);
                    Assert.isTrue(counter < 100,
                            "Failed to register filter '" + filter + "'." +
                                    "Could the same Filter instance have been registered already?");
                }
            }
            registration.setAsyncSupported(isAsyncSupported());
            registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
            return registration;
        }
    
        private EnumSet<DispatcherType> getDispatcherTypes() {
            return (isAsyncSupported() ?
                    EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) :
                    EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
        }
    
        /**
         * `DispatcherServlet`和通过`getServletFilters()`添加的所有过滤器的异步支持标记位
         */
        protected boolean isAsyncSupported() {
            return true;
        }
    
        /**
         * Optionally perform further registration customization once
         * {@link #registerDispatcherServlet(ServletContext)} has completed.
         *
         * @param registration the {@code DispatcherServlet} registration to be customized
         * @see #registerDispatcherServlet(ServletContext)
         */
        protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        }
    
    }
    

    本文由澳门新葡8455最新网站发布于编程教学,转载请注明出处:澳门新葡萄京娱乐场SpringMVC启动原理

    关键词: