Spring MVC AbstractAnnotationConfigDispatcherServletInitializer

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

原文地址:https://www.cnblogs.com/emanlee/p/15734426.html