关于SSM以及Spring boot中对于Spring MVC配置的问题

SSM中 Spring MVC配置

传统的web.xml配置

web.xml

<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
    <!-- 配置Spring监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 添加对SpringMVC的支持 -->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 启用annotation -->
    <annotation-driven/>
    <!-- spring扫描的包 -->
    <context:component-scan base-package="spittr.web"/>
    <!-- DispatcherServlet不处理静态资源,交给服务器默认的servlet处理 -->
    <default-servlet-handler/>

    <!-- 视图渲染器 -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
                id="internalResourceViewResolver">
        <!-- 前缀 -->
        <beans:property name="prefix" value="/WEB-INF/views/"/>
        <!-- 后缀 -->
        <beans:property name="suffix" value=".jsp"/>
    </beans:bean>

</beans:beans>

基于java配置的方式

现在JavaConfig配置方式在逐步取代xml配置方式。而WebApplicationInitializer可以看做是Web.xml的替代,它是一个接口。通过实现WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml相同的作用。

SpittrWebAppInitializer 主配置类

//扩展自Abstrac~Initializer的任意类,都会自动地配置Dispatcher-Servlet和Spring应用上下文
//spring的应用上下文会位于程序的Servlet上下文之中
public class SpittrWebAppInitializer  extends
        AbstractAnnotationConfigDispatcherServletInitializer {

   @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    //映射“/”,表示会使用默认的Servlet
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // TODO Auto-generated method stub
        return null;
    }
    
    @Override
    protected Filter[] getServletFilters() {
        final CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        return new Filter[] { encodingFilter };
    }

}

我们创建的SpittrWebAppInitializer这个类是继承了
AbstractAnnotationConfigDispatcherServletInitializer,其继承关系为:

AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer implements WebApplicationInitializer

AbstractDispatcherServletInitializer对DispatcherServlet进行了自动配置和初始化;AbstractContextLoaderInitializer初始化和配置了Spring应用的上下文。因此,任意继承自这个类的类都会通过创建DispatcherServlet和ContextLoaderListener,自动配置DispatcherServlet和Spring应用上下文,但是真正完成配置上下文的是WebApplicationInitializer接口。

WebApplicationInitializer接口

WebApplicationInitializer接口是如何完成配置的呢?其只有一个方法onStartup,看不出什么头绪。但是,在这个包下有另外一个类,SpringServletContainerInitializer。

public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;

}
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @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);
        }
    }

}

SpringServletContainerInitializer实现了ServletContainerInitializer接口,其在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将HandlesTypes指定的类或者实现该接口的类注入到SpringServletContainerInitializer的onStartup方法作为参数传入。

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方法,并将感兴趣的类作为参数传入。

Spring Web中通常会有两种应用上下文,一种是Spring MVC上下文,这种上下文通过DispatcherServlet加载,对应上边的getServletConfigClasses()方法,另一种上下文是spring容器本身的上下文,就要通过ContextLoaderListerner创建,对应的是方法getRootConfigClasses()

WebConfig.java 类

@Configuration //标明了该类是一个配置类并且会将该类作为一个SpringBean添加到IOC容器内
@EnableWebMvc
//通过查看@EnableWebMvc的源码,可以发现该注解就是为了引入一个DelegatingWebMvcConfiguration Java 配置类。并翻看DelegatingWebMvcConfiguration的源码会发现该类似继承于WebMvcConfigurationSupport的类。
@ComponentScan("spitter.web")
public class WebConfig extends WebMvcConfigurerAdapter {

    /**
     * 配置JSP视图解析器,他会查找jsp文件,在查找的时候
     * 他会在视图名称上加一个特定的前缀和后缀
     * home的视图——解析成为/WEB-INF/views/home.jsp
     * @return
     */
    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver=
                new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    /**
     * 通过调用enable方法,我们要求DispatcherServelet将
     * 对静态资源的请求转发到Servlet容器中的默认的Servlet上,
     * 不是DispatcherServelet本身处理
     * @param configurer
     */
    public void configureDefaultServleHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
}

WebConfig中的配置其实就是对应web.xml中spring-mvc.xml的配置。@EnableWebMvc注解内部使用了@Import(DelegatingWebMvcConfiguration.class),其作用是会把WebMvcConfigurationSupport当成配置文件来用,将其中所有标识有@Bean注解的方法配置成bean,这就成了Spring mvc的默认配置。

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
    ....//省略其他方法
}

DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport类,其setConfigurers()方法在覆盖父类的方法之前,它会寻找容器中所有的WebMvcConfigurer实现类,将所有WebMvcConfigurer实现类中的配置组合起来,组成一个超级配置(WebMvcConfigurerAdapter是WebMvcConfigurer的实现类)。这样,WebMvcConfigurationSupport中的bean发布时,就会把这所有配置都带上了。

WebMvcConfigurer接口提供的功能如下表所示:

配置接口 接口说明
configurePathMatch 配置HandlerMapping路径匹配参数
configureContentNegotiation 配置路径到请求内容类型转换的相关参数,如.pdf结尾的请求解析成PDF类型或者其它等
configureAsyncSupport 配置异步请求处理相关参数
configureDefaultServletHandling 配置是否需要以下功能:如果一个请求没有被任何Handler处理,那是否使用DefaultServletHttpRequestHandler来进行处理?
addFormatters 增加额外的Converter和Formatter
addInterceptors 增加拦截器
addResourceHandlers 增加处理静态资源的Handler
addCorsMappings 配置跨域请求相关参数
addViewControllers 使用特殊的Controller来处理指定的URL请求;
configureViewResolvers 配置将Controller返回的视图名称转换成视图的视图解析器; 以便进行视图渲染
addArgumentResolvers 添加支持个性化配置Controller的方法参数类型的Resolver。
addReturnValueHandlers 添加支持个性化处理Controller返回数据类型的处理器;
configureMessageConverters 配置消息转换器;
extendMessageConverters 扩展消息转换器
configureHandlerExceptionResolvers 配置异常处理器
extendHandlerExceptionResolvers 扩展异常处理器
注意:
    spring-webmvc 从5.0开始已经废除了WebMvcConfigurerAdapter类,对于spring mvc的配置可以通过直接实现WebMvcConfigurer接口来实现。
    
    public class WebConfig implements WebMvcConfigurer 

Spring boot

在spring boot中通过WebMvcAutoConfiguration自动配置类已经将配置的大部分工作完成了,可以简单的认为,WebMvcAutoConfiguration完成了之前SpittrWebAppInitializer和WebConfig的工作,提供适用于多数应用的自动配置功能。自动配置添加了以下特性:

  1. 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
  2. 对静态资源的支持,包括对WebJars的支持。
  3. 自动注册Converter,GenericConverter,Formatter beans。
  4. 对HttpMessageConverters的支持。
  5. 自动注册MessageCodeResolver。
  6. 对静态index.html的支持。
    如果想了解详细信息参考:https://blog.csdn.net/qq_26000415/article/details/78998669

一般情况下是不需要改动mvc的配置的,但是如果需要添加其他mvc配置,有两种方法:

  1. 全面弃用spring boot的自动配置

    1. 直接继承WebMvcConfigurationSupport在扩展的类中重写父类的方法
    2. 使用注解@Configuration + @EnableWebMvc,并继承WebMvcConfigurationAdapter,重写父类的方法
  2. 在spring boot自动配置的基础上添加部分配置
    继承WebMvcConfigurationAdapter,在扩展的类中重写父类的方法

注意:
    spring boot 从2.0使用spring-webmvc 5.0因此继承WebMvcConfigurationAdapter需要替换为实现WebMvcConfigurer接口 
    
    public class WebConfig implements WebMvcConfigurer 

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容