日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

這節介紹SpringMVC,SpringMVC是一種基于JAVA的實現MVC設計模式的請求驅動類型的輕量級Web框架。本章會介紹相關概念,流程,再從源碼進行講解。

1. MVC

 MVC(Model View Controller)是一種軟件設計的框架模式,它采用模型(Model)-視圖(View)-控制器(controller)的方法把業務邏輯、數據與界面顯示分離。MVC框架模式是一種復合模式,MVC的三個核心部件分別是

  • Model(模型):所有的用戶數據、狀態以及程序邏輯,獨立于視圖和控制器
  • View(視圖):呈現模型,類似于Web程序中的界面,視圖會從模型中拿到需要展現的狀態以及數據,對于相同的數據可以有多種不同的顯示形式(視圖)
  • Controller(控制器):負責獲取用戶的輸入信息,進行解析并反饋給模型,通常情況下一個視圖具有一個控制器

2. SpringMVC流程

 基本上大家都會在網上看到這張圖:

SpringMVC加載流程

 

這個圖描述了SpringMVC處理一個Http請求的基本流程,對應的流程為:

  • 用戶發送請求至前端控制器DispatcherServlet。
  • DispatcherServlet收到請求調用HandlerMApping處理器映射器。
  • 處理器映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet。
  • DispatcherServlet調用HandlerAdapter處理器適配器。
  • HandlerAdapter經過適配調用具體的處理器(Controller,也叫后端控制器)。
  • Controller執行完成返回ModelAndView。
  • HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。
  • DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。
  • ViewReslover解析后返回具體View.
  • DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。
  • DispatcherServlet響應用戶。

 還有大家都會接觸到的demo:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

在applicationContext.xml,指定包的掃描訪問并添加標簽

<context:component-scan base-package="xxx" />

<mvc:annotation-driven />

添加Controller

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String excute() {
        return "hello";
    }
}

上面表示的意思為:

  • 開啟ContextLoaderListener加載Spring根Context,對應的配置文件為applicationContext.xml
  • 開啟DispatcherServlet監聽/*下的所有請求,加載WebContext,對應的配置文件為dispatcher-servlet.xml
  • 指定掃描包路徑,并開啟mvc注解支持
  • 添加對應的Controller,使用@RestController標記為Controller對象并使用@RequestMapping標記處理的請求路徑

3. SpringMVC加載流程

 SpringMVC的加載是依賴Servlet切入的,主要依賴兩個技術點:Listener和Servlet。

3.1. ContextLoaderListener的加載

 從web.xml中可以知道,ContextLoaderListener依賴于Servlet的Listener技術。Listener是在servlet2.3中加入的,主要用于對session、request、context等進行監控。使用Listener需要實現相應的接口。觸發Listener事件的時候,Tomcat會自動調用相應的Listener的方法。常用的監聽接口包括:

  • HttpSessionListener:監聽session的創建和銷毀。
  • ServletRequestListener:監聽request的創建和銷毀
  • ServletContextListener:監聽context的創建和銷毀。

這里主要使用了ServletContextListener,用于在Servlet初始化前執行自定義動作。

 ContextLoaderListener的定義如下:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

該類繼承了ContextLoader,并通過contextInitialized方法執行了初始化(傳入ServletContext),通過contextDestroyed方法進行資源銷毀回收。重點看ContextLoader方法。

 ContextLoader在初始化時,會先執行內部的一個靜態代碼塊:

private static final Properties defaultStrategies;

static {
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

這一步會加載classpath下的配置文件ContextLoader.properties,該文件將作為默認配置用于初始化Properties對象defaultStrategies。

3.1.1. contextInitialized

 contextInitialized方法的主要內容如下:

SpringMVC加載流程

 

過程為:

(1) 判斷當前Context是否已經初始化過

 通過判斷ServletContext中是否存在key為org.springframework.web.context.ROOT的值

  • 初始化WebApplicationContext:從ContextLoader.properties中查找WebApplicationContext的具體實現,如下:
 org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

即XmlWebApplicationContext,然后初始化該類

(2) 配置并刷新該XMLWebApplicationContext

 XMLWebApplicationContext繼承簡圖如下:

SpringMVC加載流程

 

層級比較明顯,本身也是一個RefreshableConfigApplicationContext(具體內容可以看往期內容)。其父類保存了ServletContext和ServletConfig兩個Web Context相關的對象,其本身也維持了一些默認屬性,如

DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

這個屬性就是默認的Spring配置文件的路徑。

 需要指出的是XMLWebApplicationContext重寫了父類的loadBeanDefinitions方法

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            reader.loadBeanDefinitions(configLocation);
        }
    }
}
    
@Override
protected String[] getDefaultConfigLocations() {//Tip:返回配置文件路徑
    if (getNamespace() != null) {
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

這里用了XmlBeanDefinitionReader來解析Bean定義,且指定了配置文件的加載邏輯,getConfigLocations方法:如果父類的configLocations不為空,則返回該值,否則返回getDefaultConfigLocations的值。而getDefaultConfigLocations方法邏輯為:如果存在命名空間,則返回/WEB_INF/namespace.xml作為配置文件,否則返回/WEB-INF/applicationContext.xml。對應上面的demo,將返回配置中的文件(同默認值相同)。

 XMLWebApplicationContext的初始化步驟為:

  • 讀取contextId配置,進行設置
  • 讀取contextConfigLocation配置,使用指定的配置文件,若沒有則使用上面提到的默認配置文件DEFAULTCONFIGLOCATION
  • 加載contextInitializerClasses指定的class,用于在context刷新前執行自定義處理
  • 調用XMLWebApplicationContext的refresh方法

(3)標記已經初始化

 通過將該根Context存在ServletContext中,并設置值為org.springframework.web.context.ROOT,用于第(1)步的判斷

3.1.2. contextDestroyed

 銷毀過程比較簡單,首先調用WebApplicationContext的close方法銷毀該Context,然后移除ServletContex中的org.springframework.web.context.ROOT屬性值,最后清除ServletContext中所有org.springframework.開頭的屬性值。

3.2. DispatcherServlet的加載

 同ContextLoaderListener類似,DispatcherServlet依賴于Servlet進行擴展。DispatcherServlet的結構如下:

SpringMVC加載流程

 

如上,DispatcherServlet繼承自HttpServlet,并重寫了doService方法,用于處理http請求,其中:

3.2.1. HttpServletBean

 在HttpServlet的繼承上增加了ConfigurableEnvironment屬性,用于存放Servlet的配置項。通過重寫init方法,在初始化時將servlet配置項添加到上下文環境變量中,并在該方法中開放了initBeanWrapper和initServletBean方法給子類。

3.2.2. FrameworkServlet

 基于Servlet實現的Web框架,每個Servlet內部都對應一個XmlWebApplicationContext對象,且namespace格式為ServletName-servlet。上面說了在沒顯示設定配置文件路徑的情況下,且存在namespace時,會使用/WEB-INF/namespace.xml作為Spring配置文件,對應到demo即為/WEB-INF/dispatcher-servlet.xml。FrameworkServlet重寫了父類的intServletBean方法,對XmlWebApplicationContext的初始化工作。Servlet在初始化XmlWebApplicationContext時,會嘗試從ServletContext中獲取根Context(上面提到的,會將根Ccontext放到ServletContext中以標記已經初始化過)并設置為當前Context的父Context,然后再按照雷士根Contextde 的初始化過程對其進行初始化。不同的是,會在refresh前開放口子進行擴展,包括:

  • 對內通過重寫子類的postProcessWebApplicationContext方法
  • 對外通過加載并執行globalInitializerClasses中配置的ApplicationContextInitializer類

FrameworkServlet還重寫了父類的各doXXX方法,都交給processService方法,以處理Http請求。processService最終委托給了doService方法。

3.2.3. DispatchdrServlet

 是SpringMVC處理Http請求的主要實現,主要完成了兩件事:

3.1. 重寫了onRefresh方法

 初始化時設置了眾多默認處理策略,包括:文件處理策略、HandlerMapping處理策略、HandlerAdapter處理策略、HandlerException處理策略、View解析策略等。SpringMVC在處理Http的每個步驟上,都提供了類似filter的機制,每個步驟都能夠注冊多個策略處理器,按照順序選擇出能夠處理當前請求的策略并交給其處理。而大部分的默認策略來至于spring-mvc模塊下的org/springframework/web/servlet/DispatcherServlet.properties文件,如下:

SpringMVC加載流程

 

下面為本人demo(SpringBoot)運行時DispatcherServlet各屬性以及注冊的各策略的情況

SpringMVC加載流程

 

主要關注handlerMappings中的RequestMappingHandlerMapping和handlerAdapters中的RequestMappingHandlerAdapter。這兩個都不是在DispatcherServlet.properties文件中指定的,而是在開啟 后自動注冊的,這個后面會介紹。

3.1.1 RequestMappingHandlerMapping初始化

 RequestMappingHandlerMapping主要用于查找@RequestMapping注解的handler,其繼承關系如下:

SpringMVC加載流程

 

  • AbstractHandlerMapping:實現了HandlerMapping接口,提供了獲取handler的主要實現。getHandler方法的實現為,將具體handler的查找委托給了子類的getHandlerInternal方法,然后跟當前請求路徑相關的interceptor一起包裝為一個HandlerExecutionChain返回。interceptor為所有實現了MappedInterceptor接口的bean,會在AbstractHandlerMapping初始化的時候遍歷上下文進行查找。
  • AbstractHandlerMethodMapping:在AbstractHandlerMapping的基礎上,主要提供了根據請求查找對應handler method的實現,即getHandlerInternal方法。該類會在初始化時遍歷上下文中所有的Bean,然后符合條件的Bean(通過isHandler方法),遍歷當前Bean符合條件的方法(通過getMappingForMethod方法),每個方法都有一個對應的path,稱為lookUpPath。getHandlerInternal實現上也是通過請求的HttpServletRequest得到對應的lookUpPath,然后從內存緩存中獲取對應的handler。
  • RequestMappingHandlerMapping:@RequestMapping的實現,主要實現了 isHandler和getMappingForMethod。 isHandler:判斷是否出現@Controller注解或者@RequestMapping注解 getMappingForMethod:根據@RequestMapping注解返回RequestMappingInfo實例。
    3.1.2 RequestMappingHandlerAdapter初始化

  RequestMappingHandlerAdapter主要完成HandlerMethod的執行,,其繼承關系如下:

SpringMVC加載流程

 

  • AbstractHandlerMethodAdapter:用于判斷是否支持Handler的執行,需要傳入的handler是否為HandlerMethod實例,同時將handler的執行委托給子類的handleInternal方法。
  • RequestMappingHandlerAdapter:真正執行handler對應的Method對象,會調用各種resolvers解析參數,用于在反射時作為入參傳入;調用各種converter用于對結果進行加工等操作。

3.2. 重寫doService方法

 實現了Http請求的處理過程,具體流程如下圖,即開頭提及的SpringMVC處理Http請求的過程,前面已經介紹過流程,這里不再贅述。

SpringMVC加載流程

 

3.3. mvc:annotation-driven

 按照之前說的,先看resource/META-INF/spring.handlers文件,這個配置在spring-webmvc模塊下,內容為:

http://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

支持的標簽如下:

SpringMVC加載流程

 

annotation-driven的解析類為:AnnotationDrivenBeanDefinitionParser,該類主要自動做了如下動作:

  • 注入了RequestMappingHandlerMapping和BeanNameUrlHandlerMapping兩個HandlerMapping實現
  • 注入了RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter三個HandlerAdapter實現。需要指出的是對于RequestMappingHandlerAdapter,如果沒有配置message-converters標簽指定消息處理器的話,會根據classpath中存在的包自動注入處理器,包括: ByteArrayHttpMessageConverter StringHttpMessageConverter ResourceHttpMessageConverter SourceHttpMessageConverter AllEncompassingFormHttpMessageConverter 如果存在com.rometools.rome.feed.WireFeed類,則增加AtomFeedHttpMessageConverter、RssChannelHttpMessageConverter 如果存在com.fasterxml.jackson.dataformat.xml.XmlMapper類,則增加MappingJackson2XmlHttpMessageConverter 如果存在javax.xml.bind.Binder類,則增加Jaxb2RootElementHttpMessageConverter 如果存在com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator,則增加MappingJackson2HttpMessageConverter 如果存在com.google.gson.Gson,則增加GsonHttpMessageConverter
  • 注入了ExceptionHandlerExceptionResolver用于實現@ExceptionHandler注解、注入了ResponseStatusExceptionResolver用于實現@ResponseStatus和DefaultHandlerExceptionResolver
  • 注入了AntPathMatcher和UrlPathHelper用于路徑解析

 上面介紹了SpringMVC大體流程的實現,當然還有很多細節沒有進行說明,如@Param,HttpServletRequest等各種參數的解析和注入,響應結果轉為json等各種結果的加工,詳細內容可以根據上面介紹再進行深入。

4. WebApplicationInitializer

 Servlet3.0+提供了ServletContainerInitializer接口,用于在web容器啟動時為提供給第三方組件機會做一些初始化的工作,例如注冊servlet或者filtes等。

 每個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄創建一個名為javax.servlet.ServletContainerInitializer的文件,文件內容指定具體的ServletContainerInitializer實現類。spring-web模塊下便存在該配置:

SpringMVC加載流程

 

內容為:

org.springframework.web.SpringServletContainerInitializer

SpringServletContainerInitializer的主要功能是加載classpath下的所有WebApplicationInitializer實現類(非接口、非抽象類),按照@Order進行排序后依次執行WebApplicationInitializer的onStartup方法。

 spring-web模塊提供的抽象類實現AbstractContextLoaderInitializer能夠不用web.xml配置增加RootContext;提供的抽象類實現AbstractDispatcherServletInitializer能夠不用web.xml配置增加DispatcherServlet。當然更重要的實現是SpringBoot中的實現,這個后續介紹SpringBoot時再提。

更多原創內容請搜索微信公眾號:啊駝(doubaotaizi)

分享到:
標簽:SpringMVC
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定