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類,用來自動封裝參數,是用的時候會方便得多。