初探SpringMVC源碼
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iNwEjN0cDM5UjNiJzN1UjNzYzX1EzNxIDM1AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
上篇文章我們通過手寫分析了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來作為入口分析。
既然是一個Servlet。我們就需要分析對應的生命周期的方法
2.1 init方法
init方法中會完成相關的初始化操作。我們來看看完成了哪些初始化的操作。我們可以直接進入到FrameworkServlet的
initServletBean()
方法中檢視
initWebApplicationContext()
方法中。
進入後:
上面的IoC容器的初始化過程前面介紹Spring的時候重點講解過,先不關注。直接看
onRefresh
方法
我們看下
initHandlerMapping
方法。
上面的代碼我們可以看到邏輯很簡單,會先讀取xml檔案中配置的HandlerMapping類型的資訊,如果沒有就讀取預設的。案例中我們配置的有
如果沒有配置的邏輯為就會讀取 DispatcherServlet.properties 檔案中内容
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方法做了兩件事情
- 完成了IoC的初始化
- 完成了SpringMVC核心元件的初始化
2.2 service方法
service方法是在使用者請求到來的時候觸發的。也就是具體處理請求的方法。我們來看下,直接進入到doDispatch方法中
擷取對應的擴充卡
調用ha.handle方法
然後就進入到了自定義的控制器中了。
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中會提供相關的解析器。這塊我們可以看下源碼中的内容
看到提供了一個處理器MvcNamespaceHandler。
可以看到,對應的标簽都映射了對應的解析器,也就是解析到這個标簽的時候,會找到對應的解析器來解析操作。然後我們進入到
AnnotationDrivenBeanDefinitionParser
中可以看看具體的解析邏輯。
可以看到。在這塊完成了核心的
HandlerMapping
和
HandlerAdapter
注入到了容器中。那麼在DispatcherServlet中處理請求的時候就會通過對應的Handler來處理了。
這塊的串聯是在IoC的加載解析xml檔案的邏輯中處理的。
三、SpringBoot項目
然後我們來看下載下傳SpringBoot項目中是怎麼自動裝配SpringMVC架構的,首先我們找到對應的配置類
同時我們也需要關注下這個配置類
在這個配置類中注入的HandlerMapping和HandlerAdapter的具體實作類