J2EE项目-仿天猫整站day04
后台-分类管理Filter配合Servlet1. 一个路径对应一个Servlet的弊端通过观察:http://127.0.0.1:8080/tmall/admin_category_list可以发现,各类需要:增加,删除,编辑,修改,查询5个服务端功能。按照传统的在web.xml中配置Servlet的方式,一个路径对应一个Servlet的思路,就需要设计5个Servlet类,并且在...
后台-分类管理
Filter配合Servlet
1. 一个路径对应一个Servlet的弊端
通过观察:http://127.0.0.1:8080/tmall/admin_category_list
可以发现,各类需要:增加,删除,编辑,修改,查询5个服务端功能。按照传统的在web.xml中配置Servlet的方式,一个路径对应一个Servlet的思路,就需要设计5个Servlet类,并且在wen.xml中配置5个路径:
- AddCategoryServlet
- DeleteCategoryServlet
- EditCategoryServlet
- UpdateCategoryServlet
- ListCateagoryServlet
而后台需要的分类,产品,属性,产品图片,用户,订单这么6种管理,那么就一共需要30个Servlet,以及在web.xml中对应的配置,那么配置文件就会变得臃肿,并且容易出错。
2. 对设计进行修改
观察代码,发现Servlet轴一个即CategoryServlet,web.xml里也只有CategoryServlet映射,并没有5个映射。
3.原理流程图
CategoryServlet的list()方法会被调用
- 1.假设访问路径是 http://127.0.0.1:8080/tmall/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
4.1首先贼web.xml配置文件中,让所有的请求都会经过BackServletFilter
<url-pattern>/*</url-pattern>
4.2 还是假设访问的路径是:
http://127.0.0.1:8080/tmall/admin_category_list
4.3 在BackServletFilter中通过request.getRequestURI()访问取出的uri:/tmall/admin_category_list
4.4 然后截断/tmall,得到路径/admin_category_list
4.5 判断是否以/admin开头
4.6 如果是,那么就取出两个_之间的字符串,category,并且拼接成/categoryServlet,通过服务端跳转到/categoryServlet
4.7 在跳转之前,还取出了list字符串,然后通过request.setAttrbute的方式,借助服务端跳转,传递到categoryServlet里去。
package tmall.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;
public class BackServletFilter implements Filter {
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String contextPath=request.getServletContext().getContextPath();
String uri = request.getRequestURI();
uri =StringUtils.remove(uri, contextPath);
if(uri.startsWith("/admin_")){
String servletPath = StringUtils.substringBetween(uri,"_", "_") + "Servlet";
String method = StringUtils.substringAfterLast(uri,"_" );
request.setAttribute("method", method);
req.getRequestDispatcher("/" + servletPath).forward(request, response);
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
}
<filter>
<filter-name>BackServletFilter</filter-name>
<filter-class>tmall.filter.BackServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BackServletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5. 代码讲解
流程就到categoryServlet,根据web.xml中的配置:
<servlet-name>CategoryServlet</servlet-name>
<url-pattern>/categoryServlet</url-pattern>
服务端跳转/categoryServlet就到了CategoryServlet这个类里
- 1. 首先CategoryServlet继承了BaseBackServlet,而BaseBackServlet又继承了HttpServlet
- 2. 服务端跳转过来之后,会访问CategoryServlet的doGet()或者doPost()方法
- 3. 在访问doGet()或者doPost()之前,会访问service()方法
- 4. BaseBackServlet中重写了service() 方法,所以流程就进入到了service()中
- 5. 在service()方法中有三块内容
5.1 第一块是获取分页信息
5.2 第二块是根据反射访问对应的方法
5.3 第三块是根据对应方法的返回值,进行服务端跳转、客户端跳转、或者直接输出字符串。 - 6. 第一块和第三块放在后面讲解,这里着重讲解第二块是根据反射访问对应的方法
6.1 取到从BackServletFilter中request.setAttribute()传递过来的值 list
6.2 根据这个值list,借助反射机制调用CategoryServlet类中的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()方法
JSP包含关系
1. 分类查询对应的JSP文件
分类查询对应的JSP文件时listCategory.jsp。
listCategory.jsp用到了4个公共包含文件
- 1. <%@include file="../include/admin/adminHeader.jsp"%>
- 2. <%@include file="../include/admin/adminNavigator.jsp"%>
- 3. <%@include file="../include/admin/adminPage.jsp"%>
- 4. <%@include file="../include/admin/adminFooter.jsp"%>
2. adminHeader.jsp
每个后台页面都在一开始使用了adminHeader.jsp
2.1 表示本页面使用HTML5的技术
<!DOCTYPE html>
2.2 jsp指令
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
contentType="text/html;charset=UTF-8"告诉浏览器使用UTF-8进行中文编码识别
pageEncoding="UTF-8"本jsp上的中文文字,使用UTF-8进行编码
isELIgnored="false"本jsp上会使用EL表达式
2.3 引入JSTL
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix='fmt' %>
使用c和fmt两种标准标签库
2.4 引入js和css
<script src="js/jquery/2.0.0/jquery.min.js"></script>
<link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
<script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
<link href="css/back/style.css" rel="stylesheet">
2.5 预先定义一些判断输入框的函数,方便后面使用
<script>
function checkEmpty(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
return true;
}
function checkNumber(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
if(isNaN(value)){
alert(name+ "必须是数字");
$("#"+id)[0].focus();
return false;
}
return true;
}
function checkInt(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
if(parseInt(value)!=value){
alert(name+ "必须是整数");
$("#"+id)[0].focus();
return false;
}
return true;
}
2.6 对于删除超链接,都需要进行确认操作
$(function(){
$("a").click(function(){
var deleteLink = $(this).attr("deleteLink");
console.log(deleteLink);
if("true"==deleteLink){
var confirmDelete = confirm("确认要删除");
if(confirmDelete)
return true;
return false;
}
});
})
3. adminNavigator.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<div class="navitagorDiv">
<nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
<img style="margin-left:10px;margin-right:0px" class="pull-left" src="img/site/tmallbuy.png" height="45px">
<a class="navbar-brand" href="#nowhere">天猫后台</a>
<a class="navbar-brand" href="admin_category_list">分类管理</a>
<a class="navbar-brand" href="admin_user_list">用户管理</a>
<a class="navbar-brand" href="admin_order_list">订单管理</a>
</nav>
</div>
4. adminPage.jsp
这是分页JSP。分页功能不仅仅有前端效果,还需要结合服务端传递过来的数据综合才能起作用。
5. adminFooter.jsp
页脚部分。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<div class="footer">
</div>
</body>
</html>
查询
1. MVC设计思想
根据MVC设计模式的思想,做J2EE web应用,从MVC的角度来看,就是把不同的数据显示在不同的页面上。
数据就是模型(bean,dao)
页面就是视图(jsp)
控制不同的模型显示在不同的视图上,这件事就是由控制器来完成(servlet)
所以分类管理,从MVC的角度来看,就是把多条分类Category数据放在一个集合里,让listCategory.jsp这个视图去显示出来。
2. list()方法
根据filter+servlet的设计模式,访问地址:
http://127.0.0.1:8080/tmall/admin_category_list
会导致CategoryServlet的list犯法被调用。根据MVC设计思想,CategoryServlet充当的是C-控制器的角色。那么在list()方法里做的事情是,取出数据,并且交由jsp显示。
第二行: 通过categoryDAO取得数据集合 cs
第六行: 通过request.setAttribute 放在 “thecs" 这个key中,为后续服务端跳转到jsp之后使用。
第九行:return "admin/listCategory.jsp"; 服务端跳转到视图listCategory.jsp页面。
注: CategoryServlet的list()方法中可以直接使用categoryDAO,是因为其父类BaseBackServlet在属性中声明了
protected CategoryDAO categoryDAO = new CategoryDAO();
作为BaseBackServlet的子类,CategoryServlet可以直接使用该protected修饰的属性。除此之外,list里还有page对象。
public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
List<Category> cs = categoryDAO.list(page.getStart(),page.getCount());
int total = categoryDAO.getTotal();
page.setTotal(total);
request.setAttribute("thecs", cs);
request.setAttribute("page", page);
return "admin/listCategory.jsp";
}
3. 服务端跳转
list()方法返回字符串"admin/listCategory.jsp"就导致服务端跳转到了页面"admin/listCategory.jsp"。
为什么返回这个字符串,就可以实现服务端跳转呢?在Servlet中进行服务端跳转不是要如下格式吗?
request.getRequestDispatcher("xxx.jsp").forward(request,response);
这就要结合CategoryServlet的父类BaseBackServlet来理解了。在BaseBackServlet的70行,在接货组反射机制调用了list()方法之后,获取返回值redirect。
- 如果redirect是以@开头的字符串,那么就进行客户端跳转
- 如果redirect是以%开头的字符串,那么就直接输出字符串
- 如果都不是,则进行服务端跳转
String redirect = m.invoke(this,request, response,page).toString();
/*根据方法的返回值,进行相应的客户端跳转,服务端跳转,或者仅仅是输出字符串*/
if(redirect.startsWith("@"))
response.sendRedirect(redirect.substring(1));
else if(redirect.startsWith("%"))
response.getWriter().print(redirect.substring(1));
else
request.getRequestDispatcher(redirect).forward(request, response);
4. listCategory.jsp
最后就是视图listCategory.jsp了。作为视图,担当的主要角色就是显示数据。所以关键就是从44行开始,借助JSTL的c:forEach标签遍历从CategoryServlet的list()的request.setAttrbute("thecs",cs);传递过来的集合。
<c:forEach items="${thecs}" var="c">
分页
1. Page.java
Page这个类专门为分页提供必要信息。
属性:
- int start;开始位置
- int count;每页显示的数量
- int total;总共有多少条数据
- String param;参数
方法:
- getTotalPage 根据每页显示的数量count以及总共有多少条数据total,计算出总共有多少页
- getLast 计算出最后一页的数值是多少
- isHasPrevious 判断是否有前一页
- isHasNext 判断是否有后一页
package tmall.util;
public class Page {
int start;
int count;
int total;
String param;
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Page(int start, int count) {
super();
this.start = start;
this.count = count;
}
public boolean isHasPreviouse(){
if(start==0)
return false;
return true;
}
public boolean isHasNext(){
if(start==getLast())
return false;
return true;
}
public int getTotalPage(){
int totalPage;
// 假设总数是50,是能够被5整除的,那么就有10页
if (0 == total % count)
totalPage = total /count;
// 假设总数是51,不能够被5整除的,那么就有11页
else
totalPage = total / count + 1;
if(0==totalPage)
totalPage = 1;
return totalPage;
}
public int getLast(){
int last;
// 假设总数是50,是能够被5整除的,那么最后一页的开始就是45
if (0 == total % count)
last = total - count;
// 假设总数是51,不能够被5整除的,那么最后一页的开始就是50
else
last = total - total % count;
last = last<0?0:last;
return last;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
}
2. 获取分页参数
获取分页信息是在BaseBackServlet的service方法中
int start= 0;
int count = 5;
try {
start = Integer.parseInt(request.getParameter("page.start"));
} catch (Exception e) {
}
try {
count = Integer.parseInt(request.getParameter("page.count"));
} catch (Exception e) {
}
Page page = new Page(start,count);
获取网页上传来的开始位置,以及每页需要显示的数量。如果传递数据过来,开始位置取0,每页显示的数量取默认值:5。
3. 基于分页参数,获取数据
接着就是通过反射,访问了CategServlet的list方法,并在其中通过categoryDAO,基于page的start,和count值,获取分页数据。
List<Category> cs = categoryDAO.list(page.getStart(),page.getCount());
4. 为page对象设置总数
除此之外,还为page对象添加了总数。
int total = categoryDAO.getTotal();
page.setTotal(total);
为什么要加这个总数?因为在分页显示的时候,要根据这个总数来判断一共有多少页面,最后一页是多少。
接着通过:
request.setAttrbute("page",page);
把page对象,传参到了listCategory.jsp页面。
更多推荐
所有评论(0)