仍是以前的三個套路前端
Spring
提供一套視圖層的處理框架,他基於Servlet
實現,能夠經過XML
或者註解進行咱們須要的配置。java
他提供了攔截器,文件上傳,CORS
等服務。web
原生Servlet
在大型項目中須要進過多重封裝,來避免代碼冗餘,其次因爲不一樣接口須要的參數不一樣,咱們須要本身在Servlet
層 封裝咱們須要的參數,這對於開發者來講是一種重複且枯燥的工做,因而出現了視圖層框架,爲咱們進行參數封裝等功能。讓開發者的注意力所有放在邏輯架構中,不須要考慮參數封裝等問題。面試
再聊怎麼用以前,咱們須要瞭解一下MVC
的工做原理。spring
他基於一個DispatcherServlet
類實現對各類請求的轉發,即前端的全部請求都會來到這個Servlet中,而後這個類進行參數封裝和請求轉發,執行具體的邏輯。(第二章咱們細聊)shell
DispatcherServlet
來爲咱們提供基礎的Servlet
服務,咱們能夠經過servlet
規範的web.xml
文件,對該類進行初始化。而且聲明該類處理全部的請求,而後經過這個類實現請求轉發。mvc
信息。下面來看一個完整的web.xml
配置編程
<web-app> <servlet> <servlet-name>dispatchServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatchServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
註解方式也是如今主流,SpringBoot
基於JavaConfig
實現了自動配置後端
實現方式:架構
在Servlet3.0
的時候定義了一個規範SPI
規範。mvc
SPI
,全稱爲 Service Provider Interface
,是一種服務發現機制。它經過在ClassPath
路徑下的META-INF/services
文件夾查找文件,自動加載文件裏所定義的類。也就是在服務啓動的時候會Servlet會自動加載該文件定義的類
咱們看一眼這個文件裏的內容。他內部定義了SpringServletContainerInitializer
容器初始化類,也就是說在Servlet
啓動的時候會自動初始化這個類,這個類也是註解實現的關鍵。
這個類中存在一個onStartup
方法,這個也是當容器初始化的時候調用的方法,這個方法有兩參數
Set<Class<?>> webAppInitializerClasses
他表明了當前咱們的Spring
容器中存在的web
初始化類。咱們本身能夠經過實現WebApplicationInitializer
類來自定義Servlet
初始化的時候執行的方法。ServletContext servletContex
表明了Servlet
上下文對象org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { //啓動邏輯 } }
具體看一下註解配置方式:
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); //一個配置類,@Configuration ac.register(AppConfig.class); //spring的那個refresh方法 ac.refresh(); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
經過實現WebApplicationInitializer
接口,來做爲MVC
的配置類,在加載SpringServletContainerInitializer
的時候加載這個類。
不過在具體的實現中,Spring
不建議咱們這樣作,他建議將Spring
和SpringMvc
分開,看個圖
他在Spring之上加了一層Web環境配置。至關於在Spring的外面包裝了一層Servlet
看一下此時的代碼
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //Spring配置文件 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } //SpringMVC的配置文件 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { App1Config.class }; } //指定DispatcherServlet能夠攔截的路徑 @Override protected String[] getServletMappings() { return new String[] { "/app1/*" }; } }
經過AbstractAnnotationConfigDispatcherServletInitializer
能夠看到他實現了WebApplicationInitializer
接口,即在Servlet
初始化的時候會加載這個類。
AbstractContextLoaderInitializer
類,他初始化了Spring
AbstractDispatcherServletInitializer
類,初始化了DispatcherServlet
AbstractAnnotationConfigDispatcherServletInitializer
類,將兩個類整合到一塊兒
聊這個原理以前,先來聊聊他要幹什麼?
需求:請求分發;參數封裝;結果返回
那若是咱們本身來實現,該怎麼辦?(單說註解,先來看看咱們怎麼使用MVC
的)
@Controller
註解,標識當前類爲控制層接口,RequestMapping
標識這個方法的URI
和請求方式等信息@ResponseBody
標識這個方法的返回類型爲JSON
test01
標識這個方法用來處理/test
請求@Controller public class UserController { @GetMapping("/test") @ResponseBody public String test01(){ return "success" ; } }
接下來,咱們經過咱們已有的東西,看一下咱們本身去處理請求的邏輯
先來想一下咱們的請求過程:
Http
請求,經過不一樣的uri
實現不一樣邏輯的處理uri
和咱們後端的定義的@RequestMapping
中的value
值相同Map
結構,將value
做爲key
,將method
的Class
對象做爲一個value
存到一個MappingRegister
中URI
從這個Map
中獲取相應的Method
執行,若是沒有對應的Method
給一個404
.在上面的怎麼用中提到了,他經過AbstractContextLoaderInitializer
來加載Spring
配置文件的。
此時關於Spring的東西已經加載好了,但並未進行初始化
一樣也是經過AbstractDispatcherServletInitializer
類實現
接下來咱們具體看一下在這個期間,DispatcherServlet
如何處理請求的
做用:分發全部的請求
類繼承結構圖
能夠看到他繼承了HttpServlet
類,屬於一個Servlet
,而在以前咱們配置了這個Servlet
的攔截路徑。他會將全部的請求攔截,而後作一個分發。
下面這個圖各位看官應該很是熟悉:
其實DispatcherServlet
處理全部請求的方式在這個圖裏徹底都體現了。
接下來聊一下他的設計思路吧。
當一個請求來的時候,進入doDispatch
方法中,而後處理這個請求,也是返回一個執行鏈
Spring
提供了三種方式的處理器映射器來處理不一樣的請求。
BeanNameUrlHandlerMapping
處理單獨Bean
的請求。適用於實現Controller
和HttpRequestHandler
接口的類@Component("/test02") public class HttpController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("HttpController執行"); return null; } }
@Component("/test01") public class HandlerController implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("handlerRequest"); } }
RequestMappingHandlerMapping
適用於方法類型的處理器映射。@Controller public class UserController { @GetMapping("/test") public String test01(){ System.out.println("執行了"); return "success" ; } }
RouterFunctionMapping
,MVC
提供的一個處理經過函數式編程定義控制器的一個映射器處理器。須要直接添加到容器中,而後 經過路由一個地址,返回對應的數據@Configuration @ComponentScan("com.bywlstudio.controller") @EnableWebMvc public class MvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/pages/",".jsp"); } @Bean public RouterFunction<?> routerFunctionA() { return RouterFunctions.route() .GET("/person/{id}", request1 -> ServerResponse.ok().body("Hello World")) .build(); } }
聊完了處理器映射器,再來聊一下處理器適配器
不一樣的請求方式,須要不一樣的處理方式,這也是Spring
爲何要提供一個適配器的緣由。
RequestMappingHandlerAdapter
用來處理全部的方法請求,即經過@Controller
註解定義的HandlerFunctionAdapter
用來處理函數式的映射,即經過RouterFunctionMapping
定義的HttpRequestHandlerAdapter
用來處理實現了HttpRequestHandler
接口的SimpleControllerHandlerAdapter
用來處理實現了Controller
接口的請求經過處理器適配器拿到適合的處理器,來處理對應的請求。
在處理器執行具體的請求的過程,實際上就是調用咱們的方法的過程,因而就會出現返回值
一般對於返回值咱們有兩種方法:
@ResponseBody
直接返回JSON
數據。對於返回值解析,MVC
提供了一個接口用於處理全部的返回值,這裏咱們僅僅談上面的兩種
ModelAndViewMethodReturnValueHandler
用於處理返回視圖模型的請求RequestResponseBodyMethodProcessor
用於處理返回JSON
在咱們拿到方法返回值之後,會調用this.returnValueHandlers.handleReturnValue
返回值解析器的這個方法,用於對視圖模型的返回和JSON
數據的回顯(直接回顯到網頁,此時返回的視圖對象爲null)
對於視圖對象,經過視圖解析器直接解析,進行數據模型渲染,而後回顯給前端。
這個類存放了method
的映射信息。
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
MVC
會從這個類中獲取方法和URL
的引用。至關於Spring MVC
的容器。
答:MVC
是一個架構模式,它有三個核心
MVVM
也是一種架構模式,它也是三個核心
Model
)。後端數據ViewModel
)。它完成了數據和視圖的綁定View
)。用戶界面它的核心思想是:經過ViewModel
將數據和視圖綁定,用數據操做視圖,常見框架爲Vue
DispatcherServlet
DispatcherServelt
收到請求之後調用HandlerMapping
,找到請求處理器映射器(三選一)URI
的處理器執行鏈(包含了攔截器,和處理器對象)處理器具體執行,返回ModelAndView
對象
@ResponseBody
註解,直接進行數據回顯ModelAndView
對象傳給ViewResove
視圖解析器解析,返回視圖DispatcherServlet
對View
進行渲染視圖更多原創文章請關注筆者公衆號@MakerStack,轉載請聯繫做者受權