老杜 JavaWeb 讲解(十二) ——oa项目的改造(模板方法)

(十三)对单表操作的优化

对应视频:

33-Servlet中的注解式开发

34-使用模板方法设计模式解决类爆炸

在一个web应用中应该如何完成资源的跳转?


在一个web应用中通过两种方式,可以完成资源的跳转:

  • 转发

  • 重定向

转发和重定向有什么区别?

代码的区别:

  • 转发:
// 获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list");
// 调用请求转发器对象的forward方法完成转发
dispatcher.forward(request, response);

// 合并一行代码
request.getRequestDispatcher("/dept/list").forward(request, response);
// 转发的时候是一次请求,不管你转发了多少次。都是一次请求。
// AServlet转发到BServlet,再转发到CServlet,再转发到DServlet,不管转发了多少次,都在同一个request当中。
// 这是因为调用forward方法的时候,会将当前的request和response对象传递给下一个Servlet。
  • 重定向
//重定向
//重定向时的路径需要添加项目名。
//response对象将  equest.getContextPath()+"/b"  路径响应给浏览器。
//在这个过程中,浏览器又自发的向浏览器发送了一次请求:http://localhost:8080/equest.getContextPath()/b
//最终,浏览器地址栏上的地址会发生改变,显示最后一次的请求的路径。
response.sendRedirect(request.getContextPath()+"/b");

形式区别:

  • 转发是一次请求。

  • 重定向是两次请求。

本质区别:

  • 转发:是由WEB服务器来控制的。A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的。
  • 重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。

例子生动描述转发和重定向:

  • 借钱(转发:发送了一次请求)
    • 杜老师没钱了,找张三借钱,其实张三没有钱,但是张三够义气,张三自己找李四借了钱,然后张三把这个钱给了杜老师,杜老师不知道这个钱是李四的,杜老师只求了一个人。杜老师以为这个钱就是张三的。
  • 借钱(重定向:发送了两次请求)
    • 杜老师没钱了,找张三借钱,张三没有钱,张三有一个好哥们,叫李四,李四是个富二代,于是张三将李四的家庭住址告诉了杜老师,杜老师按照这个地址去找到李四,然后从李四那里借了钱。显然杜老师在这个过程中,求了两个人。并且杜老师知道最终这个钱是李四借给俺的。

转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?

  • 如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制。
  • 剩下所有的请求均使用重定向。(重定向使用较多。)

跳转的下一个资源有没有要求呢?必须是一个Servlet吗?

  • 不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML.....

  • 转发会存在浏览器的刷新问题(使用重定向可以避免)。

Servlet注解,简化配置

  • 分析oa项目中的web.xml文件

    • 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml文件会非常庞大,有可能最终会达到几十兆。
    • 在web.xml文件中进行servlet信息的配置,显然开发效率比较低,每一个都需要配置一下。
    • 而且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?可以的。
  • Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?

    • 开发效率高,不需要编写大量的配置信息。直接在java类上使用注解进行标注。
    • web.xml文件体积变小了。
  • 并不是说注解有了之后,web.xml文件就不需要了:

    • 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
    • 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。
  • 我们的第一个注解:jakarta.servlet.annotation.WebServlet

    • 在Servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?
      • name属性:用来指定Servlet的名字。等同于:
      • urlPatterns属性:用来指定Servlet的映射路径。可以指定多个字符串。
      • loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet。等同于:
      • value属性:当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略的。
      • 注意:不是必须将所有属性都写上,只需要提供需要的。(需要什么用
      • 什么。)
      • 注意:属性是一个数组,如果数组中只有一个元素,使用该注解的时候,属性值的大括号可以省略。
  • 注解对象的使用格式:

    • @注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)

通过反射获取注解信息:

package com.zwm.javaweb.servlet;

import jakarta.servlet.annotation.WebServlet;

/**
 * @author 猪无名
 * @date 2023/7/21 23 59
 * discription:
 */
public class Test {
    public static void main(String[] args) throws Exception{

        //反射获取类
        Class<?> helloServletClass = Class.forName("com.zwm.javaweb.servlet.HelloServlet");

        //获取类上面的注解对象
        //先判断这个类上有没有注解对象,有的话就获取。
//        boolean annotationPresent = helloServletClass.isAnnotationPresent(WebServlet.class);
//        System.out.println(annotationPresent);//true
        if(helloServletClass.isAnnotationPresent(WebServlet.class)){
            //获取这个类上面的注解对象
            WebServlet annotation = helloServletClass.getAnnotation(WebServlet.class);

            //获取注解的value属性值
            String[] value = annotation.value();

            for (String s : value) {
                System.out.println(s);
            }

        }

    }
}

使用模板方法设计模式优化oa项目

上面的注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。

  • 一个单标的CRUD,就写了7个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大。)
  • 怎么解决这个类爆炸问题?可以使用模板方法设计模式。

怎么解决类爆炸问题?

  • 以前的设计是一个请求一个Servlet类。1000个请求对应1000个Servlet类。导致类爆炸。
  • 可以这样做:一个请求对应一个方法。一个业务对应一个Servlet类。
  • 处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlet。处理银行卡卡片业务对应一个CardServlet。

更改之后的代码:

package com.zwm.javaweb.action;

import com.zwm.javaweb.utils.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author 猪无名
 * @date 2023/7/22 00 20
 * discription:
 */
//母版类
@WebServlet({"/dept/list","/dept/update","/dept/JDBCupdate","/dept/detail","/dept/delete","/dept/add","/dept/JDBCadd"})
public class DeptServlet extends HttpServlet {
    //模板方法
    //重写service方法
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String servletPath = request.getServletPath();
        if("/dept/list".equals(servletPath)){
            doList(request,response);
        }else if("/dept/detail".equals(servletPath)){
            doDetail(request,response);
        }else if("/dept/delete".equals(servletPath)){
            doDel(request,response);
        }else if("/dept/update".equals(servletPath)){
            doUpdate(request,response);
        }else if("/dept/JDBCupdate".equals(servletPath)){
            doJDBCupdate(request,response);
        }else if("/dept/add".equals(servletPath)){
            doAdd(request,response);
        }else if("/dept/JDBCadd".equals(servletPath)){
            doJDBCadd(request,response);
        }
    }
    //部门列表
    private void doList(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取应用的根路径
        String contextPath = request.getContextPath();

        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        //在html页面中,固定不变的。
        out.print("<!DOCTYPE html>");
        out.print("<html>");
        out.print("   <head>");
        out.print("      <meta charset='utf-8'>");
        out.print("      <title>部门列表页面</title>");

        out.print("   <script type='text/javascript'>");
        out.print("           function del(dno){");
        out.print("       if(window.confirm('亲,删了不可恢复哦!')){");
        out.print("           document.location.href = '"+contextPath+"/dept/delete?deptno=' + dno;");
        out.print("       }");
        out.print("   }");
        out.print("   </script>");


        out.print("   </head>");
        out.print("   <body>");
        out.print("      <h1 align='center'>部门列表</h1>");
        out.print("      <hr>");
        out.print("      <table border='1px' align='center' width='50%'>");
        out.print("         <tr>");
        out.print("            <th>序号</th>");
        out.print("            <th>部门编号</th>");
        out.print("            <th>部门名称</th>");
        out.print("            <th>操作</th>");
        out.print("         </tr>");


        //连接数据库所需变量
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            //获取连接
            conn = DBUtil.getConnection();
            //获取预处理的数据库操作对象
            String sql = "select deptno,dname,loc from dept";
            ps=conn.prepareStatement(sql);
            //执行SQL语句
            rs = ps.executeQuery();


            int number = 0;
            //处理结果集
            while(rs.next()){
                String deptno = rs.getString("deptno");
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");



                out.print("         <tr>");
                out.print("            <td>"+(++number)+"</td>");
                out.print("            <td>"+deptno+"</td>");
                out.print("            <td>"+dname+"</td>");
                out.print("            <td>");
                out.print("               <a href= 'javascript:void(0)' onclick='del("+deptno+")'>删除</a>");
                out.print("               <a href='"+contextPath+"/dept/update?deptno="+deptno+"&dname="+dname+"&loc="+loc+"'>修改</a>");
                out.print("               <a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>详情</a>");
                out.print("            </td>");
                out.print("         </tr>");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }

        //在html页面中,固定不变的。
        out.print("      </table>");
        out.print("      ");
        out.print("      <hr>");
        out.print("      <a href='"+contextPath+"/dept/add'>新增部门</a>");
        //      out.print("      <a href='"+contextPath+"/add. html'>新增部门</a>");
        out.print("   </body>");
        out.print("</html>");
    }
    //查看部门详情
    private void doDetail(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        out.print("<!DOCTYPE html>");
        out.print("<html>");
        out.print("	<head>");
        out.print("		<meta charset='utf-8'>");
        out.print("		<title>部门详情</title>");
        out.print("	</head>");
        out.print("	<body>");
        out.print("		<h1>部门详情</h1>");
        out.print("		<hr>");

        //中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
        // 第一步:获取部门编号
        String deptno = request.getParameter("deptno");
        // 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
        //连接数据库所需变量
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            //获取预处理的数据库操作对象
            String sql = "select deptno,dname,loc from dept where deptno = ?";
            ps=conn.prepareStatement(sql);
            ps.setString(1,deptno);

            //执行SQL语句
            rs = ps.executeQuery();


            if(rs.next()){
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");

                out.print("                部门编号:"+deptno+"			<br>");
                out.print("                部门名称:"+dname+"			<br>");
                out.print("                部门位置:"+loc+"		<br>");

            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }


        out.print("		<input type='button' value='后退' onclick='window.history.back()'/>");
        out.print("		");
        out.print("	</body>");
        out.print("</html>");
    }
    //删除部门
    private void doDel(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String deptno = request.getParameter("deptno");


        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            String sql = "DELETE FROM dept WHERE deptno = ?";
            ps=conn.prepareStatement(sql);
            ps.setString(1,deptno);
            //执行SQL语句
            //返回值代表影响了数据库当中多少条数据。
            int rowsAffected = ps.executeUpdate();
            if (rowsAffected > 0) {
                System.out.println("删除成功");
                // 删除完成后重定向回原来的页面

                response.sendRedirect(request.getContextPath()+"/dept/list");

            } else {
                System.out.println("删除失败");
                response.sendRedirect(request.getContextPath()+"/dept/list");
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
    //增加部门
    private void doAdd(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //获取应用的根路径
        String contextPath = request.getContextPath();
        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        out.print("   <!DOCTYPE html>");
        out.print("   <html>");
        out.print("   	<head>");
        out.print("   		<meta charset='utf-8'>");
        out.print("   		<title>新增部门</title>");
        out.print("   	</head>");
        out.print("   	<body>");
        out.print("   		<h1>新增部门</h1>");
        out.print("   		<hr>");
        out.print("   		<form action='"+contextPath+"/dept/JDBCadd' method='get'>");
        out.print("                  部门编号:<input type='text' name='deptno'/><br>");
        out.print("                  部门名称:<input type='text' name='deptname'/><br>");
        out.print("                  部门位置:<input type='text' name='loc'/><br>");
        out.print("   			<input type='submit' value='提交'/><br>");
        out.print("   		</form>");
        out.print("   	</body>");
        out.print("   </html>");
    }
    private void doJDBCadd(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String deptno = request.getParameter("deptno");
        String deptname = request.getParameter("deptname");
        String loc = request.getParameter("loc");

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();

            String sql = "INSERT INTO dept (deptno, dname, loc) VALUES (?, ?, ?)";
            ps=conn.prepareStatement(sql);

            ps.setString(1,deptno);
            ps.setString(2,deptname);
            ps.setString(3,loc);
            //执行SQL语句
            //返回值代表影响了数据库当中多少条数据。
            int rowsAffected = ps.executeUpdate();

            if (rowsAffected > 0) {
                System.out.println(rowsAffected + " 行已插入数据");

                //request.getRequestDispatcher("/dept/list").forward(request, response);
                response.sendRedirect(request.getContextPath()+"/dept/list");

            } else {
                System.out.println("未插入任何数据");
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
    //修改部门
    private void doUpdate(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //获取应用的根路径
        String contextPath = request.getContextPath();

        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");


        out.print("    <!DOCTYPE html>");
        out.print("    <html>");
        out.print("       <head>");
        out.print("          <meta charset='utf-8'>");
        out.print("          <title>修改部门</title>");
        out.print("       </head>");
        out.print("       <body>");
        out.print("          <h1>修改部门</h1>");
        out.print("          <hr>");
        out.print("          <form action='"+contextPath+"/dept/JDBCupdate' method='get'>");
        out.print("                    部门编号:<input type='text' name='deptno' value='"+deptno+"' readonly/><br>");
        out.print("                    部门名称:<input type='text' name='deptname' value='"+dname+"'/><br>");
        out.print("                    部门位置:<input type='text' name='loc' value='"+loc+"'/><br>");
        out.print("             <input type='submit' value='修改'/><br>");
        out.print("          </form>");
        out.print("       </body>");
        out.print("    </html>");
    }
    private void doJDBCupdate(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //设置相应内容类型和字符集,防止中文乱码。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String deptno = request.getParameter("deptno");
        String deptname = request.getParameter("deptname");
        String loc = request.getParameter("loc");

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();

            String sql = "UPDATE dept SET dname = ?, loc = ? WHERE deptno = ?";

            ps=conn.prepareStatement(sql);

            ps.setString(1,deptname);
            ps.setString(2,loc);
            ps.setString(3,deptno);
            //执行SQL语句
            //返回值代表影响了数据库当中多少条数据。
            int rowsAffected = ps.executeUpdate();

            if (rowsAffected > 0) {
                System.out.println("更改成功");

                response.sendRedirect(request.getContextPath()+"/dept/list");

            } else {
                System.out.println("无匹配数据");
                response.sendRedirect(request.getContextPath()+"/dept/list");
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
}

分析使用纯粹Servlet开发web应用的缺陷

  • 在Servlet当中编写HTML/CSS/JavaScript等前端代码。存在什么问题?

    • java程序中编写前端代码,编写难度大,无法直观地看到前端代码的错误,麻烦。

    • java程序中编写前端代码,显然程序的耦合度非常高。

    • java程序中编写前端代码,代码非常不美观。

    • java程序中编写前端代码,维护成本太高。(非常难于维护)

      修改小小的一个前端代码,只要有改动,就需要重新编译java代码,生成新的class文件,打一个新的war包,重新发布。

  • 思考一下,如何解决这个问题?

    • 上面的那个Servlet(Java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的“前端的那段代码”,然后让机器将我们写的“前端代码”自动翻译生成“Servlet这种java程序”。然后机器再自动将“java”程序编译生成"class"文件。然后再使用JVM调用这个class中的方法。
    • 对应技术JSP






posted @ 2023-07-22 02:08  猪无名  阅读(19)  评论(0编辑  收藏  举报