SpringMVC零xml配置原理


前言:

我們使用springmvc時,每次都要去配置web.xml,spring-mvc.xml,甚至和spring整合時候,還要配置spring.xml。用起來比較麻煩,用過springboot的朋友應該知道,springboot中使用springmvc時候就不會去指定xml。那這樣的操作是怎么實現的呢,下面我們就來探究下springmvc零配置xml原理。

 

不使用web.xml

1.實現WebApplicationInitializer接口

如果我們想實現不使用web.xml來配置需要的內容(注:此情況下web.xml可不配置內容,但不能刪除),關鍵在於實現WebApplicationInitializer。然后重寫接口中的onStartup()方法。

然后在這個重寫的方法內完成相應的配置,代碼如下所示。

import com.evan.config.AppConfig;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
 
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
 
//https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/web.html#mvc-servlet
public class MyWebApplicationInitializer implements WebApplicationInitializer {
  
    @Override
    public void onStartup(ServletContext servletCxt) {
        //初始化spring容器  以注解的方式
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        //將配置類注冊進spring容器
        ac.register(AppConfig.class);
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("*.do");
    }
}

不使用web.xml原因詳解

tomcat在啟動時候,就會調用到這個接口。

那么為什么tomcat啟動后就會調用到這個接口呢?下面請聽我慢慢介紹。

原因:實際上是因為servlet3.0的新規范,間接實現了ServletContainerInitializer接口。

Tomcat也遵守了這套規范,去找實現了ServletContainerInitializer接口的類,調用它的onStartUp方法。

springMVC遵守新規范,並且實現了SPI(相當於也實現了ServletContainerInitializer接口)。

 

那么什么是SPI機制呢?

SPI ,全稱為 Service Provider Interface,是一種服務發現機制。它通過在ClassPath路徑下的META-INF/services文件夾查找文件,自動加載文件里所定義的類。

了解了上面這些后,我們接着去看一下spring-web的jar包。在這里我們可以看到如SPI機制所描述的一樣,springMVC自己實現了這種SPI。

 

 然后我們繼續去看一下文件的內容,這里我們可以看到里面是一個類的全類名,接着我們點進去看一下這個類的詳細內容。

 

 

這里我們可以看到這個類,實現了ServletContainerInitializer接口。已經逐漸驗證了我們的結論。

接着再看,上面有一個@HandlesTypes注解。

那么這個注解有什么用呢?

作用:這個注解可以配置一個類,更准確來說是一個接口。假如現在我們有一個A接口,里面有一個test()方法,然后分別有B、C、D實現了A接口,

然后我們將A接口配置到@HandlesTypes(A.class)注解中。servlet將會掃描所有實現了A接口的類,即掃描到BCD,然后把他們

組裝成一個set作為參數傳入到重寫的onStartup()方法中。

 

 

然后會在onStartUp()方法中遍歷每一個A的實現類的test()方法,這樣就達到了在onStartup()中,調用這些方法的目的。

那么servlet為什么要這么做呢?

這就就不得不說這種成熟的經典框架中的設計思想多么優秀了。如果我們自己實現SPI的話,如果有10個類,都實現了

ServletContainerInitializer接口,我們需要為每一個接口都去寫一個javax.servlet.ServletContainerInitializer文件,然后在文件中,寫上我們實現了這個接口的類的全限定名。如果有100個我們要寫一百次,這樣會十分麻煩,而且這樣的設計在程序中是絕不允許的,所以我們要把這些操作抽象出來。從而這樣設計來達到目的。

不使用spring-mvc.xml

想不使用spring-mvc.xml更加簡單,只需要通過配置類實現WebMvcConfigurer接口,或繼承WebMvcConfigurationSupport類。

兩者沒有很大的區別,但是實現WebMvcConfigurer接口接口的話,要在配置類上加上@EnableWebMvc注解。

在里面我們可以做例如消息轉換器,視圖解析器,參數處理器等等一系列的配置。

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
 
import java.util.List;
 
@Configuration
@ComponentScan("com")
@EnableWebMvc  // <annotation:driver>
//https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/web.html#mvc-config
public class AppConfig implements WebMvcConfigurer {
 
    //這接口里面的方法貫穿了所有spring mvc的配置
 
//    @Bean
 
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
 
    }
 
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
 
    }
 
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
 
    }
 
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
 
    }
 
    @Override
    public void addFormatters(FormatterRegistry registry) {
 
    }
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
 
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
 
    }
 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
 
    }
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
 
    }
 
    /**
     * 在這里配置視圖解析器
     *
     * @param registry
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/page/", ".html");
    }
 
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
 
    }
 
    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
 
    }
 
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//        for (HttpMessageConverter<?> converter : converters) {
//            System.out.println(converter);
//        }
        //每個消息轉換器都會跑一遍
        //new一個新的解析器 添加到converters中。一般是在spring-mvc.xml中配置
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        converters.add(fastJsonHttpMessageConverter);
    }
 
    //剔除掉不想用的解析器
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
 
    }
 
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
 
    }
 
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
 
    }
 
    @Override
    public Validator getValidator() {
        return null;
    }
 
    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
 
 
//    @Bean
//    public TestController user(){
//         return  new TestController();
//    }
 
 
}

真正的不使用web.xml

之前的不使用web.xml的話,只是不在web.xml中配置內容,但並不能刪除web.xml,不然會報錯。因為之前是以web資源的形式去啟動項目,tomcat會去檢測是否存在web.xml。所以如果刪除的話是會報錯的。

但是我們都知道,springboot中是沒有web.xml的,也沒有報錯,

那是怎么實現的呢,答案是通過內嵌的tomcat。

spring是以一個啟動類,main方法作為程序入口,我們模擬一下。

在代碼中,指定了一個資源目錄是當前項目,獲取配置屬性指定為一個臨時目錄。

這樣的話就不再需要web.xml了,springboot也是通過這種方式實現了零xml。

但是,這種方式有個問題,就是無法訪問靜態資源,那么springboot是怎么實現靜態資源訪問的呢,我們后續會專門寫一篇文章解釋,在此先記錄一下。

下面代碼實現,通過這個main方法即可啟動web項目。

<!--使用maven tomcat插件時,當前依賴需要注釋掉,不然會產生沖突。-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.31</version>
        </dependency>

代碼

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Tomcat;
 
/**
 * @ClassName App
 * @Description
 * @Author EvanWang
 * @Version 1.0.0
 * @Date 2019/11/19 11:25
 */
public class App {
    public static void main(String[] args) throws Exception {
        //內嵌tomcat
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(80);
        //現在找不到靜態資源,如果指向我們當前項目,則可以訪問到靜態資源
        Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
        //addContext和addWebapp的區別:只會去初始化一個 context的資源目錄(項目) 並不會加載 web的生命周期
        // Tomcat的文件夾webapps,目錄內是我們的項目
        // 有兩種方式啟動項目:1.war  2.文件夾
        //tomcat.addWebapp("/","C:\\Program Files\\pro\\evan-project\\spring-mvc\\src\\main\\webapp");
        //手動添加生命周期監聽器
        context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
        tomcat.start();
        //掛起
        tomcat.getServer().await();
 
        //傳一個xml文件進去
//        ClassPathXmlApplicationContext
//                classPathXmlApplicationContext = new ClassPathXmlApplicationContext();
 
    }
}

 

 

版權聲明:本文為CSDN博主「Evan Wang」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_41378597/article/details/103173322



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM