水果库存管理系统 进阶1 单sevlet版
上一个基础版,我们是写了好多个Servlet,然后每个不同的操作我们就去找到不同的Servlet去响应请求完成应答。这么写的逻辑好理解,但是如果代码量大了,就会显得比较乱,所以我们想,能否将所有的servlet整合到一个Servlet中呢?还真的可以。
Servlet改动
我们可以将所有servlet整合成一个大的FruitServlet,然后将原本那么多不同的servlet实现的功能写成FruitSerlet中的函数。
首先第一步,创建一个FruitServlet,然后重写servlet方法:
request.setCharacterEncoding("UTF-8");
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
然后下面对这个operate进行判断:
switch(operate){
case "index":
index(request,response);
break
case "add":
add(request,response);
break;
case "del":
del(request,response);
break;
case "edit":
edit(request,response);
break;
case "update":
update(request,response);
break;
default:
throw new RuntimeException("operate值非法!");
其实看这个FruitServlet就能大概理解一些,也就是FruitServlet会接受一个参数(方法名),然后根据这个参数判断要执行什么方法。
比如接收的参数为index,就执行index方法,接收的参数为add,就执行add方法。
所以我们要做的事也很简单,就是把原本不同servlet实现的功能写成FruitServlet中的不同函数。
比如说原本的UpdateServlet是这样的:
public class UpdateServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
response.sendRedirect("index");
}
}
那么现在就得将里面的实现代码,都迁移到FruitServlet的update函数中。
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
response.sendRedirect("fruit.do");
}
之前不是有个 private FruitDAO fruitDAO = new FruitDAOImpl();现在就将这个写到FruitServlet的变量中就行了。
而唯一一处还需要注意的就是:
之前的最后是 response.sendRedirect(“index”);之前是直接让原本的请求去请求index.servlet(通过研究,发现让原本的update.html发送新的请求给index,且这个请求是没参数的),让其获取新的数据库中的数据然后重返页面。
而现在是 response.sendRedirect(“fruit.do”);现在得让原本的请求重新去请求fruit.do(FruitServlet),让其重新获取页面。
(原本以为不太行呀,因为FruitSevlet接收请求时需要传一个参数(方法名呀)但是这里没有。后面想了一想,可以的,这里是重定向,也就是让原本的请求去申请FruitSevlet,原本的请求是update.html,让其重新发一个请求给FruitServlet,且这个请求是没有参数的,所以Fruit.do前面设置如果获取不到方法的参数,就默认执行index方法。所以重定位请求页面时,就是执行index方法,重新渲染后显示页面。
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
还是有用的。
而其它三个方法都是同样道理:
private void edit(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit",fruit);
super.processTemplate("edit",request,response);
}
}
private void del(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
response.sendRedirect("fruit.do");
}
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String fname = request.getParameter("fname");
Integer price = Integer.parseInt(request.getParameter("price")) ;
Integer fcount = Integer.parseInt(request.getParameter("fcount"));
String remark = request.getParameter("remark");
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitDAO.addFruit(fruit);
response.sendRedirect("fruit.do");
}
private void index(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
HttpSession session = request.getSession() ;
Integer pageNo = 1 ;
String oper = request.getParameter("oper");
String keyword = null ;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
pageNo = 1 ;
keyword = request.getParameter("keyword");
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
session.setAttribute("keyword",keyword);
}else{
String pageNoStr = request.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
int fruitCount = fruitDAO.getFruitCount(keyword);
int pageCount = (fruitCount+5-1)/5 ;
session.setAttribute("pageCount",pageCount);
super.processTemplate("index",request,response);
}
}
HTML代码改动
现在重点就在于修改html页面中的逻辑,让原本的请求某个特定servlet改为请求FruitServlet,然后同时传一个要执行的方法的名字参数过来。
比如输入关键词查询的逻辑这里:
<form th:action="@{/fruit.do}" method="post" style="float:left;width:60%;margin-left:20%;">
<input type="hidden" name="oper" value="search"/>
请输入关键字:<input type="text" name="keyword" th:value="${session.keyword}"/>
<input type="submit" value="查询" class="btn"/>
</form>
action="@{/fruit.do},action要改成去找fruit.do,而重要的是下面要加一个,也就是加一个隐藏域将要执行的方法名传过去(search方法)
所以这两句的逻辑就是找到FruitSerlet,然后让其执行search方法,就跟之前找到searchServlet是同样的作用。
修改的地方改成这样:
<td><a th:text="${fruit.fname}" th:href="@{/fruit.do(fid=${fruit.fid},operate='edit')}">苹果</a></td>
有一些则是在JavaScript代码中改:
function delFruit(fid){
if(confirm('是否确认删除?')){
window.location.href='fruit.do?fid='+fid+'&operate=del';
}
}
总结一下:
1.创建一个FruitServlet,然后将之前不同功能的类delServlet,editServlet,updateServlet改为FruitServlet中的不同函数。(记得重定位等代码需要改一些)
2.将html代码中的相应请求不同类的代码改写,改写为请求FruitServlet,同时传一个方法参数(可以是隐藏域发,或者是直接在地址名后面加)
进阶反射代码
如果说一个Sevrlet类中有很多个方法,那么我们做switch-case的时候会写很多很多代码,有没有什么改进呢?
switch(operate){
case "index":
index(request,response);
break;
case "add":
add(request,response);
break;
case "del":
del(request,response);
break;
case "edit":
edit(request,response);
break;
case "update":
update(request,response);
break;
default:
throw new RuntimeException("operate值非法!");
}
太冗长了,有无改进的方法?
有,就是运用反射进制。
反射机制是啥咧?让我们复习一下。
很简单,其实就是说,我们可以获取一个类的属性(比如获取一个类的成员变量,或者获取一个类的方法)。
比如 Class cls = Class.forName(className);Method method = cls.getMethod(methodName);
就是先获取一个类的原始类(给出路径),然后获取其中名字为methodName的方法。
同时,你还可以执行该方法,就是用invoke方法,但是要传一个实例进去。所以现在看一下要我们的switch-case案例可以怎么改:
- 获取FruitServlet的所有方法,然后看请求传过来的名字有没有名字相同的,有的话,执行该方法
- 如何执行该方法呢?就用invoke方法,然后实例传一个this指针进去。
代码如下:
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
Method[] methods=this.getClass().getDeclaredMethods();
for(Method m:methods){
String methodName=m.getName();
if(operate.equals(methodName)){
try {
m.invoke(this,request,response);
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("operate值非法")
}
源代码如下: @WebServlet("/fruit.do") public class FruitServlet extends ViewBaseServlet { private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("UTF-8");
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
//可以获取到该类的方法的数组
Method[] methods=this.getClass().getDeclaredMethods();
for(Method m:methods){
String methodName=m.getName();
if(operate.equals(methodName)){
try {
m.invoke(this,request,response);
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("operate值非法")
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
request.setCharacterEncoding("utf-8");
//2.获取参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
//3.执行更新
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
response.sendRedirect("fruit.do");
}
private void edit(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit",fruit);
super.processTemplate("edit",request,response);
}
}
private void del(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
//super.processTemplate("index",request,response);
response.sendRedirect("fruit.do");
}
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String fname = request.getParameter("fname");
Integer price = Integer.parseInt(request.getParameter("price")) ;
Integer fcount = Integer.parseInt(request.getParameter("fcount"));
String remark = request.getParameter("remark");
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitDAO.addFruit(fruit);
response.sendRedirect("fruit.do");
}
private void index(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
HttpSession session = request.getSession() ;
// 设置当前页,默认值1
Integer pageNo = 1 ;
String oper = request.getParameter("oper");
//如果oper!=null 说明 通过表单的查询按钮点击过来的
//如果oper是空的,说明 不是通过表单的查询按钮点击过来的
String keyword = null ;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1 , keyword应该从请求参数中获取
pageNo = 1 ;
keyword = request.getParameter("keyword");
//如果keyword为null,需要设置为空字符串"",否则查询时会拼接成 %null% , 我们期望的是 %%
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
//将keyword保存(覆盖)到session中
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送过来的请求(比如点击下面的上一页下一页或者直接在地址栏输入网址)
//此时keyword应该从session作用域获取
String pageNoStr = request.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr); //如果从请求中读取到pageNo,则类型转换。否则,pageNo默认就是1
}
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
// 重新更新当前页的值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5 ;
session.setAttribute("pageCount",pageCount);
super.processTemplate("index",request,response);
}
}
总结一下:
1.创建一个FruitServlet,然后将之前不同功能的类delServlet,editServlet,updateServlet改为FruitServlet中的不同函数。(记得重定位等代码需要改一些)
2.将html代码中的相应请求不同类的代码改写,改写为请求FruitServlet,同时传一个方法参数(可以是隐藏域发,或者是直接在地址名后面加)
3.利用反射机制,可以获取到类的方法名的一个数组,然后将传进来的方法名跟数组逐个比较,比较成功的就执行该方法。(反射机制,可以使代码更整洁)
|