程序猿的日常——SpringMVC系統架構與流程回顧


web開發經歷了很漫長的時間,在國內也快有十幾年的時間了。從最開始的進程級到現在的MVC經歷了很多的改進和優化,本篇就主要復習了解下Spring MVC相關的知識。

發展歷程

第一階段 CGI進程響應

這一階段,服務器比較弱,請求也很簡單,就是用戶發一個請求,服務器接收后新建進程,然后返回結果。

這種方式一看代價就很大,每次都新建進程,很麻煩。

第二階段 Servlet線程級別響應

Servlet結構跟上面差不多,只不過每次都只是新建一個線程,這樣代價就小很多了。

Servlet的生命周期有四個階段:

1 加載和實例化:啟動Tomcat這種Servlet容器,容器會根據配置文件加載Servlet類,並通過new方法進行實例化
2 初始化:然后調用init()方法初始化,每個Servlet只會初始化一次,可以理解為單例模式
3 請求處理:當服務器接收請求后,接收請求的線程找到對應的Servlet,調用service()方法響應。因此會存在多個線程同時掉用一個Servlet實例的情況,因此這里會有線程安全問題的!
4 銷毀:Tomcat關閉時,調用destroy()銷毀容器。

那么整體的流程是這樣的:

1 客戶端發送請求,Tomcat服務器接收請求后,封裝HttpRequest對象和HttpResponse對象
2 根據配置文件xml去查找匹配的servlet-name,並加載對應的servlet
3 如果之前沒有加載過,那么加載並進行實例化和初始化;如果加載過,則直接調用service方法處理
4 把處理的結果封裝到HttpResponse中返回

那么如何回答Serlet到底是不是線程安全呢?可以說它本身是無狀態的,如果沒有在里面自己新增一個什么count++的操作,就不會存在線程安全問題。

如果想要避免線程安全問題,可以采用下面的思路:

1 避免使用實例變量
2 避免使用非線程安全的集合
3 訪問外部可寫文件需要加鎖

總結來說,這里只要注意Servlet的生命周期以及線程安全問題即可。

第三階段 JSP+Model1

這個階段引入了JSP技術,即Java Server Page,它是一種把HTML和Java混合在一起的技術語言。我記得我剛學習Java的時候,就是用這種JSP的技術,如果頁面稍微復雜一點,代碼就會特別混亂。

不過這種方式也引入了一種前后端分離開發的合作模式,即會有專門的開發靜態頁面的人,開發完后把頁面交給后段程序猿,增量的開發Java相關的后端處理和數據展現相關的功能。

大體的流程是

1 用戶發送請求給服務器,服務器對應的JSP頁面接收到請求。
2 JSP會被編譯成Servlet,模式就跟之前一樣了
3 最后填充數據,返回即可。也就是說,它其實就是把之前頁面視圖的部分和Servlet的部分融合到一起而已。

現在基本上已經看不到這種技術模式了。

第四階段 前后端分離+Spring MVC

現在大部分的模式就是這樣的,只是在后段展現上略有不同。這種模式主要的關鍵是那個控制器,它負責任務的分發請求,以及數據的返回。

架構模型就如上面所示,不過在SpringMVC中,控制器有兩種,一種是前端控制器,一種是應用控制器。

大致的流程為:

1 用戶發送請求,前端控制器統一接收
2 然后根據不同的規則分發到對應的應用控制器,比如根據URL
3 應用控制器在調用邏輯代碼處理
4 最后層層返回。

目前一般的公司,都是采用前后端分離的技術結構。
1 前端是Vue.js或者AngularJS再或者是JQuery,通過Http的方式發送到后端。
2 后端接收請求后按照一定的業務規則處理,然后把數據返回給前端。
3 前端通過JavaScript代碼進行解析,瀏覽器渲染展現。

源碼細節

經過上面的描述,對SpringMVC的整體流程應該有了大致的了解。但是經典的那句話,talk is cheap, show me your code。

這個Dispacther分發器是怎么實現的呢?其實它就是一個普通的Servlet而已,只不過Servlet攔截的請求時所有的請求而已:

<servlet>  
    <servlet-name>test</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
<servlet-mapping>  
    <servlet-name>test</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  

然后這個Servlet會調用doDispatch方法,主要的內容都在這里

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   省略代碼
   try {
      doDispatch(request, response);
   }
   finally {
      省略代碼
   }
}

doDispatch方法則包含了剛才描述的種種步驟:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  省略
   try {
      省略
      try {
         // Determine handler for the current request.獲得處理器映射
         mappedHandler = getHandler(processedRequest);
         // Determine handler adapter for the current request.獲得適配器對象
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         // Actually invoke the handler.實際處理
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         //最后返回結果
      }
   }
}

常用的經驗

1 如果時開發Restful風格的后端程序,即通過Http以及GET、POST、PUT、DELETE等進行數據的增刪改查,那么可以直接使用@RestController注解

2 通常工程設計都會分為幾層,Controller,Service,Mapper 如果有分層,可以用@Service@Autowired注解搭配自動注入

3 如果使用@Service,最好直接寫上Service的名字,如@Service(value = "myService")不然如果你的名字是ABCService,默認的Service名字大小寫會容易引發BUG,尤其是需要手動查找某個bean時。

4 一般為了讓代碼簡潔,Controller參數列表可以封裝一個JavaBean類,用來自動封裝參數,是用的時候會方便得多。


免責聲明!

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



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