第一阶段 表单验证
新建一个模块
把书城的静态资源拷贝到工程下
验证用户名里输入的内容
获取输入框内容
创建正则表达式对象
使用test验证
提示用户结果
第二阶段——用户注册和登录
JavaEE项目的三层架构

web层 com.tjq(我的名字缩写qwq).web/servlet/controller
service层 com.tjq.serivce service接口包类
com.tjq.service.impl service接口实现类
dao持久层 dao Dao接口包
dao.impl Dao接口实现类
实体Bean对象 com.tjq.pojo/entity/domain/bean JavaBean类
测试包 com.tjq.test.junit
工具类 com.tjq.utils
创建数据库和t_user表
CREATE TABLE t_user(
`id` int PRIMARY KEY auto_increment,
`username` VARCHAR(10) NOT NULL UNIQUE,
`password` VARCHAR(32) NOT NULL,
`email` VARCHAR(200)
);
INSERT INTO t_user (`username`,`password`,`email`) VALUES ('admin','admin','admin@tjq.com');
SELECT * FROM t_user
创建数据库对应的user类
编写Dao持久层
编写BaseDao
编写UserDao
编写UserService和测试
需求1:用户注册
需求如下:
访问注册页面
填写注册信息,提交给服务器
服务器应该保存用户
当用户已经存在-提示用户注册失败,用户已存在
当用户不存在时,注册成功
web阶段使用:base+相对
框架之后:绝对路径
需求2:用户登录
第三阶段
a)页面JSP动态化
b)抽取页面中相同内容
动态base值
在Servlet中setAttribute,然后在jsp中<%=%>
BaseServlet的抽取
在实际的项目开发中,一个模块,一般只使用一个Servlet程序
代码优化:
优化一:合并LoginServlet和RegistServlet
优化二:使用反射优化大量else if
String action = request.getParameter("action");
System.out.println(action);
try {
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, request, response);
} catch (Exception e) {
e.printStackTrace();
}
优化三:抽取BaseServlet程序
数据的抽取和BeanUtils的使用
BeanUtils工具类,可以一次性的把所有请求的参数注入到JavaBean中
BeanUtils不是官方类,第三方,需要导包
1、导入需要的包
commons-beanutils-1.8.0.jar
commons-logging-1.1.1.jar
2、使用BeanUtils类方法注入
底层:注入的核心是User类的写方法set,通过反射赋值,所以名称必须相同
把Map中的值注入到对应的JavaBean属性中,适用性更强使用更加灵活
第四阶段
EL表达式错误回显
第五阶段
MVC概念
MVC全称:Model 模型、View 视图、Controller 控制器
MVC最早出现在JavaEE三层中的Web层,他可以有效知道Web层的代码如何进行有效分离,单独工作
View视图:只负责数据和界面的显示
Controller控制器:只负责接受请求
Model模型
MVC是一种思想
1.图书模块
编写图书模块的数据库表
编写图书模块的JavaBean
编写图书模块的Dao和测试Dao
编写图书模块的Service和测试Service
编写图书模块的Web层和页面联调测试
图书列表功能实现
点击——访问BookServlet(地址/manager/bookServlet?action=list)
设置Attribute把queryBooks传过去,重定向页面
protected void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Book> books = bookDao.queryBooks();
request.setAttribute("books", books);
request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request, response);
}
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${book.name}</td>
<td>${book.price}</td>
<td>${book.author}</td>
<td>${book.sales}</td>
<td>${book.stock}</td>
<td><a href="pages/manager/book_edit.jsp">修改</a></td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
前后台的简单介绍

添加图书功能实现
重点:request.getParameter()名字要和html里面的name一样,因为原理
bug:表单重复提交
所以不能用请求转发:getRequestDispatcher
用重定向:response.sendRedirect
删除图书功能实现

修改图书

图书分页
Page类:
pageNo 当前页码
pageTotal 总页码
pageTotalCount 总记录数
pageSize 每页显示数量
items 当前页数据
pageNo 当前页码是由客户端进行传递
pageSize 每页显示数量由两种因素决定
一、客户端进行传递
二、由页面布局决定
pageTotalCount 总记录数可以由sql语句求得
pageTotal 总页码可以由总记录数/每页数量得到
注:总记录数%每页数量>0,则 总页码+1
items 是当前页数据,也是可以由sql语句求得
sql语句是:select * form 表名 limit begin, pageSize
begin 可以由公式求得:(pageNo-1)*pageSize
结构图

显示五个连续的页码,当前页码在中间
情况一:总页码小于等于5
情况二:总页码大于5
小情况1:当前页码为前三个:1,2,3的情况,页码范围是 :1-5
小情况2:当前页码为最后三个,页码范围:总页码减4-总页码
小情况3:4,5,6,7,页码范围减2-当前页码加2
注意事项
数据有效边境检查
修改,添加图书后能直接跳到所操作的图书的当前页,这里直接用的是pageNo,并在add和update里面改跳转地址
前台分页的初步实现
准备一个pages下的client目录,复制index.jsp到client目录下
web下的index只干请求转发
分页条的抽取(优化重点)
跟common里的一样
价格区间搜索并分页

查询价格回显
判断req.getParameter min是不是null,StringBuffer添加参数,避免回显默认值
第六阶段
1.显示登录的用户信息
<c:if test="${empty sessionScope.user}">
<a href="pages/user/login.jsp">登录</a> |
<a href="pages/user/regist.jsp">注册</a>
</c:if>
<c:if test="${not empty sessionScope.user}">
<span>欢迎<span class="um_span">${sessionScope.user}</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="index.jsp">注销</a>
</c:if>
2.登出——注销用户
销毁Session中用户登录的信息(或者销毁Session)
重定向到首页
3.表单重复提交之——验证码
表单重复提交有三种常见的情况:
一、提交完表单,服务器使用请求转发来进行页面跳转。这个时候,用户按下f5,就会发起最后一次请求,造成表单重复提交问题。解决方法:使用重定向来进行跳转
二、用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交
三、用户正常提交服务器,服务器也没有延迟,但是提交完成后,用户回退浏览器,重新提交。也会造成表单重复提交。
验证码底层原理

4.谷歌kaptcha图片验证码的使用
导入谷歌验证码的jar包
在web.xml中去配置用于验证码的Servlet程序
在表单中使用img标签显示验证码并使用它
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<img src="http://localhost:8080/temp/kaptcha.jpg" alt="" style="width: 100px;height: 28px"><br>
<input type="submit" value="登录">
在服务器获取谷歌生成的验证码和客户端发送来的验证码比较
5.验证码的切换
给img标签绑定一个单机事件,this.src后面加上一个时间戳来保证每次点击发送的请求不一样。(原理,浏览器缓存)
6.购物车
6.1 购物车模块的分析


6.2 添加商品到购物车
如何解决在第二页添加购物车返回的却是第一页的问题:
使用请求头中的Referer,可以把地址栏中的地址发送到服务器,进而实现跳转到原来的页面
6.3 购物车的展示
使用的jsp中的循环语句
<c:forEach items="${sessionScope.cart.items}" var="entry">
<tr>
<td>${entry.value.name}</td>
<td>${entry.value.count}</td>
<td>${entry.value.price}</td>
<td>${entry.value.totalPrice}</td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
6.4修改购物车商品数量
使用js
$(".updateCount").change(function () {
//给输入框绑定失去焦点事件
var name = $(this).parent().parent().find("td:first").text();
var count = this.value;
var id = $(this).attr("bookId");
if (confirm("你确定要将【" + name + "】的数量修改为:" + count + "吗?")) {
location.href = "http://localhost:8080/book/cartServlet?action=updateCount&count=" + count + "&id=" + id;
} else {
this.value = this.defaultValue;
}
});
7.订单
7.1 订单模块的分析

订单功能:
生成订单
查询所有订单(管理员)
发货(管理员)
查看订单详情(管理员/用户)
查看我的订单(用户)
签收订单(用户)

7.2 订单模块
第八阶段
1.使用Filter过滤器拦截/pages/manager所有内容,实现权限检查
<filter>
<filter-name>ManagerFilter</filter-name>
<filter-class>com.tjq.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManagerFilter</filter-name>
<url-pattern>/pages/manager/*</url-pattern>
<url-pattern>/manager/bookServlet</url-pattern>
</filter-mapping>
2.ThreadLocald的使用
ThreadLocal的作用,可以解决多线程的数据安全问题
ThreadLocal它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
ThreadLocal的特点:
ThreadLocal可以为当前线程关联一个数据(它可以像Map一样存取数据,key为当前线程)
每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例
每个ThreadLoca对象实例定义的时候,一般都是static类型
ThreadLocal中保存数据,在线程销毁后。会由JVM虚拟机自动释放。
2.使用Filter和ThreadLocal组合管理事务

3.使用Filter过滤器统一给所有的Service方法都加上try-catch。来实现统一的事务管理

第九模块
通过ajax验证用户名可用不可用

http://localhost:8080/book/userServlet?action=ajaxExistsUsername&username=tjq
http://localhost:8080/book/userSerlvet?action=ajaxExistsUsername&username=tjq