Java Servlet開發的輕量級MVC框架最佳實踐
在Servlet開發的工程實踐中,為了減少過多的業務Servlet編寫,會採用構建公共Servlet的方式,通過反射來搭建輕量級的MVC框架,從而加快應用開發。
關於Servlet開發的基礎知識,請看:JavaWeb開發之詳解Servlet及Servlet容器
前後端互動的基本形式
一般來說,前端提交資料請求有三種基本方式,分別是表單、連結和Ajax
1. 按鈕
1 <form action="/BaseServlet/ServletDemo02?method=addStu" method="post"> 2 使用者<input type="text" name="username"/><br/> 3 <button>提交</button> 4 </form>
2. 連結
<a href="/BaseServlet/ServletDemo02?method=delStu">刪除學生</a><br/>
3. Ajax
1 <button onclick="fn()">按鈕</button> 2 <script> 3 function fn(){ 4 $.post("/BaseServlet/ServletDemo02",{"method":"checkStu","user":"tom"},function(data){ 5 alert(data); 6 }); 7 }
在Servlet開發的語境中,它們的共同點都是:指定處理的Servlet類路徑(在web.xml中指定)以及附帶在請求中的“method”引數
通過呼叫request引數匹配業務處理邏輯
前端按照以上方法發起請求,Servlet容器就會把請求交給對應的Servlet處理,並且附帶上形如 method=delStu 的引數,利用這個原理,就可以構建一個基礎Servlet類,用來優化開發:
1 protectedvoid doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 //獲取客戶端提交到服務端的method對應的值 3 String md=request.getParameter("method"); 4 //定義變數,存放功能執行完畢之後要轉發的路徑 5 String path=null; 6 7 //通過判斷md中不同的內容來決定本次功能 8 if("addStu".equals(md)){ 9 path=addStu(request, response); 10 }else if("delStu".equals(md)){ 11 path=delStu(request, response); 12 }else if("checkStu".equals(md)){ 13 path=checkStu(request, response); 14 }else if("".equals(md)){ 15 16 } 17 if(null!=path){ 18 //服務端的請求轉發 19 request.getRequestDispatcher(path).forward(request, response); 20 } 21 } 22 23 24 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 25 doGet(request, response); 26 } 27 28 protected String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 System.out.println("新增學生"); 30 return "/test.html"; 31 32 } 33 protected String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 34 System.out.println("刪除學生"); 35 return "/test.html"; 36 37 } 38 protected String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 System.out.println("檢查學生"); 40 response.getWriter().println("DDDDDD"); 41 return null; 42 }
以上的Servlet對請求進行了處理,通過獲取index.html的請求,最後請求轉發至目標頁面,其核心思想是:
1. 提取request的method引數的值;
2. 定義變數,儲存請求轉發的路徑;
3. 通過判斷method引數中的值的內容,來決定呼叫哪個業務功能。
4. 完成業務功能後,使用請求轉發處理
通過反射匹配業務處理邏輯
當業務量多的時候,以上實踐仍是不夠,此時比較好的方式就是採用反射。
1 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 doGet(request, response); 3 } 4 5 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 6 //獲取客戶端提交到服務端的method對應的值 7 String md=request.getParameter("method"); 8 //定義變數,存放功能執行完畢之後要轉發的路徑 9 String path=null; 10 //獲取到當前位元組碼物件(ServletDemo02.class在記憶體中物件) 11 Class<? extends ServletDemo02> clazz = this.getClass(); 12 try { 13 //獲取clazz上名稱為md, 引數為HttpServletRequest和HttpServletResponse的方法 14 Method method=clazz.getMethod(md, HttpServletRequest.class,HttpServletResponse.class); 15 if(null!=method){ 16 //呼叫找到的方法 17 path=(String)method.invoke(this, request,response); 18 } 19 if(null!=path){ 20 //服務端的轉發 21 request.getRequestDispatcher(path).forward(request, response); 22 } 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 27 } 28 29 public String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 30 System.out.println("新增學生"); 31 return "/test.html"; 32 33 } 34 public String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 35 System.out.println("刪除學生"); 36 return "/test.html"; 37 38 } 39 public String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 40 System.out.println("檢查學生"); 41 response.getWriter().println("DDDDDD"); 42 return null; 43 }
1. 提取request的method引數的值;
2. 定義變數,儲存請求轉發的路徑;
3. 獲取到當前Servlet物件在記憶體中的Class物件
4. 獲取Class物件上名稱為md, 引數為HttpServletRequest和HttpServletResponse的方法
5. 根據是否返回方法物件,呼叫業務功能並使用請求轉發處理
Java HttpServlet的父類GenericServlet,就是通過這個方法來調取HttpServlet的doGet或doPost方法的。
最佳實踐
在實踐開發中,一般會搭建一個BaseServlet,繼承了HttpServlet並重寫其Service方法,通過反射來找到業務功能子類對應的業務方法。
1 public class BaseServlet extends HttpServlet { 2 private static final long serialVersionUID = 12197442526341123L; 3 4 @Override 5 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 6 System.out.println("service....."); 7 //獲取客戶端提交到服務端的method對應的值 8 String md=request.getParameter("method"); 9 //定義變數,存放功能執行完畢之後要轉發的路徑 10 String path=null; 11 //獲取到當前位元組碼物件(ServletDemo02.class在記憶體中物件) 12 Class<? extends BaseServlet> clazz = this.getClass(); 13 try { 14 //獲取clazz上名稱為md方法 15 Method method=clazz.getMethod(md, HttpServletRequest.class,HttpServletResponse.class); 16 if(null!=method){ 17 //呼叫找到的方法 18 path=(String)method.invoke(this, request,response); 19 } 20 if(null!=path){ 21 //服務端的轉發 22 request.getRequestDispatcher(path).forward(request, response); 23 } 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 29 }
而業務功能子類,則繼承BaseServlet,專注於業務邏輯的開發
1 public class ServletDemo03 extends BaseServlet { 2 3 private static final long serialVersionUID = 11248215356242123L; 4 5 public ServletDemo03() { 6 System.out.println("沒有引數的建構函式"); 7 } 8 9 public String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 10 System.out.println("新增學生"); 11 return "/test.html"; 12 13 } 14 public String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 System.out.println("刪除學生"); 16 return "/test.html"; 17 18 } 19 public String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 20 System.out.println("檢查學生"); 21 response.getWriter().println("DDDDDD"); 22 return null; 23 } 24 25 }