這篇文章主要介紹“Servlet到Spring的請(qǐng)求分發(fā)機(jī)制是什么”,在日常操作中,相信很多人在Servlet到Spring的請(qǐng)求分發(fā)機(jī)制是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Servlet到Spring的請(qǐng)求分發(fā)機(jī)制是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
在講請(qǐng)求分發(fā)之前先梳理一下一個(gè)Web請(qǐng)求的交互邏輯:
首先用戶(hù)在客戶(hù)端發(fā)送一個(gè)請(qǐng)求到服務(wù)器 。
這個(gè)請(qǐng)求首先會(huì)經(jīng)過(guò)操作系統(tǒng)的 TCP/IP 協(xié)議棧解析后發(fā)送至某一個(gè)端口
在該端口運(yùn)行著一個(gè) Web 應(yīng)用服務(wù)器(假設(shè)是 Tomcat)
接著 Tomcat 會(huì)把請(qǐng)求根據(jù)請(qǐng)求路徑傳送給對(duì)應(yīng)的 Servlet 處理 (要注意的是,Web 服務(wù)器本身是不處理請(qǐng)求的,比如說(shuō) Tomcat,它只負(fù)責(zé)分發(fā)請(qǐng)求)
1.Servlet時(shí)期的請(qǐng)求分發(fā) 在還沒(méi)有 spring 框架的時(shí)候,只能單純用 Servlet 處理請(qǐng)求。
具體做法是:把 Servlet 及其映射路徑配置在一個(gè)叫 web.xml 的配置文件中,當(dāng)服務(wù)器啟動(dòng)時(shí),Tomcat 會(huì)自動(dòng)讀取這個(gè)文件,然后根據(jù)文件中的配置,把請(qǐng)求分配到對(duì)應(yīng)的 Servlet。
這個(gè)時(shí)候請(qǐng)求分發(fā)的工作是在 Tomcat 中完成的,通常一個(gè)業(yè)務(wù)對(duì)應(yīng)一個(gè) Servlet。比如說(shuō)關(guān)于用戶(hù)的增刪改查,對(duì)應(yīng)的 Servlet 很有可能是這樣子的:
AddUserServlet
DeleteUserServlet
UpdateUserServlet ...
對(duì)應(yīng)的結(jié)構(gòu)圖如下:
這和我們?nèi)缃耖_(kāi)發(fā)中的架構(gòu)很不一樣,因?yàn)楝F(xiàn)在通常是一個(gè)類(lèi)里面包含了對(duì)同個(gè)業(yè)務(wù)所有處理邏輯。比如說(shuō)對(duì)用戶(hù)的增刪改查都放在同一個(gè)類(lèi)里,用不同的方法區(qū)分。
但在以前,一個(gè) Servlet 里面通常只有一個(gè) service 是有用的,其他和生命周期相關(guān)的方法基本只是給一個(gè)空殼的重寫(xiě),所以一個(gè) service 方法就對(duì)應(yīng)一個(gè)業(yè)務(wù)處理邏輯。這種分類(lèi)方法不僅給 web 服務(wù)器帶來(lái)很大負(fù)擔(dān)的,而且會(huì)導(dǎo)致 web.xml 文件十分龐大。(User 的增刪改查邏輯對(duì)應(yīng)4個(gè) Servlet,要是能把和 User 相關(guān)的處理邏輯分發(fā)給一個(gè)大的 UserServlet 就好了)
后來(lái)有人找到了把和 User 相關(guān)的處理邏輯分發(fā)給一個(gè)大的 UserServlet 的方法。具體方法就是使用 Java 中的反射。詳細(xì)代碼如下:
/** * 該Servlet不需要進(jìn)行配置,因?yàn)樵揝ervlet從來(lái)不需要被直接訪(fǎng)問(wèn),使用來(lái)被繼承的 * 可以定義為abstract class */ public abstract class BaseServlet extends HttpServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解決post表單中文亂碼問(wèn)題 request.setCharacterEncoding("utf-8"); //獲取method屬性的值(方法名) String methodName = request.getParameter("method"); if(methodName == null || methodName.trim().isEmpty()) { throw new RuntimeException("您沒(méi)有傳遞method參數(shù)!無(wú)法確定您想要調(diào)用的方法!"); } //使用反射調(diào)用方法 try { //獲取當(dāng)前Servlet的Class信息 Class clazz = this.getClass();//實(shí)際訪(fǎng)問(wèn)的Servlet,不是BaseServlet,是BaseServlet的子類(lèi)比如UserServlet //使用反射創(chuàng)建對(duì)象 //Object obj = clazz.newInstance(); //獲取方法 Method method = clazz.getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); //使用反射執(zhí)行方法 method.invoke(this, request,response); } catch (Exception e) { e.printStackTrace(); } } }
這樣一來(lái),分發(fā)請(qǐng)求的結(jié)構(gòu)就變成了這樣:
也就是說(shuō)同類(lèi)業(yè)務(wù)的請(qǐng)求分配給同一個(gè) Serlvet,當(dāng)具體到細(xì)分的業(yè)務(wù)邏輯時(shí),使用不同的方法進(jìn)行區(qū)分;在運(yùn)行的時(shí)候使用反射調(diào)用具體的方法。
這么做能明顯減輕服務(wù)器分發(fā)請(qǐng)求的壓力,也讓 web.xml 文件變得更加簡(jiǎn)潔。而且同類(lèi)型的業(yè)務(wù)邏輯能夠編寫(xiě)在同一個(gè)類(lèi)中,方便管理維護(hù)。但是有沒(méi)有可能做得更好呢?
2.Spring框架的請(qǐng)求分發(fā) 先用一幅圖來(lái)描述 Spring 框架的請(qǐng)求分發(fā),圖片通俗易懂:
從圖中可以看到 web 服務(wù)器此時(shí)已經(jīng)不用考慮分發(fā)請(qǐng)求的問(wèn)題了,它只需要把請(qǐng)求發(fā)送給 DispatcherServlet 即可。請(qǐng)求分發(fā)的任務(wù)將落到 DispatcherServlet 身上。
而 DispatchServlet 也不再是把請(qǐng)求分發(fā)給其他 Servlet,而是根據(jù)請(qǐng)求路徑分發(fā)給對(duì)應(yīng)的 Controller,然后再分發(fā)到 Controller 中對(duì)應(yīng)的處理方法。
下面給出一個(gè)迷你簡(jiǎn)寫(xiě)版的 DispatchServlet 代碼:
public class DispatcherServlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException {} @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { for(MappingHandler mappingHandler : HandlerManager.mappingHandlerList){ try{ if (mappingHandler.handle(servletRequest, servletResponse)){ return; } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } @Override public String getServletInfo() { return null; } @Override public void destroy() {} }
從上面的代碼可以看到 DispatcherServlet 處理請(qǐng)求的邏輯也很簡(jiǎn)單,就是根據(jù)請(qǐng)求路徑找到對(duì)應(yīng)可以處理的處理器。
至于 MappingHandler 是怎么初始化的,這就涉及到 Spring 框架中控制反轉(zhuǎn)和依賴(lài)注入的知識(shí)了。簡(jiǎn)單來(lái)說(shuō)就是,在開(kāi)發(fā)的過(guò)程中,我們不再把請(qǐng)求路徑配置在 web.xml 里面,而是通過(guò)注解的方式配置在每一個(gè)個(gè)處理方法的上方。當(dāng) spring 框架啟動(dòng)后,會(huì)根據(jù)注解把每個(gè)處理方法初始化為一個(gè)個(gè) MappingHandler(里面包括請(qǐng)求路徑和處理邏輯),供 DispatcherServlet 調(diào)配使用。
這樣的請(qǐng)求分發(fā)方式無(wú)疑比 Serlvet 時(shí)期的請(qǐng)求分發(fā)更加靈活,強(qiáng)大。它把請(qǐng)求分發(fā)的工作從 web 服務(wù)器上移植到框架中,可擴(kuò)展性更強(qiáng)。而且配合 spring 框架的控制反轉(zhuǎn)機(jī)制,把處理邏輯類(lèi)的初始化及生命周期控制交給框架管理,更加安全高效。
到此,關(guān)于“Servlet到Spring的請(qǐng)求分發(fā)機(jī)制是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源:
創(chuàng)新互聯(lián)