尚硅谷JavaWeb_2020idea_王振国_学习笔记


项目是书城项目,感觉不太完整。在讲各种web技术的同时,顺带做下项目。该项目没有使用SSM、SSH等框架,Javaweb三层框架都是直接写的代码。 大致熟悉一下流程即可,在真正开发的时候会用框架来做。

B站地址:https://www.bilibili.com/video/BV1Y7411K7zz?p=1

网盘视频和相应资料文件地址:尚硅谷公众号回复 Java 获取。在第一个Java基础文件夹中,找尚硅谷JavaWeb_2020idea新版。

前置知识:Java入门、数据库和JDBC。同样方式获取资料。

Java地址:https://www.bilibili.com/video/BV1Kb411W75N

数据库地址:https://www.bilibili.com/video/BV1xW411u7ax?spm_id_from=333.788.b_636f6d6d656e74.8

JDBC地址:https://www.bilibili.com/video/BV1eJ411c7rf

另一个只教项目的地址(网上在线书城-JavaWeb教程案例-尚硅谷_佟刚):https://www.bilibili.com/video/BV1Vt411T7v5?p=1

首先介绍一下资料的使用:

先去上面网盘下载相关资料。导入的jar包来源:尚硅谷JavaWeb_2020idea新版\资料\05-XML & Tomcat\笔记\JavaWeb需要用到的jar包;如果有些无法识别,需要将tomcat\lib中的所有jar也导入。视频中使用的是 JDK8/tomcat-8.0.50,建议使用相同版本。

尚硅谷JavaWeb_2020idea新版\资料\01-html&CSS\笔记中有一个项目实战:尚硅谷商城.rar,里面有各个阶段的代码和一个项目说明。

另外,我对视频中对于基本知识介绍的pdf(包含一部分项目阶段)进行了整合,可以关注我的公众号 Java与大数据进阶,回复pdf获取。

基本操作

我使用的idea版本是ultimate 2020.3,和视频中版本不同。这里创建Module方式如下。第一步,File-New-Module;第二步,选择 Java EE(Legacy)-Web Application(4.0),下面 create web.xml 打钩,Next。
在这里插入图片描述

视频中的在Project Structure中配置jar这种我不会。配置的时候经常出现关不掉的情况。这里建议如下图,shift选中全部,右键-add As Library,在窗口中Level 选择 Module Library,成功后包左边会出现展开符号,可以查看里面的class,在idea中默认查看的是反编译后的结果。

在这里插入图片描述

阶段一、使用JS正则表达式检查输入

使用JS正则表达式在前端检查注册状态,在pages/user/regist.html进行修改。

注意,在前端HTML/JS中,出错不会提示,如果没有效果可以看看是否是单词写错。

在事件加载完成之后写代码,$(function(){...})

text/val/html 区别:val用于input,text只输出文本,html输出全部内容。具体区别可见下方代码

<script type="text/javascript" src="../../static/script/jquery-1.7.2.js"></script>
<script type="text/javascript">
        $(function () {
            console.log($("div").text());//divText::pText
            console.log($("p").text());//pText
            console.log($("input").text());//

            console.log($("div").html());//divText::<p>pText</p>
            console.log($("p").html());//pText
            console.log($("input").html());//

            console.log($("div").val());//
            console.log($("p").val());//
            console.log($("input").val());//text
        })
</script>


<body>
<div>divText::<p>pText</p></div>
<input type="text" value="text"/>
</body>

return false 阻止默认的事件行为

阶段二、实现登陆和注册功能

下图为Java EE三层架构,视频中从数据库写起,从右到左。

在这里插入图片描述

下图是三层架构对应的包结构,注意到对于service/dao是先有接口,后有实现类。

在这里插入图片描述

创建了工具类utils.JdbcUtils,在static块中初始化相应的Druid数据库连接池,并提供创建连接和关闭连接的方法。在dao.impl.BaseDao中实现了对数据库的各种操作的一般方法,将该类设定为abstract class。

登录和注册只和用户有关。所以接下来,在数据库中创建一个用户表。bean.User是对应的实体bean对象,接口dao.BookDao定义对用户表需要执行哪些操作。

实现了BookDao的类dao.impl.BookDaoImpl需要调用操作数据库的一般方法来完成相应功能,所以该类extends BaseDao implements BookDao

接下来会创建接口service.UserService和实现类,该类的方法就是需要处理的业务逻辑。

最后写web层,web.RegistServlet实现注册逻辑,web.LoginServlet实现登录逻辑。注意:req.getParameter的参数是表单项的name属性值,跳转方式是请求转发,将对应Servlet写入WEB-INF/web.xml。

在html页面使用base标签和相对路径。

另外,在完成一定功能后,比如DAO/service后需要测试,防止错误范围太大难以查找,在接口中生成测试类快捷键 ctrl+shift+T。

阶段三、做一些优化

jsp已经淘汰,大致了解即可。

主要工作:修改html为jsp,抽取页面中重复的内容,错误提示及表单回显(在Servlet中setAttribute,在jsp中getAttribute并判断是否为空),使用BaseServlet,BeanUtils一次性将Map中的值注入JavaBean。

BaseServlet首先获取隐藏域表单项中name="action"的value,并通过反射调用同名方法。具体的Servlet只需要继承BaseServlet,并写出对应的同名方法即可。

还有一种方法是将参数放入href中,具体如下。

//在jsp的表单中添加一个隐藏域,name="action",在Servlet中获取对应的value为regist
//<input type="hidden" name="action" value="regist" />
//获取地址中action的值为list
//<a href="manager/bookServlet?action=list">图书管理</a>

public abstract class BaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      	//获取对应表单项中action的value
        String action = req.getParameter("action");
        try {
            // 获取action业务鉴别字符串,获取相应的业务方法反射对象
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//            System.out.println(method);
            // 调用目标业务 方法
            method.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

阶段四、使用EL表达式修改表单回显

阶段五、图书模块

1.MVC 全称:Model 模型、 View 视图、 Controller 控制器。

MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作—— JSP/HTML。

Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。 转到某个页面。或者是重定向到某个页面。

Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码—— JavaBean/domain/entity/pojo。

在这里插入图片描述

在图书模块,先写DAO,service,然后实现查看所有书籍,添加修改和删除书籍的功能,由于要使用到数据库中的数据,需要Servlet处理,然后请求转发/重定向到相应位置。

由于提交请求后,浏览器记录了最后一次请求的全部信息,按下F5,会再次请求,这是表单重复提交的问题。所以,增删改都需要使用重定向

①对于删除操作,一般需要提示用户是否确认删除,通过JS实现。

②对于添加和修改操作,均使用的是book_edit.jsp,为了将两者区分,有三种方法。

解决方案一:可以请求发起时,附带上要操作的方法名,并注入隐藏域

<%--在book_manager.jsp中添加图书和修改图书的标签中的href添加对应参数--%>
<td><a href="manager/bookServlet?action=getBook&id=${book.id}&method=update">修改</a></td>
...
<td><a href="pages/manager/book_edit.jsp?method=add">添加图书</a></td>
<%--在book_edit.jsp中添加--%>
<input type="hidden" name="action" value="${param.method}">

解决方案二:可以判断当前请求参数中是否包含有id,有说明是修改,否则是添加.${empty param.id?"add":"update"}

解决方案三:可以通过判断,Request域中是否包含有修改的图书信息对象,没有说明是添加,有说明是修改。

<input type="hidden" name="action" value="${ empty param.book ? "add" : "update" }" />

③对于修改操作,修改需要id值,所以在book_edit.jsp中放一个隐藏域保存,代码不用动,会被BeanUtils自动封装到Book中。

<input type="hidden" name="id" value="${ requestScope.book.id }" />


阶段五、下、分页的实现

实现分页功能,需要新建一个Page类,需要传入pageNo和pageSize。每个页面的数据通过limit语句获取。在jsp的相应跳转地址中,会添加pageNo字段。

修改index.jsp,请求转发到一个Servlet,获取分页数据后,再跳转到其他jsp页面输出。

阶段六、登录/注销/验证码

登录使用Session保存,注销是销毁Session然后重定向。为了防止表单重复提交,使用kaptcha图片验证码,在获取验证码后删除,这样重定向时,验证码的地方为空,重复提交失败。

表单重复提交有三种常见的情况:

一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转 .

二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交。

三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。

阶段六、购物车模块

购物车里面有两个类,购物车Cart,购物车项CartItem。

购物车实现版本有三种:

1、Session版本(把购物车信息保存在Session域中) 本次使用的版本

2、数据库版本(把购物车信息,保存到数据库)

3、redis+数据库+Cookie(使用Cookie+Redis缓存,和数据库)

具体来说,每次添加时,会先获取Session中的Cart,如果为空就新建一个并放入Session,然后执行相应的添加操作。

在HTTP协议中有一个请求头,叫Referer,它可以把请求发起时,浏览器地址栏中的地址发送给服务器。

回显最后一个添加的商品,只需要每次添加时在Session中覆盖"lastName"对应的购物车项的名字即可。

阶段七、订单

同样有两个类,订单Order和订单项OrderItem,对应数据库的两个表t_order/t_order_item。t_order和用户表t_user可以通过userId关联,t_order和t_order_item通过orderId关联。

这里只实现了生成订单功能,生成订单会在两个表中分别保存订单和每个订单项。

阶段八、权限检查和事务管理

1、使用Filter过滤器拦截/pages/manager/所有内容,实现权限检查。

2、ThreadLocal的使用,其实ThreadLocal底层是ThreadLocalMap,每个线程有一个ThreadLocalMap,存着所有使用过的ThreadLocal,这样同一个ThreadLocal在不同线程中结果就可以不同。具体可见我的这篇文章…

3、使用 Filter 和 ThreadLocal 组合管理事务

修改JdbcUtils,使用ThreadLocal<Connection>来表示当前线程的连接,在getConnection中,如果存在则获取,否则生成。在提交和回滚事务后,关闭连接并删除当前ThreadLocal。

在BaseDao的相应方法中,去掉关闭连接的部分,并抛出异常。在BaseServlet中同样抛出异常。在Filter的doFilter方法中用try-catch包裹相应代码。

public class JdbcUtils {

    private static DruidDataSource dataSource;
    private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();

    static {
        try {
            Properties properties = new Properties();
            // 读取 jdbc.properties属性配置文件
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            // 从流中加载数据
            properties.load(inputStream);
            // 创建 数据库连接 池
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取数据库连接池中的连接
     * @return 如果返回null,说明获取连接失败<br/>有值就是获取连接成功
     */
    public static Connection getConnection(){
        Connection conn = conns.get();
        if (conn == null) {
            try {
                conn = dataSource.getConnection();//从数据库连接池中获取连接
                conns.set(conn); // 保存到ThreadLocal对象中,供后面的jdbc操作使用
                conn.setAutoCommit(false); // 设置为手动管理事务
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }

    /**
     * 提交事务,并关闭释放连接
     */
    public static void commitAndClose(){
        Connection connection = conns.get();
        if (connection != null) { // 如果不等于null,说明 之前使用过连接,操作过数据库
            try {
                connection.commit(); // 提交 事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close(); // 关闭连接,资源资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        // 一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }

    /**
     * 回滚事务,并关闭释放连接
     */
    public static void rollbackAndClose(){
        Connection connection = conns.get();
        if (connection != null) { // 如果不等于null,说明 之前使用过连接,操作过数据库
            try {
                connection.rollback();//回滚事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close(); // 关闭连接,资源资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        // 一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }
}

阶段九、Ajax实现相应功能

Ajax 是一种浏览器通过 js 异步发起请求,局部更新页面的技术。

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值