DispatcherServlet是SpringMVC的核心,担任着请求分发的责任。
在SpringMVC中配置DispatcherServlet有两种方式:
1. 在Servlet容器的web.xml文件中配置;
2. 用Java代码将DispatcherServlet配置到Servlet容器中
文本主要讲解一下第二种配置方法
首先直接看在代码中如何配置DispatcherServlet
package com.windcloud.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class FyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
//指定配置类
@Override
protected Class>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
//将DispatcherServlet映射到“/”
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
通过扩展AbstractAnnotationConfigDispatcherServletInitializer抽象类,可以将DispatcherServlet和Spring应用上下文配置到Servlet容器中。其原理如下:
在Servlet3.0的环境中,即如果Servlet容器遵循的是Servlet3.0接口,那么Servlet容器会在类路径中寻找实现javax.servlet.ServletContainerInitializer接口的类,然后用它来配置Servlet容器。
幸运的是Spring已经为我们提供了这个接口的实现类,名为SpringServletContainerInitializer,在这个类中又会查找WebApplicationInitializer接口的实现类,然后将配置的任务交给这个接口的实现类来完成。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List initializers = new LinkedList<>();
......//查找WebApplicationInitializer的实现类,然后放入initializers中
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
从上面的源码中可以看到,在WebApplicationInitializer接口的onStartup方法中实现在servletContext中加载Servlet.
在Spring3.2中引入了WebApplicationInitializer接口的基础实现类(这是一个抽象类),就是AbstractAnnotationConfigDispatcherServletInitializer.但是AbstractAnnotationConfigDispatcherServletInitializer并不是直接实现WebApplicationInitializer接口的,它的实现路径如下:
WebApplicationInitializer ---> AbstractContextLoaderInitializer ---> AbstractDispatcherServletInitializer ---> AbstractAnnotationConfigDispatcherServletInitializer
在AbstractAnnotationConfigDispatcherServletInitializer中,给我们留下了三个抽象方法要求我们去实现:
所以我们需要写一个继承AbstractAnnotationConfigDispatcherServletInitializer的配置类,如开头的FyWebAppInitializer所示。
下面着重解释一下getRootConfigClasses()和getServletConfigClasses()方法。
在解释这两个方法之前,我们需要知道Spring应用上下文的一些基础知识。
Spring中通常由两个应用上下文:
在创建这个上下文的时候会加载配置文件或配置类中声明的bean,getServletConfigClasses返回的配置类(如案例中的WebConfig类)中配置的bean会在这个时候被加载到该上下文中
方法getRootConfigClasses返回的配置类(如案例中的RootConfig类)中配置的bean会被加载到这个上下文中
上面两个上下文的区别是:上下文一中加载的通常是Web组件的bean,如控制器、视图解析器、处理器映射器等。而上下文二中加载的往往是一些中间层和数据层的组件。
其实,在AbstractAnnotationConfigDispatcherServletInitializer中会同时创建DispatcherServlet和ContextLoaderListener.
下面分别给出示例中WebConfig和RootConfig的实现
WebConfig 实现如下:
package com.windcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc //启用SpringMVC
@ComponentScan("com.windcloud.config") //启用组件扫描
public class WebConfig extends WebMvcConfigurerAdapter {
//配置JSP视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
//配置静态资源的处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
RootConfig 实现如下:
package com.windcloud.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = { "com.windcloud.controller" },
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)})
public class RootConfig {
}
以上就是关于DispatcherServlet在代码中的配置过程。
这是一个完全通过Java代码将DispatcherServlet和ContextLoaderListener配置到Servlet容器中的方法。
1. 上述的在代码中配置DispatcherServlet只能在支持Servlet3.0及以上版本的服务器中才可以使用,如Tomcat7及以上,如果版本低于Servlet3.0,那只能在web.xml中配置了
2. 配置DispatcherServlet有三个步骤:
上述通过继承Spring已经为我们设计好的抽象类AbstractAnnotationConfigDispatcherServletInitializer类,我们只需要实现其中的三个抽象方法即可。其实在AbstractAnnotationConfigDispatcherServletInitializer类中,Spring已经将DispatcherServlet和ContextLoaderListener加载到了Servlet容器中了。但是如果我们想自己再添加其它的Servlet或Filter到Servlet容器中,或者想对DispatcherServlet增加一些初始化配置时该怎么办呢?
下面分别介绍一下:
在上文中,我们只对AbstractAnnotationConfigDispatcherServletInitializer类中的三个抽象方法进行了实现,如果想进一步设置DispatcherServlet,可以对AbstractAnnotationConfigDispatcherServletInitializer类中的其它方法进行重写。
例如,其中一个方法就是customizeRegistration(ServletRegistration.Dynamic registration),在AbstractAnnotationConfigDispatcherServletInitializer类将DispatcherServlet注册到Servlet容器中后,会调用customizeRegistration(ServletRegistration.Dynamic registration)方法,假设我们想让DispatcherServlet支持multipart请求,那么可以通过重写customizeRegistration方法来实现。
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
//将上传的文件临时存放到/tmp/fy/uploads目录中
registration.setMultipartConfig(new MultipartConfigElement("/tmp/fy/uploads"));
}
除了上面的配置支持multipart请求,还可以通过registration.setInitParameters(Map
通过学习上面DispatcherServlet的配置,我们可以想到如果想自己配置Servlet到Servlet容器中,我们需要自己写一个WebApplicationInitializer接口的实现类即可(称为spring的初始化器),即自己创建一个初始化器。
如下就是一个将自定义的Servlet加载到Servlet容器中的初始化器:
public class MyServletInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
myServlet.addMapping("custom/**");
}
}
如果需要将自定义的Filter或Listener加载到Servlet容器中的初始化器与Servlet类似:
public class MyFilterInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class);
filter.addMappingForUrlPatterns(null, false, "custom/*");
}
}