天天看點

是時候掌握SpringMVC源碼了-初探篇

初探SpringMVC源碼

是時候掌握SpringMVC源碼了-初探篇

  上篇文章我們通過手寫分析了SpringMVC中的Controller的兩種實作方式。接下來我們來看看在SpringMVC中具體是如何使用的。

一.基于Controller接口

1. 案例代碼

引入相關的依賴

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.18</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>      

然後建立自定義的Controller

/**
 * 自定義控制器
 * 必須實作Controller接口
 * @author dpb【波波烤鴨】
 *
 */
public class UserController implements Controller{

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("本方法被調用了...");
        ModelAndView view = new ModelAndView();
        view.setViewName("/index.jsp");
        return view;
    }
}      

然後添加SpringMVC的配置檔案

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 處理器映射器 将bean的name作為url進行查找 ,
                  需要在配置Handler時指定beanname(就是url) 所有的映射器都實作
         HandlerMapping接口。
     -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

    <!-- 配置 Controller擴充卡 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

    <bean name="/hello.action" class="com.boge.controller.UserController" />
</beans>      

我們需要在配置檔案中配置對應的 ​

​HandlerMapping​

​​和 ​

​HandlerAdapter​

最後在web.xml中配置下前端控制器就可以了

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>test</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <!-- 配置前端控制器 -->
  <!-- contextConfigLocation配置springmvc加載的配置檔案(配置處理器映射器、擴充卡等等)
    如果不配置contextConfigLocation,
    預設加載的是/WEB-INF/servlet名稱-serlvet.xml(springmvc-servlet.xml)
     -->
  <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>
</web-app>      

然後就可以啟動服務來通路了

2. 源碼分析

  通過上面的案例分析,我們可以看的出來在 ​

​web.xml​

​​中配置了一個 ​

​Servlet​

​當使用者請求到來的時候都會攔截處理。是以我們通過DispatcherServlet來作為入口分析。

是時候掌握SpringMVC源碼了-初探篇

既然是一個Servlet。我們就需要分析對應的生命周期的方法

是時候掌握SpringMVC源碼了-初探篇

2.1 init方法

  init方法中會完成相關的初始化操作。我們來看看完成了哪些初始化的操作。我們可以直接進入到FrameworkServlet的 ​

​initServletBean()​

​​方法中檢視 ​

​initWebApplicationContext()​

​方法中。

是時候掌握SpringMVC源碼了-初探篇

進入後:

是時候掌握SpringMVC源碼了-初探篇

上面的IoC容器的初始化過程前面介紹Spring的時候重點講解過,先不關注。直接看 ​

​onRefresh​

​方法

是時候掌握SpringMVC源碼了-初探篇

我們看下 ​

​initHandlerMapping​

​方法。

是時候掌握SpringMVC源碼了-初探篇

上面的代碼我們可以看到邏輯很簡單,會先讀取xml檔案中配置的HandlerMapping類型的資訊,如果沒有就讀取預設的。案例中我們配置的有

是時候掌握SpringMVC源碼了-初探篇

如果沒有配置的邏輯為就會讀取 DispatcherServlet.properties 檔案中内容

是時候掌握SpringMVC源碼了-初探篇

DispatcherServlet.properties中的資訊為:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
  org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
  org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
  org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
  org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
  org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager      

到這我們就清楚了init方法做了兩件事情

  1. 完成了IoC的初始化
  2. 完成了SpringMVC核心元件的初始化

2.2 service方法

  service方法是在使用者請求到來的時候觸發的。也就是具體處理請求的方法。我們來看下,直接進入到doDispatch方法中

是時候掌握SpringMVC源碼了-初探篇
是時候掌握SpringMVC源碼了-初探篇

擷取對應的擴充卡

是時候掌握SpringMVC源碼了-初探篇

調用ha.handle方法

是時候掌握SpringMVC源碼了-初探篇

然後就進入到了自定義的控制器中了。

是時候掌握SpringMVC源碼了-初探篇

2.3 destory方法

  在destory方法中的處理就很簡單。完成IoC容器的關閉操作

public void destroy() {
        this.getServletContext().log("Destroying Spring FrameworkServlet '" + this.getServletName() + "'");
        if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
            ((ConfigurableApplicationContext)this.webApplicationContext).close();
        }

    }      

二、基于注解的方式

1.案例講解

  注解方式的使用我們需要在配置檔案中添加相關的标簽來開啟

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.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-4.3.xsd">

    <!-- 開啟注解 -->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!-- 開啟掃描 -->
    <context:component-scan base-package="com.boge.controller"></context:component-scan>
</beans>      

然後我們就可以在自定義控制器中通過@Controller注解和@RequestMapping注解來做方法級别的映射

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/do1")
    @ResponseBody
    public String doSome1(){
        return "do1";
    }

    @RequestMapping("/do2")
    @ResponseBody
    public String doSome2(){
        return "do2";
    }
}      

2.源碼分析

  我們可以看到添加了一個标簽後就開啟了注解的使用方式,那麼他的内部是怎麼執行的呢?因為是自定義标簽,是以在SpringMVC中會提供相關的解析器。這塊我們可以看下源碼中的内容

是時候掌握SpringMVC源碼了-初探篇

看到提供了一個處理器MvcNamespaceHandler。

是時候掌握SpringMVC源碼了-初探篇

  可以看到,對應的标簽都映射了對應的解析器,也就是解析到這個标簽的時候,會找到對應的解析器來解析操作。然後我們進入到 ​

​AnnotationDrivenBeanDefinitionParser​

​中可以看看具體的解析邏輯。

是時候掌握SpringMVC源碼了-初探篇
是時候掌握SpringMVC源碼了-初探篇

可以看到。在這塊完成了核心的 ​

​HandlerMapping​

​​和 ​

​HandlerAdapter​

​注入到了容器中。那麼在DispatcherServlet中處理請求的時候就會通過對應的Handler來處理了。

這塊的串聯是在IoC的加載解析xml檔案的邏輯中處理的。

是時候掌握SpringMVC源碼了-初探篇
是時候掌握SpringMVC源碼了-初探篇

三、SpringBoot項目

  然後我們來看下載下傳SpringBoot項目中是怎麼自動裝配SpringMVC架構的,首先我們找到對應的配置類

是時候掌握SpringMVC源碼了-初探篇

同時我們也需要關注下這個配置類

是時候掌握SpringMVC源碼了-初探篇

在這個配置類中注入的HandlerMapping和HandlerAdapter的具體實作類

是時候掌握SpringMVC源碼了-初探篇
是時候掌握SpringMVC源碼了-初探篇