關(guān)于SSM以及Spring boot中對(duì)于Spring MVC配置的問題

SSM中 Spring MVC配置

傳統(tǒng)的web.xml配置

web.xml

<!-- 指定Spring Bean的配置文件所在目錄。默認(rèn)配置在WEB-INF目錄下 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
    <!-- 配置Spring監(jiān)聽器 -->
    <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>

    <!-- 添加對(duì)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不處理靜態(tài)資源,交給服務(wù)器默認(rèn)的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配置的方式

現(xiàn)在JavaConfig配置方式在逐步取代xml配置方式。而WebApplicationInitializer可以看做是Web.xml的替代,它是一個(gè)接口。通過實(shí)現(xiàn)WebApplicationInitializer,在其中可以添加servlet,listener等,在加載Web項(xiàng)目的時(shí)候會(huì)加載這個(gè)接口實(shí)現(xiàn)類,從而起到web.xml相同的作用。

SpittrWebAppInitializer 主配置類

//擴(kuò)展自Abstrac~Initializer的任意類,都會(huì)自動(dòng)地配置Dispatcher-Servlet和Spring應(yīng)用上下文
//spring的應(yīng)用上下文會(huì)位于程序的Servlet上下文之中
public class SpittrWebAppInitializer  extends
        AbstractAnnotationConfigDispatcherServletInitializer {

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

    //映射“/”,表示會(huì)使用默認(rèn)的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 };
    }

}

我們創(chuàng)建的SpittrWebAppInitializer這個(gè)類是繼承了
AbstractAnnotationConfigDispatcherServletInitializer,其繼承關(guān)系為:

AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer implements WebApplicationInitializer

AbstractDispatcherServletInitializer對(duì)DispatcherServlet進(jìn)行了自動(dòng)配置和初始化;AbstractContextLoaderInitializer初始化和配置了Spring應(yīng)用的上下文。因此,任意繼承自這個(gè)類的類都會(huì)通過創(chuàng)建DispatcherServlet和ContextLoaderListener,自動(dòng)配置DispatcherServlet和Spring應(yīng)用上下文,但是真正完成配置上下文的是WebApplicationInitializer接口。

WebApplicationInitializer接口

WebApplicationInitializer接口是如何完成配置的呢?其只有一個(gè)方法onStartup,看不出什么頭緒。但是,在這個(gè)包下有另外一個(gè)類,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實(shí)現(xiàn)了ServletContainerInitializer接口,其在web容器啟動(dòng)時(shí)為提供給第三方組件機(jī)會(huì)做一些初始化的工作,例如注冊(cè)servlet或者filtes等,servlet規(guī)范中通過ServletContainerInitializer實(shí)現(xiàn)此功能。每個(gè)框架要使用ServletContainerInitializer就必須在對(duì)應(yīng)的jar包的META-INF/services 目錄創(chuàng)建一個(gè)名為javax.servlet.ServletContainerInitializer的文件,文件內(nèi)容指定具體的ServletContainerInitializer實(shí)現(xiàn)類,那么,當(dāng)web容器啟動(dòng)時(shí)就會(huì)運(yùn)行這個(gè)初始化器做一些組件內(nèi)的初始化工作。

一般伴隨著ServletContainerInitializer一起使用的還有HandlesTypes注解,通過HandlesTypes可以將HandlesTypes指定的類或者實(shí)現(xiàn)該接口的類注入到SpringServletContainerInitializer的onStartup方法作為參數(shù)傳入。

Tomcat容器的ServletContainerInitializer機(jī)制的實(shí)現(xiàn),主要交由Context容器和ContextConfig監(jiān)聽器共同實(shí)現(xiàn),ContextConfig監(jiān)聽器負(fù)責(zé)在容器啟動(dòng)時(shí)讀取每個(gè)web應(yīng)用的WEB-INF/lib目錄下包含的jar包的META-INF/services/javax.servlet.ServletContainerInitializer,以及web根目錄下的META-INF/services/javax.servlet.ServletContainerInitializer,通過反射完成這些ServletContainerInitializer的實(shí)例化,然后再設(shè)置到Context容器中,最后Context容器啟動(dòng)時(shí)就會(huì)分別調(diào)用每個(gè)ServletContainerInitializer的onStartup方法,并將感興趣的類作為參數(shù)傳入。

Spring Web中通常會(huì)有兩種應(yīng)用上下文,一種是Spring MVC上下文,這種上下文通過DispatcherServlet加載,對(duì)應(yīng)上邊的getServletConfigClasses()方法,另一種上下文是spring容器本身的上下文,就要通過ContextLoaderListerner創(chuàng)建,對(duì)應(yīng)的是方法getRootConfigClasses()

WebConfig.java 類

@Configuration //標(biāo)明了該類是一個(gè)配置類并且會(huì)將該類作為一個(gè)SpringBean添加到IOC容器內(nèi)
@EnableWebMvc
//通過查看@EnableWebMvc的源碼,可以發(fā)現(xiàn)該注解就是為了引入一個(gè)DelegatingWebMvcConfiguration Java 配置類。并翻看DelegatingWebMvcConfiguration的源碼會(huì)發(fā)現(xiàn)該類似繼承于WebMvcConfigurationSupport的類。
@ComponentScan("spitter.web")
public class WebConfig extends WebMvcConfigurerAdapter {

    /**
     * 配置JSP視圖解析器,他會(huì)查找jsp文件,在查找的時(shí)候
     * 他會(huì)在視圖名稱上加一個(gè)特定的前綴和后綴
     * 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;
    }

    /**
     * 通過調(diào)用enable方法,我們要求DispatcherServelet將
     * 對(duì)靜態(tài)資源的請(qǐng)求轉(zhuǎn)發(fā)到Servlet容器中的默認(rèn)的Servlet上,
     * 不是DispatcherServelet本身處理
     * @param configurer
     */
    public void configureDefaultServleHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
}

WebConfig中的配置其實(shí)就是對(duì)應(yīng)web.xml中spring-mvc.xml的配置。@EnableWebMvc注解內(nèi)部使用了@Import(DelegatingWebMvcConfiguration.class),其作用是會(huì)把WebMvcConfigurationSupport當(dāng)成配置文件來用,將其中所有標(biāo)識(shí)有@Bean注解的方法配置成bean,這就成了Spring mvc的默認(rèn)配置。

@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()方法在覆蓋父類的方法之前,它會(huì)尋找容器中所有的WebMvcConfigurer實(shí)現(xiàn)類,將所有WebMvcConfigurer實(shí)現(xiàn)類中的配置組合起來,組成一個(gè)超級(jí)配置(WebMvcConfigurerAdapter是WebMvcConfigurer的實(shí)現(xiàn)類)。這樣,WebMvcConfigurationSupport中的bean發(fā)布時(shí),就會(huì)把這所有配置都帶上了。

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

配置接口 接口說明
configurePathMatch 配置HandlerMapping路徑匹配參數(shù)
configureContentNegotiation 配置路徑到請(qǐng)求內(nèi)容類型轉(zhuǎn)換的相關(guān)參數(shù),如.pdf結(jié)尾的請(qǐng)求解析成PDF類型或者其它等
configureAsyncSupport 配置異步請(qǐng)求處理相關(guān)參數(shù)
configureDefaultServletHandling 配置是否需要以下功能:如果一個(gè)請(qǐng)求沒有被任何Handler處理,那是否使用DefaultServletHttpRequestHandler來進(jìn)行處理?
addFormatters 增加額外的Converter和Formatter
addInterceptors 增加攔截器
addResourceHandlers 增加處理靜態(tài)資源的Handler
addCorsMappings 配置跨域請(qǐng)求相關(guān)參數(shù)
addViewControllers 使用特殊的Controller來處理指定的URL請(qǐng)求;
configureViewResolvers 配置將Controller返回的視圖名稱轉(zhuǎn)換成視圖的視圖解析器; 以便進(jìn)行視圖渲染
addArgumentResolvers 添加支持個(gè)性化配置Controller的方法參數(shù)類型的Resolver。
addReturnValueHandlers 添加支持個(gè)性化處理Controller返回?cái)?shù)據(jù)類型的處理器;
configureMessageConverters 配置消息轉(zhuǎn)換器;
extendMessageConverters 擴(kuò)展消息轉(zhuǎn)換器
configureHandlerExceptionResolvers 配置異常處理器
extendHandlerExceptionResolvers 擴(kuò)展異常處理器
注意:
    spring-webmvc 從5.0開始已經(jīng)廢除了WebMvcConfigurerAdapter類,對(duì)于spring mvc的配置可以通過直接實(shí)現(xiàn)WebMvcConfigurer接口來實(shí)現(xiàn)。
    
    public class WebConfig implements WebMvcConfigurer 

Spring boot

在spring boot中通過WebMvcAutoConfiguration自動(dòng)配置類已經(jīng)將配置的大部分工作完成了,可以簡(jiǎn)單的認(rèn)為,WebMvcAutoConfiguration完成了之前SpittrWebAppInitializer和WebConfig的工作,提供適用于多數(shù)應(yīng)用的自動(dòng)配置功能。自動(dòng)配置添加了以下特性:

  1. 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
  2. 對(duì)靜態(tài)資源的支持,包括對(duì)WebJars的支持。
  3. 自動(dòng)注冊(cè)Converter,GenericConverter,F(xiàn)ormatter beans。
  4. 對(duì)HttpMessageConverters的支持。
  5. 自動(dòng)注冊(cè)MessageCodeResolver。
  6. 對(duì)靜態(tài)index.html的支持。
    如果想了解詳細(xì)信息參考:https://blog.csdn.net/qq_26000415/article/details/78998669

一般情況下是不需要改動(dòng)mvc的配置的,但是如果需要添加其他mvc配置,有兩種方法:

  1. 全面棄用spring boot的自動(dòng)配置

    1. 直接繼承WebMvcConfigurationSupport在擴(kuò)展的類中重寫父類的方法
    2. 使用注解@Configuration + @EnableWebMvc,并繼承WebMvcConfigurationAdapter,重寫父類的方法
  2. 在spring boot自動(dòng)配置的基礎(chǔ)上添加部分配置
    繼承WebMvcConfigurationAdapter,在擴(kuò)展的類中重寫父類的方法

注意:
    spring boot 從2.0使用spring-webmvc 5.0因此繼承WebMvcConfigurationAdapter需要替換為實(shí)現(xiàn)WebMvcConfigurer接口 
    
    public class WebConfig implements WebMvcConfigurer 

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容