[TOC]
# 步驟1:一個路徑對應一個Servlet的弊端
通過觀察
```
http://192.168.10.165:8080/tmall_ssm/admin_category_list
```
可以發現,分類管理需要:增加,刪除,編輯,修改,查詢5個服務端功能。
那么按照傳統的Servlet方式(`web.xml`配置或者使用注解`@WebServlet`),一個路徑對應一個Servlet的思路,就需要設計5個Servlet類。
比如:
```
AddCategoryServlet
DeleteCategoryServlet
EditCategoryServlet
UpdateCategoryServlet
ListCategoryServlet
```
而后臺需要做分類,產品,屬性,產品圖片,用戶,訂單這么6種管理,那么就一共需要30個Servlet,也就是`一個請求對應一個Servlet`,控制器就顯得比較臃腫,寫起來也比較麻煩。
# 步驟2: 對設計進行改進
但是觀察已經實現了分類管理的`可運行的項目`里的代碼,卻發現Servlet只有一個即CategoryServlet。
如圖是對于最后完工了的項目的servlet包里類的截圖,可以發現,每種實體類,對應了一個Servlet,而不是對應了5個,這樣首先從Servlet數量上來講,就大大的減少了。

# 步驟3: 原理流程圖
那么是如何做到一個CategoryServlet類,就能完成本來需要5個Servlet類才能完成的功能的呢?
讓我們借助流程圖來分析,為什么訪問`admin_category_list`的時候,CategoryServlet的list()方法會被調用。
1. 假設訪問路徑是 `http://127.0.0.1:8080/tmall_j2ee/admin_category_list`
2. 過濾器BackServletFilter進行攔截,判斷訪問的地址是否以`admin_`開頭
3. 如果是,那么做如下操作
3.1 取出兩個下劃線之間的值 category
3.2 取出最后一個下劃線之后的值 list
3.3 然后根據這個值,服務端跳轉到categoryServlet,并且把list這個值傳遞過去
4. categoryServlet 繼承了BaseBackServlet,其service方法會被調用。 在service中,借助反射技術,根據傳遞過來的值 list,調用對應categoryServlet 中的方法list() 。
5. 這樣就實現了當訪問的路徑是 `admin_category_list`的時候,就會調用`categoryServlet.list()`方法這樣一個效果。
> 換句話說:
如果訪問的路徑是admin\_category\_add,就會調用categoryServlet.add()方法
如果訪問的路徑是admin\_category\_delete,就會調用categoryServlet.delete()方法
如果訪問的路徑是admin\_category\_edit,就會調用categoryServlet.edit()方法
如果訪問的路徑是admin\_category\_update,就會調用categoryServlet.update()方法
如此這般,一個categoryServlet類,就完成了本來需要5個Servlet類才能完成的功能。

# 步驟4:代碼講解 - BackServletFilter
那么BackServletFilter類到底是如何工作的呢? 接下來我們此類進行代碼講解。
1. 首先在web.xml配置文件中,讓所有的請求都會經過BackServletFilter
```
<filter-mapping>
<filter-name>BackServletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
```
2\. 還是假設訪問的路徑是:
http://127.0.0.1:8080/tmall_j2ee/admin\_category\_list
3\. 在BackServletFilter 中通過request.getRequestURI()取出訪問的uri: /tmall_j2ee/admin\_category\_list
4\. 然后截掉/tmall_j2ee,得到路徑/admin\_category\_list
5\. 判斷其是否以/admin開頭
6\. 如果是,那么就取出兩個\_之間的字符串,category,并且拼接成/CategoryServlet,通過服務端跳轉到/CategoryServlet
7\. 在跳轉之前,還取出了list字符串,然后通過request.setAttribute的方式,借助服務端跳轉,傳遞到categoryServlet里去。
```
package com.dodoke.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
/**
* Servlet Filter implementation class BackServletFilter
*/
public class BackServletFilter implements Filter {
/**
* Default constructor.
*/
public BackServletFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String contextPath = request.getContextPath();
String uri = request.getRequestURI();
uri = StringUtils.remove(uri, contextPath);
if (uri.startsWith("/admin_")) {
// 從admin_category_list截取出category
String temp = StringUtils.substringBetween(uri, "_", "_");
// category首字母大寫與Servlet拼接
String servletPath = StringUtils.capitalize(temp) + "Servlet";
String method = StringUtils.substringAfterLast(uri, "_");
request.setAttribute("method", method);
req.getRequestDispatcher("/" + servletPath).forward(request, response);
return;
}
// pass the request along the filter chain
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
```
# 步驟5:代碼講解 - CategoryServlet和BaseBackServlet
接著流程就到了categoryServlet這里。
服務端跳轉/categoryServlet就到了CategoryServlet這個類里
1. 首先CategoryServlet繼承了BaseBackServlet,而BaseBackServlet又繼承了HttpServlet。
2. 服務端跳轉過來之后,會訪問CategoryServlet的doGet()或者doPost()方法
3. 在訪問doGet()或者doPost()之前,會訪問service()方法
4. BaseBackServlet中重寫了service() 方法,所以流程就進入到了service()中
5. 在service()方法
根據**反射**訪問對應的方法,然后根據對應方法的返回值,進行服務端跳轉、客戶端跳轉、或者直接輸出字符串。
5.1 取到從BackServletFilter中request.setAttribute()傳遞過來的值 list
5.2 根據這個值list,借助**反射機制**調用CategoryServlet類中的list()方法
這樣就達到了CategoryServlet.list()方法被調用的效果
BaseBackServlet
```
package com.dodoke.controller;
import java.lang.reflect.Method;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.dodoke.dao.impl.CategoryDaoImpl;
import com.dodoke.dao.inter.CategoryDao;
import com.dodoke.util.Page;
/**
* Servlet implementation class BaseBackServlet
*/
@WebServlet("/BaseBackServlet")
public abstract class BaseBackServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public abstract String add(HttpServletRequest request, HttpServletResponse response);
public abstract String delete(HttpServletRequest request, HttpServletResponse response);
public abstract String edit(HttpServletRequest request, HttpServletResponse response);
public abstract String update(HttpServletRequest request, HttpServletResponse response);
public abstract String list(HttpServletRequest request, HttpServletResponse response);
protected CategoryDao categoryDao = new CategoryDaoImpl();
public void service(HttpServletRequest request, HttpServletResponse response) {
try {
/* 借助反射,調用對應的方法 */
String method = (String) request.getAttribute("method");
Method m = this.getClass().getMethod(method, javax.servlet.http.HttpServletRequest.class,
javax.servlet.http.HttpServletResponse.class, Page.class);
String redirect = m.invoke(this, request, response).toString();
/* 根據方法的返回值,進行相應的客戶端跳轉,服務端跳轉,或者僅僅是輸出字符串 */
System.out.println(redirect);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
```
CategoryServlet
```
package com.dodoke.controller;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class CategoryServlet
*/
@WebServlet("/CategoryServlet")
public class CategoryServlet extends BaseBackServlet {
@Override
public String add(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return "add";
}
@Override
public String delete(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return "delete";
}
@Override
public String edit(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return "edit";
}
@Override
public String update(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return "update";
}
@Override
public String list(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return "list";
}
}
```
# 步驟6: 一個Servlet類就能滿足CRUD一系列業務要求
通過這樣一種模式,一個Servlet類就能滿足CRUD一系列業務要求
如果訪問的路徑是admin\_category\_list,就會調用categoryServlet.list()方法
如果訪問的路徑是admin\_category\_add,就會調用categoryServlet.add()方法
如果訪問的路徑是admin\_category\_delete,就會調用categoryServlet.delete()方法
如果訪問的路徑是admin\_category\_edit,就會調用categoryServlet.edit()方法
如果訪問的路徑是admin\_category\_update,就會調用categoryServlet.update()方法

- 項目簡介
- 功能一覽
- 前臺
- 后臺
- 開發流程
- 需求分析-展示
- 首頁
- 產品頁
- 分類頁
- 搜索結果頁
- 購物車查看頁
- 結算頁
- 確認支付頁
- 支付成功頁
- 我的訂單頁
- 確認收貨頁
- 評價頁
- 頁頭信息展示
- 需求分析-交互
- 分類頁排序
- 立即購買
- 加入購物車
- 調整訂單項數量
- 刪除訂單項
- 生成訂單
- 訂單頁功能
- 確認付款
- 確認收貨
- 提交評價信息
- 登錄
- 注冊
- 退出
- 搜索
- 前臺需求列表
- 需求分析后臺
- 分類管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性設置
- 用戶管理
- 訂單管理
- 后臺需求列表
- 表結構設計
- 數據建模
- 表與表之間的關系
- 實體類設計
- DAO類設計
- 工具類
- CategoryDao設計
- Service業務類設計
- 后臺-分類管理
- 可運行的項目
- 靜態資源
- FILTER配合SERVLET
- JSP包含關系
- 查詢
- 分頁
- 增加
- 刪除
- 編輯
- 修改
- 后臺其他管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性值設置
- 用戶管理
- 訂單管理