前言:為了深入web原理,本項目沒有使用框架,主要描述了從請求到頁面展現的思路,詳情請見文末的具體項目
一、為什么要用filter?直接servlet實現不就行了
因為天貓這樣的項目需要很多servlet處理具體種類的業務,比如后台的管理頁面有增刪改查,訂單頁面也有增刪改查,每一個操作都需要寫一個servlet。使用filter+反射可以解決這個冗余的問題。
二、從瀏覽器輸入路徑到filter
舉個例子,我們本地天貓項目的首頁地址為http://127.0.0.1:8080/tmall/forehome,tmall此處是項目名,我們在web.xml設置filter攔截所有請求,url-pattern設置的/*,所有請求都會映射到tmall.filter.ForeServletFilter這個Java類。
<filter>
<filter-name>ForeServletFilter</filter-name>
<filter-class>tmall.filter.ForeServletFilter</filter- class>
</filter>
<filter-mapping>
<filter-name>ForeServletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在filter的doFilter方法里面判斷請求,如是/fore開頭的,跳轉到前台對應的servlet再做具體邏輯數據處理。
@WebFilter(filterName = "ForeServletFilter")
public class ForeServletFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//獲取從項目名開始的路徑
String uri = request.getRequestURI();
//獲取項目名路徑
String contextPath = request.getContextPath();
//獲得路徑后綴
String path = StringUtils.remove(uri, contextPath);
if(path.startsWith("/fore")&&!path.startsWith("/foreServlet")) {
//需要將方法名取出並且放到session里面
String method = StringUtils.substringAfterLast(path, "fore");
request.setAttribute("method", method);
//跳轉向foreServlet
request.getRequestDispatcher("/foreServlet").forward(request, response);
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
三、從filter跳轉到servlet
web.xml設置servlet映射,跳轉后來到ForeServlet這個Java類。
<servlet>
<servlet-name>ForeServlet</servlet-name>
<servlet-class>tmall.servlet.ForeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ForeServlet</servlet-name>
<url-pattern>/foreServlet</url-pattern>
</servlet-mapping>
這里我們只寫具體方法而不寫service方法,讓它繼承其父類BaseForeServlet的service方法。這里的具體方法就包括了前面所說的各種后台的管理頁面和訂單頁面的增刪改查方法,此處舉個首頁的例子,定義一個home()方法:
public class ForeServlet extends BaseForeServlet {
public String home(HttpServletRequest request, HttpServletResponse response, Page page) {
List<Category> cs = categoryDAO.list(page.getStart(), page.getCount());
//填充數據
request.setAttribute("cs", cs);
new ProductDAO().fill(cs);
new ProductDAO().fillByRow(cs);
return "home.jsp";
}
}
四、使用反射調用具體方法
那么是如何調用到這個子類servlet的home()方法的呢?
這里我們通過BaseForeServlet的service方法,首先截取路徑里面的方法名,然后使用反射執行子類的home()方法,將數據填充好。
@WebServlet(name = "BaseForeServlet")
public abstract class BaseForeServlet extends HttpServlet {
protected CategoryDAO categoryDAO = new CategoryDAO();
protected OrderDAO orderDAO = new OrderDAO();
protected OrderItemDAO orderItemDAO = new OrderItemDAO();
protected ProductDAO productDAO = new ProductDAO();
protected ProductImageDAO productImageDAO = new ProductImageDAO();
protected PropertyDAO propertyDAO = new PropertyDAO();
protected PropertyValueDAO propertyValueDAO = new PropertyValueDAO();
protected ReviewDAO reviewDAO = new ReviewDAO();
protected UserDAO userDAO = new UserDAO();
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = (String)req.getAttribute("method");
Method md = null;
Page page = new Page(1, 5);
try {
md = this.getClass().getMethod(method, javax.servlet.http.HttpServletRequest.class,
javax.servlet.http.HttpServletResponse.class, Page.class);
String redirect = md.invoke(this, req, resp, page).toString();
if (redirect.startsWith("@")) {
resp.sendRedirect(redirect);
}else if (redirect.startsWith("%")) {
resp.sendRedirect(redirect);
}else {
req.getRequestDispatcher(redirect).forward(req, resp);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
五、頁面呈現
foreServlet返回home.jsp到父類,父類判斷路徑前綴,然后跳轉到對應的頁面。
String redirect = md.invoke(this, req, resp, page).toString();
if (redirect.startsWith("@")) {
resp.sendRedirect(redirect);
}else if (redirect.startsWith("%")) {
resp.sendRedirect(redirect);
}else {
req.getRequestDispatcher(redirect).forward(req, resp);
}
四、總結
本項目我們還暗含了MVC的設計模式,Model是Dao、Bean等數據,View是jsp頁面,Control是servlet。因為篇幅有限,很多內容都沒有展示,具體項目詳見網上一個大神的項目:天貓J2EE項目