SpringMVC之五:自定义DispatcherServlet配置及配置额外的 servlets 和 filters
相关文章
《Servlet3.0之四:动态注册和Servlet容器初始化》
《SpringBoot中通过SpringBootServletInitializer如何实现组件加载》
《SpringMVC之五:自定义DispatcherServlet配置及配置额外的 servlets 和 filters》
一、web容器如何初始化第三方组件(servlet、filters)
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer
实现此功能。每个框架要使用ServletContainerInitializer
就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer
的文件,文件内容指定具体的ServletContainerInitializer
实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。一般伴随着ServletContainerInitializer
一起使用的还有HandlesTypes
注解,通过HandlesTypes
可以将感兴趣的一些类注入到ServletContainerInitializerde
的onStartup()方法作为参数传入。
ServletContainerInitializer的源码:
- package javax.servlet;
- public interface ServletContainerInitializer {
- void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
- }
容器启动时会自动扫描当前服务中ServletContainerInitializer的实现类,并调用其onStartup方法,其参数Set<Class<?>> c,可通过在实现类上声明注解进行注入。(例如:可通过在实现类上声明注解javax.servlet.annotation.HandlesTypes(WebApplicationInitializer.class)注解自动注入,@HandlesTypes会自动扫描项目中所有的WebApplicationInitializer.class的实现类,并将其全部注入Set。)
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容器启动时就会分别调用每个ServletContainerInitializer
的onStartup方法,并将感兴趣的类作为参数传入。
图-1
基本的实现机制如图,首先通过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
标明的HttpServlet和Filter两个class被注入到了onStartup方法。所以这个注解也是需要在ContextConfig监听器中处理。前面已经介绍了注解的实现原理,由于有了编译器的协助,我们可以方便地通过ServletContainerInitializer
的class对象中获取到HandlesTypes对象,进而再获取到注解声明的类数组,如
- HandlesTypes ht =servletContainerInitializer.getClass().getAnnotation(HandlesTypes.class);
- Class<?>[] types = ht.value();
即可获取到HttpServlet和Filter的class对象数组,后面Context容器调用CustomServletContainerInitializer
对象的onStartup方法时作为参数传入。至此,即完成了servlet规范的ServletContainerInitializer
初始化器机制。
二、Spring中初始化组件(servlet、filters)的过程
2.1、Spring中是如何初始化组件(servlet、filters)--SpringServletContainerInitializer
根据一所述在 Servlet 3.0 的环境中,容器会在 classpath 中寻找继承了 javax.servlet.ServletContainerInitializer
接口的类,用它来配置 servlet 容器。 Spring 提供了一个继承这个接口的类 SpringServletContainerInitializer
,在这个类中,它会寻找任何继承了 WebApplicationInitializer
接口的类并用其来配置 servlet 容器。
先看看SpringServletContainerInitializer.java的源码:
- package org.springframework.web;
- @HandlesTypes(WebApplicationInitializer.class)
- public class SpringServletContainerInitializer implements ServletContainerInitializer { //Spring框架中继承
- @Override
- public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
- throws ServletException {
- 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...
- 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);
- }
- }
- }
2.2、Spring中是如何初始化组件(servlet、filters)--WebApplicationInitializer
Spring将Servlet配置相关的转给了WebApplicationInitializer,
Spring 3.2 提供了一个继承了 WebApplicationInitializer
接口的基类 AbstractAnnotationConfigDispatcherServletInitializer
。所以,你的 servlet 配置类只需要继承 AbstractAnnotationConfigDispatcherServletInitializer
,就会被发现而用于 servlet 容器的配置。
图-2
- ***DispatcherServlet VS ContextLoaderListener
- 在 Spring MVC 中存在两种应用上下文:DispatcherServlet 创建的和拦截器 ContextLoaderListener 创建的上下文:
- 1、DispatcherServlet:加载包含 web 组件的 bean,比如 controllers,view resolvers 和 hanlder mappings。
- 2、ContextLoaderListener:加载其他 bean,通常是一些中间层和数据层的组件(比如数据库配置 bean 等)。
回到AbstractAnnotationConfigDispatcherServletInitializer
,在 AbstractAnnotationConfigDispatcherServletInitializer
中 DispatcherServlet
和 ContextLoaderListener
都会被创建,而基类中的方法就可用来创建不同的应用上下文:
getServletConfigClasses()
:定义DispatcherServlet
应用上下文中的 beansgetRootConfigClasses()
:定义拦截器ContextLoaderListener
应用上下文中的 beans
Note:为了使用 AbstractAnnotationConfigDispatcherServletInitializer
必须保证 web 服务器支持 Servlet 3.0 标准(如 tomcat 7 或更高版本) 。
图-3
三、自定义 DispatcherServlet 配置
因为我们使用 AbstractAnnotationConfigDispatcherServletInitializer
来配置 DispatcherServlet
,同时可以通过 customizeRegistration()
方法来对DispatcherServlet进行额外的配置
。
通过 ServletRegistration.Dynamic
参数配置 DispatcherServlet
的 load-on-startup 优先级 setLoadOnStartup(int loadOnStartup)
,设置初始化参数 setInitParameters()
等。具体查看文档 ServletRegistration.Dynamic。
在AbstractAnnotationConfigDispatcherServletInitializer将DispatcherServlet注册到Servlet容 器中之后,就会调用customizeRegistration(),并将Servlet注册后得到的 Registration.Dynamic传递进来。通过重载customizeRegistration()方法,我们可 以对DispatcherServlet进行额外的配置。
看AbstractDispatcherServletInitializer的部分源码
- protected void registerDispatcherServlet(ServletContext servletContext) {
- String servletName = getServletName();
- Assert.hasLength(servletName, "getServletName() must not return empty or null");
- WebApplicationContext servletAppContext = createServletApplicationContext();
- Assert.notNull(servletAppContext,
- "createServletApplicationContext() did not return an application " +
- "context for servlet [" + servletName + "]");
- 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);
- }
例如,在本章稍后的内容中(7.2节),我们将会看到如何在Spring MVC中处理multipart请求和文 件上传。如果计划使用Servlet 3.0对multipart配置的支持,那么需要使 用DispatcherServlet的registration来启用multipart请求。我们可以重 载customizeRegistration()方法来设置MultipartConfigElement,如下所示:
- @Override
- protected void customizeRegistration(Dynamic registration) {
- registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
- super.customizeRegistration(registration);
- }
借助customizeRegistration()方法中的ServletRegistration.Dynamic,我们能 够完成多项任务,包括通过调用setLoadOnStartup()设置load-on-startup优先级,通过 setInitParameter()设置初始化参数,通过调用setMultipartConfig()配置Servlet 3.0对multipart的支持。在前面的样例中,我们设置了对multipart的支持,将上传文件的临时存 储目录设置在“/tmp/spittr/uploads”中。
四、配置额外的 servlets 和 filters
按照AbstractAnnotationConfigDispatcherServletInitializer的定义,它会创 建DispatcherServlet和ContextLoaderListener。如上图-3所示。但是,如果你想注册其他的 Servlet、Filter或Listener的话,那该怎么办呢?
基于Java的初始化器(initializer)的一个好处就在于我们可以定义任意数量的初始化器类。因 此,如果我们想往Web容器中注册其他组件的话,只需创建一个新的初始化器就可以了。最简 单的方式就是实现Spring的WebApplicationInitializer接口。 例如,如下的程序清单展现了如何创建WebApplicationInitializer实现并注册一个 Servlet。
使用 java 配置 servlet 的一个好处(不同于 web.xml)就是:可以定义任意数量的初始化类。所以,如果需要定义额外的 servlets 或 filters,只需要创建额外的初始化类。在 Spring MVC 中可以通过继承 WebApplicationInitializer
接口来实现。
接下来,我们定义一个新的 servlet:
- package com.dxz.mvcdemo1.config.servlet;
- import javax.servlet.ServletContext;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRegistration.Dynamic;
- import org.springframework.web.WebApplicationInitializer;
- public class MyServletInitializer implements WebApplicationInitializer {
- public void onStartup(ServletContext servletContext) throws ServletException {
- //定义额外的 servlet
- Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
- myServlet.addMapping("/custom/**");
- //定义filter
- javax.servlet.FilterRegistration.Dynamic filter =
- servletContext.addFilter("myFilter", MyFilter.class);
- filter.addMappingForUrlPatterns(null, false, "/custom/*");
- }
- }
当然,你也可以用来定义 filters 和 listeners:
如上面的红底部分。
在AbstractAnnotationConfigDispatcherServletInitializer
中还有一种快捷方式:
如果你需要为 DispatcherServlet
添加 filter 的话,就不用这么麻烦了,你只要重写 AbstractAnnotationConfigDispatcherServletInitializer
类的 getServletFilters()
方法就行了:
- @Override
- protected Filter[] getServletFilters() {
- return new Filter[] { new MyFilter() };
- }
不需要 mapping,因为会自动 mapping 到 DispatcherServlet
上,通过返回多个 filter,可以添加多个 filter。
SpringMVC之五:自定义DispatcherServlet配置及配置额外的 servlets 和 filters的更多相关文章
- 《SpringMVC从入门到放肆》三、DispatcherServlet的url-pattern配置详解
上一篇我们详细解释了一下SrpingMVC的执行流程以及一些默认的配置,在Spring的思想中,就是默认大于配置.今天我们来详细的研究一下DispatcherServlet的url-pattern配置 ...
- springmvc 项目完整示例07 设置配置整合springmvc springmvc所需jar包springmvc web.xml文件配置
前面主要是后台代码,spring以及mybatis的整合 下面主要是springmvc用来处理请求转发,展现层的处理 之前所有做到的,完成了后台,业务层和持久层的开发完成了 接下来就是展现层了 有很多 ...
- spring-mvc不拦截静态资源的配置
spring-mvc不拦截静态资源的配置 标签: spring 2015-03-27 23:54 11587人阅读 评论(0) 收藏 举报 版权声明:本文为博主原创文章,未经博主允许不得转载. &qu ...
- springmvc国际化 基于请求的国际化配置
springmvc国际化 基于请求的国际化配置 基于请求的国际化配置是指,在当前请求内,国际化配置生效,否则自动以浏览器为主. 项目结构图: 说明:properties文件中为国际化资源文件.格式相关 ...
- SpringMVC整合mybatis基于纯注解配置
Mybatis整合Spring配置 第一部分:配置Spring框架 配置SpringMVC的步骤 配置流程图 导入包(哪些包,基本包5个,1日志依赖包,2webmvc支持包)SpringMVC配置 & ...
- 使用IntelliJ IDEA开发SpringMVC网站(二)框架配置
原文:使用IntelliJ IDEA开发SpringMVC网站(二)框架配置 摘要 讲解如何配置SpringMVC框架xml,以及如何在Tomcat中运行 目录[-] 文章已针对IDEA 15做了一定 ...
- spring 和springmvc 在 web.xml中的配置
(1)问题:如何在Web项目中配置Spring的IoC容器? 答:如果需要在Web项目中使用Spring的IoC容器,可以在Web项目配置文件web.xml中做出如下配置: <!-- Sprin ...
- springmvc+spring-data-jpa+hibernate环境搭建与配置
1.JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现.在ORM框架中,Hibernate是一支 ...
- spring-mvc.xml 和 application-context.xml的配置与深入理解
在java框架这个话题,前几篇文章是基于搭建ssm项目框架,以及web.xml的配置讲解,本篇主要就ssm框架的其他配置文件进行深入讲解,他们分别是:1.application-context.xml ...
随机推荐
- jQuery实现点击式选项卡
参考:jQuery权威指南jQuery初步jQuery选择器jQuery操作domjQuery操作dom事件jQuery插件jQuery操作AjaxjQuery动画与特效jQuery实现导航栏jQue ...
- vux配置i18n
根据使用文档,先引入i18n import VueI18n from 'vue-i18n'; Vue.use(VueI18n) const i18n = new VueI18n({ locale: ' ...
- Hibernate主键生成策略详解
转载自:http://blog.csdn.net/wanghuan203/article/details/7562395 hibernate提供的主键生成策略,使我们可以在实体类的映射xml文件中设定 ...
- DR模式下的高可用的LVS(LVS+keepalived)
一.keepalived 在DR模式下,使用Keepalived实现LVS的高可用.Keepalived的作用是检测服务器的状态,如果有一台web服务器 宕机,或工作出现故障,Keepalived将检 ...
- 【SQL查询】正则表达式匹配字符串
1. 元字符说明 元字符 含义 ^ 匹配输入字符串的开始位置. $ 匹配输入字符串的结尾位置. * 匹配前面的字符零次或多次. + 匹配前面的字符一次或多次. ? 匹配前面的字符零次或一次. . 匹配 ...
- 【PL/SQL编程】数据类型说明
1. 数值类型 数值类型主要包括NUMBER.PLS_INTEGER.和BINARY_INTEGER 3种基本类型.NUMBER可以用来存储整数或浮点数,PLS_INTEGER和BINARY_INTE ...
- Lodash 浓缩
Lodash 是个十分有用的工具库,但我们往往只需要其中的一小部分函数.这时,整个 lodash 库就显得十分庞大,我们需要减小 lodash 的体积. cherry-pick 方法 Lodash 官 ...
- React-Native基础_1.初识React-Native项目
1.初识React-Native项目 'use strict'//使用严格模式 import React, { Component } from 'react';//导入React的Component ...
- 实现Python代码发送邮件
import smtplib from email.mime.text import MIMEText from email.utils import formataddr msg = MIMETex ...
- Learning Scrapy(一)
学习爬虫有一段时间了,从Python的Urllib.Urlllib2到scrapy,当然,scrapy的性能且效率是最高的,自己之前也看过一些资料,在此学习总结下. Scrapy介绍 关于scrapy ...