Spring MVC AbstractAnnotationConfigDispatcherServletInitializer 用于 DispatcherServlet 初始化
DispatcherServlet 是 Spring MVC 的核心组件,它是一个 request 首先到达的地方,负责 request 在其他各个组件间的传递加工,在过去,像 DispatcherServlet 这样的 servlets 是使用 web.xml 文件配置的。
基于 Servlet 3 和 Spring 3.1 的一些新特性,我们可以用更简单的方式来配置,即使用 Java 代码。
简单来说,AbstractAnnotationConfigDispatcherServletInitializer 自动被加载,负责应用程序中 servlet 上下文中的 DispatcherServlet 和 Spring 其他上下文的配置。
Spring MVC提供基类 AbstractAnnotationConfigDispatcherServletInitializer,用于 DispatcherServlet 初始化(实现了WebApplicationInitializer接口),该基类既要完成 WebApplicationInitializer 接口中配置servlet容器的功能,又完成了配置MVC的功能,即同时配置了 DispatcherServlet 和 ContextLoaderListener 。
package com.it.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { /* 加载 Spring 配置类中的信息, 初始化 Spring 容器 */ protected Class<?>[] getRootConfigClasses() { //配置root上下文,如Jpa数据源等等的配置
return new Class[]{SpringConfig.class}; }
/* 加载 Spring MVC 配置类中的信息, 初始化 Spring MVC 容器 */ protected Class<?>[] getServletConfigClasses() {//配置dispatcher servlet,如果在root config指定了该转发规则就可以忽略
return new Class[]{SpringMvcConfig.class}; }
//配置 DispatcherServlet 的映射路径 //指定开始被 servlet 处理的url,配置从 / 开始
protected String[] getServletMappings() { return new String[]{"/"}; } }
对 getServletMappings() 的进一步说明:
通常情况下,大家配置spring mvc的路径拦截(ServletMapping)都习惯配置“/”,但在某些特殊场景下,你可能希望所有的mvc的路径都有个固定的前缀,形如“/springmvc/*”,如果你在springmvc的ServletMapping中配置了这种路径,那么你的URL访问地址和controller的path就要注意了:如果你的地址栏URL为"/springmvc/user",那么你的controller中的path就必须配置"/user",因为spring mvc拦截了所有以“/springmvc”为前缀的请求,而在匹配的时候也截掉了“/springmvc”,用剩下的“/user”匹配controller。
如果路径设置为“/”,则所有的请求都会由DispatcherServlet处理。
如何取代传统的 web.xml 搭建Spring MVC,DispatcherServlet 是 Spring MVC 的核心,我们都会把它放在 web.xml 文件中,但是自从有了 Servlet 3 规范和 Spring 3.1 的功能增强,这种方式就不是唯一的方案了.
一、AbstractAnnotationConfigDispatcherServletInitializer
要想替代 web.xml 中的 DispatcherServlet,我们就需要扩展 AbstractAnnotationConfigDispatcherServletInitializer,任意的类扩展它后,都会自动的配置 DispatcherServlet 和 Spring 应用上下文。
注:在Spring 3.0环境中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer 接口的类,如果能发现的话,就会用它来配置 Servlet 容器。Spring提供了这个接口的实现,名为 SpringServletContainerInitializer。这个类反过来会查找实现 WebApplicationInitializer 的类并将配置的任务交给它们来完成。
Spring 3.2 引入了一个便利的WebApplicationInitializer基础实现,也就是 AbstractAnnotationConfigDispatcherServletInitializer ,如下面的图片,DispatcherServlet 实现了 AbstractAnnotationConfigDispatcherServletInitializer,因此当部署到Servlet 3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。
类中有3个主要方法,getServletMappings, getRootConfigClasses, getServletConfigClasses。
getServletConfigClasses 用来加载配置文件或配置类中所声明的bean。
getRootConfigClasses 用来加载ContextLoaderListener要加载的bean。
getServletMappings 用来定义请求URL
二、WebMvcConfigurerAdapter
要想实现视图解析、我们还需要扩展WebMvcConfigurerAdapter,使用@EnableWebMvc启用Spring MVC组件。
viewResolver用来配置JSP视图解析器。
configureDefaultServletHandling配置静态资源的处理.
通过配置configureDefaultServletHandling的enable方法,我们要求DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身处理请求。
三、Controller
这个大家都熟悉也不用细说了。
最后奉上整个项目截图
示例:
package org.springframework.source.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.*; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //指定开始被servlet处理的url,配置从/开始 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //配置root上下文,如Jpa数据源等等的配置 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] {ApplicationConfig.class, JpaConfig.class, SecurityConfig.class}; } //配置dispatcher servlet,如果在root config指定了该转发规则就可以忽略 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] {WebMvcConfig.class}; } //配置servlet过滤器 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); characterEncodingFilter.setForceEncoding(true); DelegatingFilterProxy securityFilterChain = new DelegatingFilterProxy("springSecurityFilterChain"); return new Filter[] {characterEncodingFilter, securityFilterChain}; } //当registerDispatcherServlet完成时自定义registration @Override protected void customizeRegistration(ServletRegistration.Dynamic registration) { registration.setInitParameter("defaultHtmlEscape", "true"); registration.setInitParameter("spring.profiles.active", "default"); } }
=======================================================
AbstractAnnotationConfigDispatcherServletInitializer 剖析
在Servlet 3.0环境中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer 接口的类,如果能发现的话,就会用它来配置Servlet容器。
Spring提供了这个接口的实现,名为 SpringServletContainerInitializer,这个类反过来又会查找实现 WebApplicationInitializer 的类并将配置的任务交给它们来完成。Spring 3.2 引入了一个便利的 WebApplicationInitializer 基础实现,也就是 AbstractAnnotationConfigDispatcherServletInitializer。因为我们的
MyWebAppInitializer 扩展了 AbstractAnnotationConfigDispatcherServletInitializer,当然也就实现了 WebApplicationInitializer,因此当部署到Servlet 3.0 容器中的时候,容器会自动发现它,并用它来配置 Servlet上下文。
尽管它的名字很长,但是 AbstractAnnotationConfigDispatcherServletInitializer 使用起来很简便。它仅要求我们重写其中的三个方法,其他的方法是否重写则根据你的具体需求而定。
第一个方法是getServletMappings(),它会将一个或多个路径映射到 DispatcherServlet 上。在本例中,它映射的是“/”,这表示它会是应用的默认Servlet。它会处理进入应用的所有请求。
为了理解其他的两个方法,我们首先要理解 DispatcherServlet 和一个 Servlet 监听器,也就是 ContextLoaderListene 的关系。
两个应用上下文之间的故事:
当 DispatcherServlet 启动的时候,它会创建 Spring应用上下文,并加载配置文件或配置类中所声明的bean。在 MyWebAppInitializer 的 getServletConfigClasses() 方法中,我们要求 DispatcherServlet 加载应用上下文时,使用定义在 WebConfig配置类(使用Java配置)中的bean。但是在Spring Web 应用中,通常还会有另外一个应用上下文。另外的这个应用上下文是由 ContextLoaderListener 创建的。
我们希望 DispatcherServlet 加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而 ContextLoaderListener 要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。
实际上,AbstractAnnotationConfigDispatcherServletInitializer 会同时创建 DispatcherServlet 和 ContextLoaderListener。getServletConfigClasses() 方法返回的带有@Configuration注解的类将会用来定义 DispatcherServlet 应用上下文中的bean,我们暂且把它记为context1。getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置 ContextLoaderListener 创建的应用上下文中的bean,记为context2。那这两个上下文的关系是什么呢?答案是,context1会把context2设置为parent,这样,当context1中的bean需要使用到context2中的bean时就可以在其中直接获取,比如当我们把一个service层的bean注入到controller中时。
在本例中,根配置定义在RootConfig中,DispatcherServlet 的配置声明在WebConfig中。稍后我们将会看到这两个类的内容。
需要注意的是,通过 AbstractAnnotationConfigDispatcherServletInitializer 来配置 DispatcherServlet 是传统web.xml方式的替代方案。如果你愿意的话,可以同时包含 web.xml和 AbstractAnnotationConfigDispatcherServletInitializer,但这其实并没有必要。
如果按照这种方式配置 DispatcherServlet,而不是使用web.xml的话,那唯一问题在于它只能部署到支持 Servlet 3.0的服务器中才能正常工作,如 Tomcat 7或更高版本。如果你还没有使用支持 Servlet 3.0的服务器,那么在 AbstractAnnotationConfigDispatcherServletInitializer 子类中配置 DispatcherServlet 的方法就不适合你了。你别无选择,只能使用web.xml了。
-----------------------------------
https://blog.51cto.com/u_9587581/2398187
=======================================================
REF
https://blog.51cto.com/u_9587581/2398187
https://baijiahao.baidu.com/s?id=1633224433148237378&wfr=spider&for=pc
https://blog.csdn.net/renchenglin118/article/details/93207031
https://blog.csdn.net/u013571243/article/details/44565289
https://blog.csdn.net/classicer/article/details/50753019