JavaWeb项目详解系列1-2(Servlet + Jsp + JavaBean)

1.整体项目架构图

在这里插入图片描述
在不考虑分页功能的情况下,本项目是非常简单的,核心功能也只是增删改查而已。

  1. 本项目是严格按照MVC架构+三层结构。
  2. 采用BaseServlet方法将View中的jsp与Servlet中的方法绑定(比如jsp中有add,相应在Servlet中也有add,而BaseServlet就是将两者绑定,具体怎么绑定之后再说。)
  3. 通过BeanUtils工具类将jsp中(也就是网页里)填入的数据转化成一个Customer对象。(这里就是JavaBean规范的好处),尤为方便的是将Map<String,String[]>转换成Customer对象。
  4. 通过QueryRunner实现数据库管理,即SQL语句在这里写,实现DAO层。
  5. 查询结果保存至PageBean<Customer>中,其实本质就是List<Customer>,是记录的集合。

2.JSP中的知识点

2.1 index,top,welcome,frame.jsp

这些很简单,就是html而已,这边不会的,直接搜标签。

  1. frameset rows表示按行划分,20%,* 表示第一部分占20%,后续占剩下的
  2. frame 放置在frameset中,指定框架的某一部分所展示的页面

2.2 add,edit,query.jsp与BaseServlet的关系

<form action='<c:url value='\CustomerServlet'/>' method=post>

以post方式,将表单提交至\CustomerServlet中。
小疑问:既然这里说以post方式提交,为什么CustomerServlet里没有doPost()呢?我们原来学的里面都要重写doPost()和doGet()方法啊。
在这里插入图片描述
为解决这个问题,需要了解BaseServlet这个类。这个类重写了service方法,导致doGet和doPost都没用了。但是我觉得不应该重写service方法,而是应该重写doGet和doPost方法。
具体解释见帖:
https://blog.csdn.net/u010452388/article/details/80723550
再仔细看BaseServlet类,发现

String methodName = request.getParameter(‘"method");

再看add.jsp中有一句话,

<input type="hidden" name="method" value="add">

发现了么,通过隐藏控件将add.jsp绑定了一个提交事件(method=add) ,然后BaseServlet类接收到,之后通过反射调用方法名为methodName的方法(本例中为add)

method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);

总结:add/edit/delete/query.jsp都是这样,发送一个(method = xxx),然后BaseServlet接收到,并在service方法中通过反射调用xxx()。在本例中,add.jsp发送method=add,然后BaseServlet接收到methodName = add,然后在CustomerServlet(BaseServlet的子类)中调用实现了的add()

疑问:为什么要采用这种方式?我们可以在doGet中写若干个if—else语句,从而判断method的值,然后来调用相应的函数。你看,HTTPServlet类的service就是你这么做的,根据不同的HTTP协议头(GET/POST/…)来派发给不同的函数做,用反射不是很麻烦吗?
回答:因为设计模式中,要对修改封闭,对添加开放——意思是原有的代码就不要改了,鼓励增加代码而不是修改代码。你想,如果现在你是CURD四个功能,如果之后功能需求越来越多,那就要添加更多的if……else代码,并且还要修改之前的,维护起来很困难采用这种方法,只要jsp中的method增加一个xxx,然后在servlet中添加一个对应的xxx方法即可实现view和controller的绑定,岂不妙哉?
至于为什么HttpServlett的service采用了if—else,那是因为,HTTP协议头的内容比较固定啊,就post/get/…但是你的项目不一样,甲方很可能给你临时添加不少需求的。
题外话:
我不知道你有没有学过MFC或者QT之类的桌面软件的开发工作,如果你拖动一个按钮(相当于jsp中的input),然后你需要对这个按钮绑定一个事件(相当于servlet中的方法),你可以右键绑定一个事件,即将控件和方法绑定起来。其实本质上,baseservlet就是做了这么一件简单的事儿。

2.3 msg.jsp与BaseServlet的关系

相当于QT或者MFC里面的MessageBox,用来弹出一个页面作为提示。
在CustomerServlet中,将字符串msg的信息写入request域中,然后通过forward转发到msg.jsp中,在通过EL表达式展示出来。
但是你在CustomerServlet中只能看到add()方法返回了一个字符串/msg.jsp,但是跳转这一步在哪里进行呢?
还是BaseServlet。

 try {
            String result = (String)method.invoke(this, request, response);
            if (result != null && !result.trim().isEmpty()) {
                int index = result.indexOf(":");
                if (index == -1) {
                    ((HttpServletRequest)request).getRequestDispatcher(result).forward((ServletRequest)request, response);
                } else {
                    String start = result.substring(0, index);
                    String path = result.substring(index + 1);
                    if (start.equals("f")) {
                        ((HttpServletRequest)request).getRequestDispatcher(path).forward((ServletRequest)request, response);
                    } else if (start.equals("r")) {
                        response.sendRedirect(((HttpServletRequest)request).getContextPath() + path);
                    }
                }
            }

methodName方法(比如add方法)的返回值,通常采用返回字符串的方式,通过前缀来决定功能。比如说返回值为f:msg.jsp表示forward过去,r:msg.jsp表示redirect重定向过去,d:a.img表示下载。也可以设置无前缀时默认为forward转发。
所以,虽然add方法只返回一个/msg.jsp的字符串,但是在service方法中,其实已经利用了这个字符串forward到了相应的地址上了。
这里,baseservlet已经讲解完毕了。

2.4 list.jsp

除了分页的逻辑代码之外,有一处可以讲一下。

        <c:forEach items="${pb.beanList}" var="cstm">
        <tr>
            <td>${cstm.name}</td>
            <td>${cstm.gender}</td>
            <td>${cstm.phone}</td>
            <td>${cstm.email}</td>
            <td>${cstm.description}</td>
            <td>
                <a href="<c:url value='/CustomerServlet?method=preEdit&id=${cstm.id}'/> ">编辑</a>
                <a href="<c:url value='/CustomerServlet?method=delete&id=${cstm.id}'/> ">删除</a>
            </td>
        </tr>

主要是熟悉一下核心标签库中foreach的用法, 还有为什么&id=${cstm.id}呢?其实就是把id发过去,之后好在数据库中根据id删除记录。


2.5 本节小结

从jsp,到baseservlet再到customerservlet,讲到这里,基本上除了数据库操作,基本的流程都结束了。
总结一下:

  1. jsp负责显示以及接受用户输入,并且将method=xxx发出去,以告知controller也就是servlet用什么方法处理。
  2. baseservlet作为基类,通过发射,将method=xxx这样的字符串与xxx()方法进行了关系映射,即你在jsp中只要有method=xxx,同时在baseservlet的子类(具体类)中实现了xxx()方法,就可以实现xxx字符串到xxx()方法的映射。
  3. CustomerServlet是BaseServlet的子类,也是具体类,这个类中定义了具体的add,edit,predit,find,delete等方法。
  4. 但是3中定义的这些方法,并不是直接和数据库对接,那是用什么方式对接呢?
  5. 但是3从jsp中获得的数据,也不是直接存入数据库,那又是以什么形式放入数据库呢?

3.jsp获得的数据如何存入数据库。

BeanUtils

将原始数据打包成对象,以add为例。

 Customer customer = CommonUtils.toBean(request.getParameterMap(), Customer.class);
        //通过反射将map中国的元素赋值一个一个的幅值到customer,然后组成一个完整的对象
        customer.setId(CommonUtils.uuid());
        customerService.add(customer);

再看toBean这个函数:

    public static <T> T toBean(Map map, Class<T> clazz) {
        try {
            T bean = clazz.newInstance();
            ConvertUtils.register(new DateConverter(), Date.class);
            BeanUtils.populate(bean, map);
            return bean;
        } catch (Exception var3) {
            throw new RuntimeException(var3);
        }
    }

本质上是把map中的键值对一个一个的填充到类里,其实这很好理解。
类A 属性a,b,c,d
Map (a,1) (b,2) (c,3) (d,4)
我问你,你能根据Map得到一个A的对象么?当然可以了,相当于A的一个对象叫aa,然后属性a=1,b=2,c=3,d=4。那么BeanUtils.populate(bean, map)其实就是这个意思。
UUID:
类似于MAC网卡,IP地址或者二维码,也可是说是身份证,简单而言就是一组在三千年内不会重复的一串数字字母的组合。原来主键大家用自增的数字,12345等等,但是这样有问题,比如两个库要合并,就会导致主键重复,而采用UUID就不存在这样的问题,这是绝对唯一的,3000年内。

4.以什么形式放入数据库呢

3中已经成功创建好了Customer对象,比如对象c好了,可以通过getXxx()方法获得c的全部属性值,然后放入数据库,就那么简单。

QueryRunner

    public void add(Customer c)
    {
        try {
            String sql = "insert into t_customer values(?,?,?,?,?,?)";
            Object[] params = {c.getId(), c.getName(), c.getGender(),
                    c.getPhone(), c.getEmail(), c.getDescription()};
            //将params的参数带入并取代?
            qr.update(sql, params);
        }catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

这个类就是用起来舒服一点的数据库操作类,其实本质都一样,就是sql语句挖个空(占位符/通配符),然后用string类型字符串去拼接他,实现动态sql语句的功能。下面给出相关的一些小知识点,想知道特别具体的,可以百度或谷歌。

  • 知识点:
  • 1.TxQueryRunner
  • 1.1 add:添加
  • sql语句?占位符,params中注入,取代?,调用update(sql,params)注入并运行
  • 1.2 edit:修改
  • 同上,类似,多一个where判断,根据ID判断应该放在哪里
  • 1.3 delete:删除
  • 问题不大,sql语句中id留空,然后update语句注入即可。
  • 1.4 query:查询
  • 1.4.1 find
  • Customer customer = qr.query(sql, new BeanHandler(Customer.class), id);
  • 查询单个结果,并通过反射以对象的形式返回。注意,Customer必须要遵循javabean规范。
  • 1.4.2 findAll
  • List beanList=qr.query(sql,new BeanListHandler<>(Customer.class),params);
  • 查询一个集合,可见和上面是类似。一行结果集对应一个JavaBean对象。
  • 1.4.3 query
  • 多条件查询而已。
  • 2 分页
  • 查询通过limit语句来选择:
  • (当前页码-1)*每页记录 + [0,每页记录数)
  • 比如说当前页码第1页,每页十条,那这一页就是0到9

结语

我在考虑写不写这个项目里关于分页的内容,感觉逻辑比较多,但是并不难。。。

猜你喜欢

转载自blog.csdn.net/w8253497062015/article/details/85004072
今日推荐