1. BaseServlet 的作用
- 讓一個Servlet可以處理多種不同的請求,不同的請求調用Servlet的不同方法.
2. 實現原理
- 客戶端發送請求時, 必須多給出一個參數, 用來說明要調用的方法!! 這樣,BaseServlet 通過該參數來
調用目標方法. - 請求處理方法的簽名必須與 service 相同, 即方法的返回值和參數,以及聲明的異常都相同.
// 代碼示例
public class AServlet extends HttpServlet{
// service 方法
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 獲取參數, 用來識別客戶端想請求的方法
// 然后判斷是哪一個方法, 是哪一個方法,就調用哪一個方法.
// 我們這里給參數的名字為 method
String methodName = req.getParameter("method");
if(methodName.equals("addUser")){
addUser(req,resp);
}else if(methodName.equals("editUser")){
editUser(req,resp);
}else if(methodName.equals("deleteUser")){
deleteUser(req,resp);
}
}
// 添加客戶的方法
public void addUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
// 編輯客戶的方法
public void editUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
// 刪除客戶的方法
public void deleteUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
}
// 升級版
/*
* 思路:
* 得到方法名稱, 是否可以通過反射來調用方法?
* 步驟:
* 1. 得到方法名, 通過方法名再得到 Method 類的對象
* 2. 需要得到 class, 然后調用它的方法進行查詢! 得到 Method
* 3. 我們要查詢的是當前類的方法, 所以我們需要得到當前類的 Class
*/
public abstact class BaseServlet extends HttpServlet{
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 獲取參數, 用來識別用戶想請求的方法
String methodName = req.getParameter("method");
// 判斷該參數是否存在, 不存在,拋出異常
if(methodName == null || methodName.trim().isEmpty()){
throw new RuntimeException("您沒有傳遞 method 參數! 無法確定您想調用的方法");
}
// 得到當前類的 class 對象
Class c = this.getClass();
// 查詢方法, 參數需要: 方法名和該方法的參數類型
// 該方法的參數類型必須與 service 中的參數類型一致
Method method = null;
try{
method = c.getMethod(methodName,
HttpServletRequest.class, HttpServletResponse.class);
} catch(Exception e){
throw new RuntimeException("您要調用的方法"+methodName+",它不存在!");
}
// 調用 method 方法
// 反射調用, 第一參數表示當前類,
// 正常調用: this.method(req,resp)
try{
method.invoke(this,req,resp);
} catch(Exception e){
throw new RuntimeException(e);
}
}
// AServlet 繼承 BaseServlet
public void class AServlet extends BaseServlet{
// 添加客戶的方法
public void addUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
// 編輯客戶的方法
public void editUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
// 刪除客戶的方法
public void deleteUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
}
}
// 處理轉發和重定向問題
public void class BServlet extends BaseServlet{
// 添加客戶的方法
public String addUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
// 返回表示轉發的字符串, "f" 表示 forward
return "f:/index.jsp";
}
// 編輯客戶的方法
public String editUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
// 返回表示重定向的字符串, "r" 表示 redirect
return "r:/index.jsp";
}
// 刪除客戶的方法
public String deleteUser(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("addUser()....");
return null;
}
}
// BaseServlet 升級
public void abstract BaseServlet extends HttpServlet{
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException{
String methdoName = req.getParameter("method");
if(methodName == null || methodName.trim().isEmpty()){
throw new RuntimeException("您沒有傳遞method參數,無法確定要調用的方法!");
}
Class c = this.getClass();
Method method=null;
try{
method = c.getMethod(methodName,
HttpServletRequest.class,HttpServletResponse.class);
}catch(Exception e){
throw new RuntimeException("您要調用的"+methodName+"方法,它不存在!");
}
// 調用 method 方法
try{
String result = (String)method.invoke(this,req,resp);
/*
* 獲取請求處理方法執行后返回的字符串, 它表示轉發或重定向的路徑!
* 完成轉發或重定向.
*
* 如果用戶返回的字符串為 null, 或為 "", 那么我們什么也不做!
*
* 查看返回的字符串中是否包含冒號, 如果沒有, 表示轉發
* 如果有, 使用冒號分割字符串, 得到前綴和后綴!!
* 其中前綴如果是 f, 表示轉發, 如果是 r, 表示重定向, 后綴就是要轉發或重定向的路徑了!
*/
if(result == null || result.trim().isEmpty()){
return;
}
// 如果不為空
if(result.contains(":")){
// 使用冒號分割字符串, 得到前綴和后綴
int index = result.indexOf(":"); // 獲取冒號的位置
String s = result.substring(0,index); // 獲取前綴
String path = result.subString(index+1); // 獲取后綴, 即路徑
if(e.equalsIgnoreCase("r")){ // 如果前綴是 r, 重定向
resp.sendRedirect(req.getContextPath()+path);
}else if(e.equalsIgnoreCase("f")){
req.getRequestDispatcher(path).forward(req,resp);
} else {
throw new RuntimeException("您指定的操作:"+s+",當前版本不支持!");
}
} else { // 沒有冒號, 默認為轉發
req.getRequestDispatcher(result).forward(req,resp);
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
參考資料: