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,(页面找不到)这样的错误,才会出现以上界面奥。

好了,今天更新到这里,我也累了。

冰雪聪明的你,喜欢那种方式呢?

原创粉丝点击