JavaWeb-Servlet2-Method
来源:互联网 发布:php exec 返回值为空 编辑:程序博客网 时间:2024/06/01 10:01
话说:
在JavaWeb-Servlet-News(CURD)这篇博客里面,我们用Servlet改造了新闻(News),在JavaWeb-Servlet-Properties、JavaWeb-Servlet-Reflect两篇里面,分别做了优化。今天继续走在优化的道路上吧!
目标
1、优化多个Servlet的局面
1)根据参数选择不同的Servlet
2)利用反射机制,动态选择调用方法
2、错误页面处理(404、500、400)
1、优化多个Servlet的局面
1)根据参数选择不同的Servlet
细心的读者会发现,之前笔者用Servlet来写CURD的时候,每实现一个功能,就建立了一个对应的Servlet,如图:
这样很麻烦,一个项目里面,有几十个功能,那都这么写……..会哭的。所以嘛,我们希望可以再一个Servlet里面写多个Servlet,或者实现类似的功能。我们创建一个NewsServlet,希望用它来达到一劳永逸的效果。
上代码:
在Servlet包下面新建一个NewsServlet类:
package com.hmc.jdbc.news.servlet;import com.hmc.jdbc.news.dao.NewsDao;import com.hmc.jdbc.news.model.News;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;/** * User:Meice * 2017/10/5 */public class NewsServlet extends HttpServlet { NewsDao nd = new NewsDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { if("list".equals(op)) { System.out.println("调用newsShow()方法"); }else if("add".equals(op)) { System.out.println("调用newsAdd()方法"); }else if("update".equals(op)) { System.out.println("调用newsUpdate()方法"); }else if("del".equals(op)) { System.out.println("调用newsDel()方法"); }else { System.out.println("参数传递有误"); } }else{ System.out.println("参数缺失"); } }}
配置web.xml如下:
(为了保持思维连贯性,把整个web.xml配置都贴了出来,请看最后那一组配置)
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!--news Add Servlet--> <servlet> <servlet-name>newsAdd</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsAddServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>newsAdd</servlet-name> <url-pattern>/newsAdd.do</url-pattern> </servlet-mapping> <!--news Show Servlet--> <servlet> <servlet-name>newsShow</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsShowServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>newsShow</servlet-name> <url-pattern>/newsShow.do</url-pattern> </servlet-mapping> <!-- news Update Servlet--> <servlet> <servlet-name>newsUpdate</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsUpdateServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>newsUpdate</servlet-name> <url-pattern>/newsUpdate.do</url-pattern> </servlet-mapping> <!--news Update Do Servlet--> <servlet> <servlet-name>newsUpdateDo</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsUpdateDoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>newsUpdateDo</servlet-name> <url-pattern>/newsUpdateDo.do</url-pattern> </servlet-mapping> <!--news Del Servlet--> <servlet> <servlet-name>newsDel</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsDelServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>newsDel</servlet-name> <url-pattern>/newsDel.do</url-pattern> </servlet-mapping> <!--news Servlet 这个Servlet可以处理所有操作;只要你给我一个参数--> <servlet> <servlet-name>news</servlet-name> <servlet-class>com.hmc.jdbc.news.servlet.NewsServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>news</servlet-name> <url-pattern>/news.do</url-pattern> </servlet-mapping></web-app>
这里为了简单阐述清楚访问的情况,我们先简单的输出结果,看是否如此:
假如你在页面访问:
localhost:8080/JavaWeb_Servlet/news.do?op=list
localhost:8080/JavaWeb_Servlet/news.do?op=add
localhost:8080/JavaWeb_Servlet/news.do?op=update
localhost:8080/JavaWeb_Servlet/news.do?op=del
,控制台就输出调用对应的方法。因此,我们只需要把之前其他类似NewsAddServlet、NewsDelServlet…中的代码拷贝到对应位置即可,对跳转做酌情修改。
为了让界面更加整洁,我就补充两个功能的代码:list和del;补充后代码如下:
package com.hmc.jdbc.news.servlet;import com.hmc.jdbc.news.dao.NewsDao;import com.hmc.jdbc.news.model.News;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;/** * User:Meice * 2017/10/5 */public class NewsServlet extends HttpServlet { NewsDao nd = new NewsDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { if("list".equals(op)) { // System.out.println("调用newsShow()方法"); //1 处理编码(抽离出来了,在doPost()一开始统一处理) //2 处理业务逻辑,调用方法 List<News> list = nd.list(); //3 存储到req中 req.setAttribute("list",list); req.getRequestDispatcher("newsShow.jsp").forward(req,resp); }else if("add".equals(op)) { // System.out.println("调用newsAdd()方法"); }else if("update".equals(op)) { System.out.println("调用newsUpdate()方法"); }else if("del".equals(op)) { // System.out.println("调用newsDel()方法"); //1 接受参数 int id = 0; String strId = req.getParameter("id"); if(strId != null && !"".equals(strId)) { id = Integer.valueOf(strId); } //2 业务逻辑处理(按照id删除对应新闻) int result = nd.newsDel2(id); //3 页面跳转 if(result >0 ) { req.getRequestDispatcher("newsShow.do").forward(req,resp); }else { req.getRequestDispatcher("newsShow.do").forward(req,resp); } }else { System.out.println("参数传递有误"); } }else{ System.out.println("参数缺失"); } }}
总结:
以上代码,把设置编码抽离了出来,统一在doPost()方法里面设置,只用设置一次;NewsDao实例化也是在类一开始就实例化了,因为后面的方法基本都要用到,未避免多次实例化,占用内存和资源,只实例化一次。
然鹅:如果功能一多起来,看起来还是混乱,不便于整理。所以,我们把相关功能方法封装起来,在判断的时候直接调用方法。改进如下:
package com.hmc.jdbc.news.servlet;import com.hmc.jdbc.news.dao.NewsDao;import com.hmc.jdbc.news.model.News;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;/** * User:Meice * 2017/10/5 */public class NewsServlet extends HttpServlet { NewsDao nd = new NewsDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { if("list".equals(op)) { // System.out.println("调用newsShow()方法"); list(req,resp); }else if("add".equals(op)) { // System.out.println("调用newsAdd()方法"); }else if("update".equals(op)) { System.out.println("调用newsUpdate()方法"); }else if("del".equals(op)) { // System.out.println("调用newsDel()方法"); Del(req,resp); }else { System.out.println("参数传递有误"); } }else{ System.out.println("参数缺失"); } } //未避免代码混乱,把显示新闻列表封装成方法 private void list(HttpServletRequest req,HttpServletResponse resp) { List<News> list = nd.list(); //3 存储到req中 req.setAttribute("list",list); try { req.getRequestDispatcher("newsShow.jsp").forward(req,resp); } catch (ServletException e) { } catch (IOException e) { } } //同理,把删除封装成方法 private void Del(HttpServletRequest req,HttpServletResponse resp) { //1 接受参数 int id = 0; String strId = req.getParameter("id"); if(strId != null && !"".equals(strId)) { id = Integer.valueOf(strId); } //2 业务逻辑处理(按照id删除对应新闻) int result = nd.newsDel2(id); //3 页面跳转 try { if(result >0 ) { req.getRequestDispatcher("newsShow.do").forward(req,resp); }else { req.getRequestDispatcher("newsShow.do").forward(req,resp); } } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
然鹅,这表面看起来简洁了些,本质还不是需要自己去调用,下面我们利用反射机制,利用Method类来实现动态选择方法(自动选择)
2)利用反射机制,动态选择调用方法
要实现的目标就是避免用if () else{}来判断调用哪个方法。你参数传递的是什么方法名(?op= ),我就调用与参数op同名的方法。
package com.hmc.jdbc.news.servlet;import com.hmc.jdbc.news.dao.NewsDao;import com.hmc.jdbc.news.model.News;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.List;/** * User:Meice * 2017/10/5 */public class NewsServlet extends HttpServlet { NewsDao nd = new NewsDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { try { //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似 Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class); /** * 调用invoke()方法;属于java.lang.reflect下面的类 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数 */ method.invoke(this,req,resp); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }else{ System.out.println("参数缺失"); } } //未避免代码混乱,把显示新闻列表封装成方法 private void list(HttpServletRequest req,HttpServletResponse resp) { List<News> list = nd.list(); //3 存储到req中 req.setAttribute("list",list); try { req.getRequestDispatcher("newsShow.jsp").forward(req,resp); } catch (ServletException e) { } catch (IOException e) { } } //同理,把删除封装成方法 private void Del(HttpServletRequest req,HttpServletResponse resp) { //1 接受参数 int id = 0; String strId = req.getParameter("id"); if(strId != null && !"".equals(strId)) { id = Integer.valueOf(strId); } //2 业务逻辑处理(按照id删除对应新闻) int result = nd.newsDel2(id); //3 页面跳转 try { if(result >0 ) { req.getRequestDispatcher("newsShow.do").forward(req,resp); }else { req.getRequestDispatcher("newsShow.do").forward(req,resp); } } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
总结:
1、获取Method类的对象
用Class的方法来获取Method对象,前提是给我一个和想要调用方法名相同的String,和参数的类。
后面的各种框架,就很简单了,调用什么方法加注解,而且对post 、get方法区分很明确。
2、调用反射类Method的invoke()方法
调用方法也很简单,给方法所在的类,和方法包含的参数即可。之所以知道调用方法名和op相同的方法,是因为你创建了Method这个对象,这个对象就根据你传过来的参数op底层映射成了对应名称相等的方法。
这样就简单了,想用什么方法NewsUpdate、NewsAdd等方法直接接着写既可以啦。
这里还存在一个问题,假如我有不同的业务类,需要调用,那么遇到的问题和我们优化查最后一步相似,我们也需要写重复类似调用Method类的方法吗?
所以,封装起来。新建一个BaseServlet类,把共性代码封装,然后这个NewsServlet的实例extends一下就好。这里我们不用重写doGet()
doPost(),我们重写Service,不论什么请求,都可以处理。
BaseServlet
package com.hmc.jdbc.news.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * User:Meice * 2017/10/5 */public class BaseServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { try { //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似 Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class); /** * 调用invoke()方法;属于java.lang.reflect下面的类 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数 */ //如果不取消访问权限检查,会报IllegalAccessException异常 method.setAccessible(true); method.invoke(this,req,resp); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }else{ System.out.println("参数缺失"); } }}
NewsServlet改造后变成这样:
package com.hmc.jdbc.news.servlet;import com.hmc.jdbc.news.dao.NewsDao;import com.hmc.jdbc.news.model.News;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;/** * User:Meice * 2017/10/5 */public class NewsServlet extends BaseServlet { NewsDao nd = new NewsDao(); //未避免代码混乱,把显示新闻列表封装成方法 private void list (HttpServletRequest req, HttpServletResponse resp){ List<News> list = nd.list(); //3 存储到req中 req.setAttribute("list", list); try { req.getRequestDispatcher("newsShow.jsp").forward(req, resp); } catch (ServletException e) { } catch (IOException e) { } } //同理,把删除封装成方法 private void Del(HttpServletRequest req,HttpServletResponse resp) { //1 接受参数 int id = 0; String strId = req.getParameter("id"); if (strId != null && !"".equals(strId)) { id = Integer.valueOf(strId); } //2 业务逻辑处理(按照id删除对应新闻) int result = nd.newsDel2(id); //3 页面跳转 try { if (result > 0) { req.getRequestDispatcher("newsShow.do").forward(req, resp); } else { req.getRequestDispatcher("newsShow.do").forward(req, resp); } } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
这里只写了CURD的查,只要查没问题,其他直接把方法搬过来即可。
需要注意,如果没有设置method.setAccessible(true)的话,因为我们的NewsServlet中的方法是Private的,所以会报访问权限检查异常。
java.lang.IllegalAccessException: Class com.hmc.jdbc.news.servlet.BaseServlet can not access a member of class com.hmc.jdbc.news.servlet.NewsServlet with modifiers "private"
这个异常很熟悉了,之前在优化BaseDao实例化Field对象的时候,遇到过。因为是反射类,所以他们一般都容易报这样的异常。可以把Private变为public?这样确实就不会访问检查了。不过,这样就得不偿失了,我们目的就是要Private,违背了封装的本意,因小失大。
至此,我们的Servlet优化告一段落。优化的过程,如同修行啊,嘻嘻;
温故而知新。
然鹅,今天的内容还没有结束,下午出去吃了一顿好的,所以在来点产出把!也请感兴趣滴读者有耐心看下去。以下内容比较好玩。
2、错误页面处理(404、500、400)
一旦访问不小心,访问到了我们没有写的方法,比如
localhost:8080/JavaWeb_Servlet?op=list6666
我们就没有list6666()这样的方法,怎么调用?所以界面什么也不显示,多尴尬!同时,后台报错:
java.lang.NoSuchMethodException: com.hmc.jdbc.news.servlet.NewsServlet.list34(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
找不到方法丫!所以,为了避免这样,我们起码给客户一个反馈,出现一个页面,来提示一下。以挽回我们编程人员的一点点面子。奥,报错啊。我知道的,你看看那个错误页面,就是我写的,一切都在掌握之中,哈哈。
不让程序抛出异常,而是访问我们事先准备好的错误页面,有2种方法。
法1:在try() {}代码块中写错误页面跳转代码
法2:在web.xml中做全局错误页面配置
法1:在try() {}代码块中写跳转代码
1)先准备一个错误显示页面,以及显示图片。
页面error.jsp
<%-- User: Meice Date: 2017/10/5 Time: 21:33--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>找小美处理吧</title></head><body> <h2>嘻嘻,页面不见了,请不要慌张,不要失望</h2> <img src="images/error.jpg" alt="请找程序猿小美处理奥"></body></html>
修改后,整体布局是这样的:
修改抛出异常代码:
请只用关注: resp.sendRedirect(“error.jsp”);这个位置就好。可惜的是MarkDown不能标记颜色。
package com.hmc.jdbc.news.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * User:Meice * 2017/10/5 */public class BaseServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义 String op = req.getParameter("op"); if(op !=null && !"".equals(op)) { try { //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似 Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class); /** * 调用invoke()方法;属于java.lang.reflect下面的类 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数 */ //如果不取消访问权限检查,会报IllegalAccessException异常 method.setAccessible(true); method.invoke(this,req,resp); } catch (NoSuchMethodException e) { //这里,我们来掌控,不要让它抛出异常即可。因为不需要携带什么参数,有请重定向上场! //e.printStackTrace(); resp.sendRedirect("error.jsp"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }else{ System.out.println("参数缺失"); } }}
有个小细节需要注意下:
resp.sendRedirect(“error.jsp”);如果加上/的话,直接从根目录开始访问。比如我们访问路径:
http://localhost:8080/JavaWeb_Servlet/news.do?op=list6666
直接变成这样:
http://localhost:8080/error.jsp
所以,应该这么访问,就会默认带上上下文路径(项目路径):
resp.sendRedirect(“error.jsp”);
提示页面如下:
法2:在web.xml中做全局错误页面配置
除了在代码里面写异常处理跳转,web.xml还提供了直接进行页面跳转的方式:
配置web.xml错误页面:
<!--错误页面配置--> <error-page> <!--表示页面错误类型404 400 500之类的--> <error-code>404</error-code> <!--这里配置错误页面位置--> <location>/error.jsp</location> <!--这个配置是配置错误类型类似:NullPointException --> <!-- <exception-type></exception-type>--> </error-page>
注意,这里节点也是有顺序的。同时,要确保你页面错误是404,(页面找不到)这样的错误,才会出现以上界面奥。
好了,今天更新到这里,我也累了。
冰雪聪明的你,喜欢那种方式呢?
- JavaWeb-Servlet2-Method
- javaweb之servlet2
- servlet2
- servlet2
- Servlet2
- 将Servlet3.0版本的JavaWeb项目 回退到Servlet2.5版本
- servlet2见解
- Servlet2/3
- servlet2 --6.16
- 旧的java工程(J2EE4转J2EE6 tomcat6 转 tomcat8 Java6转Java8 Servlet2转Servlet3 Java工程转Javaweb工程)
- servlet2.3规范
- servlet2.4过滤器
- Servlet2.3规范
- Servlet2.4新特性
- Servlet2.3规范
- 韩顺平servlet2
- 41 Servlet2 多线程
- servlet2-get和post
- java基本类
- bzoj 4987: Tree 树形dp
- Babellua
- hdu 2544 最短路(模板)
- 【java核心技术笔记】接口与内部类
- JavaWeb-Servlet2-Method
- 九度OJ题目1144:Freckles
- nyoj199 无线覆盖问题
- ERROR 1045 (28000)及ERROR 1054 (42S22)的解决方法
- Java基础部分第十五节
- 模板:Dijkstra 队列优化
- dubbo相关(一) zookeeper安装与启动
- 计蒜客 Barty's Computer hash求字符串前缀和后缀
- C++表达式求值(Stack and Expression)加州大学伯克利分校计算机专业数据结构与算法作业