0%

JavaWeb书城项目(十)———Filter过滤器和Aajx请求

整个项目源代码可以到我的github上下载。前九个部分我们大致完成了书城项目的所有功能,第十部分我们做一个结尾,主要包括使用Filter过滤器进行权限检查,使用Filter和ThreadLocal组合管理事务,将异常同一交给Tomcat并展示友好的错误页面,使用Aajx请求改进功能。

Filter过滤器实现权限检查

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

  1. Filter的工作流程如下

    Filter工作流程图

  2. Filter 过滤器的使用步骤:

1
2
3
4
5
1、 编写一个类去实现 Filter 接口

2、 实现过滤方法 doFilter()

3、 到 web.xml 中去配置 Filter
  1. Filter 的生命周期
1
2
3
4
5
6
7
8
Filter 的生命周期包含几个方法
1、构造器方法
2、init 初始化方法
12 步,在 web 工程启动的时候执行(Filter 已经创建)
3、doFilter 过滤方法
3 步,每次拦截到请求,就会执行
4、destroy 销毁
4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)
  1. Filter 的拦截路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--精确匹配
<url-pattern>/target.jsp</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

--目录匹配
<url-pattern>/admin/*</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*

--后缀名匹配
<url-pattern>*.html</url-pattern>
以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
<url-pattern>*.do</url-pattern>
以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
<url-pattern>*.action</url-pattern>
以上配置的路径,表示请求地址必须以.action 结尾才会拦截
  1. 创建ManagerFilter实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ManagerFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
Object user = httpServletRequest.getSession().getAttribute("user");

if (user == null) {
httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}
}

  1. 配置 web.xml 文件
1
2
3
4
5
6
7
8
9
<filter>
<filter-name>ManagerFilter</filter-name>
<filter-class>com.atguigu.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>

Filter和ThreadLocal组合管理事务

  1. ThreadLocal的作用
1
2
3
4
5
6
7
8
9
10
ThreadLocal 的作用,它可以解决多线程的数据安全问题。

ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

ThreadLocal 的特点:
1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)
2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用 多个
ThreadLocal 对象实例。
3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放
  1. 使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成。
    原理分析图
    JdbcUtils工具类的修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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 (Exception e) {
e.printStackTrace();
}
}
return conn;
}

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

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

修改BaseDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public abstract class BaseDao {

// 使用DbUtils操作数据库
private QueryRunner queryRunner = new QueryRunner();

/**
* update() 方法用来执行:Insert\Update\Delete语句
* @return 如果返回-1说明执行失败<br/>返回其它表示影响的行数
*/
public int update(String sql, Object ... args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.update(connection, sql, args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

/**
* 查询返回一个javaBean的sql语句
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T> T queryForOne(Class<T> type, String sql, Object ... args){
Connection con = JdbcUtils.getConnection();
try {
return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

/**
* 查询返回多个javaBean的sql语句
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T> List<T> queryForList(Class<T> type, String sql, Object ... args){
Connection con = JdbcUtils.getConnection();
try {
return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

/**
* 执行返回一行一列的sql语句
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @return
*/
public Object queryForSingleValue(String sql, Object ... args){

Connection conn = JdbcUtils.getConnection();

try {
return queryRunner.query(conn, sql, new ScalarHandler(), args);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
  1. 使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch。来进行实现的管理。
    原理分析图
    Filter类代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TransactionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try{
filterChain.doFilter(servletRequest, servletResponse);
JdbcUtils.commitAndClose(); // 提交事务
} catch (Exception e) {
JdbcUtils.rollbackAndClose(); // 回滚事务
e.printStackTrace();
}
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}
}

配置web.xml

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.atguigu.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /* 表示当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>

一定要记得把BaseServlet中的异常往外抛给Filter过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class BaseServlet extends HttpServlet {

@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 {
// 解决post请求中文乱码问题
// 一定要在获取请求参数之前调用才有效
req.setCharacterEncoding("UTF-8");

String action = req.getParameter("action");

try {
// 获取action业务鉴别字符串,获得相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e); // 把异常抛给Filter过滤器
}

}

}

Tomcat管理异常

我们将所有异常都统一交给 Tomcat,让Tomcat展示友好的错误信息页面,这样用户就不用面对一大堆问题代码了。

  1. web.xml中我们可以通过错误页面配置来进行管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>500</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error500.jsp</location>
</error-page>
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>404</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>
  1. 编写错误页面,我们就简单做一下
    error500.jsp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>服务器出错啦</title>
    <%-- 静态包含 base标签,css样式,jQuery文件 --%>
    <%@ include file="/pages/common/head.jsp"%>
    </head>
    <body>
    服务器出错啦,程序员小哥正在加紧抢修。<br/>
    <a href="index.jsp">返回首页</a>
    </body>
    </html>
    error404.jsp
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>访问资源不存在</title>
<%-- 静态包含 base标签,css样式,jQuery文件 --%>
<%@ include file="/pages/common/head.jsp"%>
</head>
<body>
您访问的资源不存在,或已被删除<br/>
<a href="index.jsp">返回首页</a>
</body>
</html>
  1. TransactionFilter要把异常抛给Tomcat
    修改TransactionFilter

使用AJAX验证用户名是否可用

  1. 图解验证用户名是否可用流程
    验证用户名是否可用流程
  2. 导入相关jar
1
gson-2.2.4.jar
  1. UserServlet程序添加ajaxExistsUsername方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* ajax请求判断用户名是否存在
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void ajaxExistUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数username
String username = req.getParameter("username");
// 调用userService.existUsername()
boolean existsUsername = userService.existsUsername(username);
// 把返回的结果封装为map对象
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("existsUsername", existsUsername);

Gson gson= new Gson();
String json = gson.toJson(resultMap);

resp.getWriter().write(json);
}
  1. 修改regist.jsp中的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	// 给用户名绑定失去焦点事件
$("#username").blur(function () {
// 1. 获取用户名
var username = this.value;
//2 创建正则表达式对象
var usernamePatt = /^\w{5,12}$/;
//3 使用test方法验证
if(!usernamePatt.test(username)) { // 用户名不合法
//4 提示用户结果
$("span.errorMsg").text("用户名不合法!");
} else{
// 用户名合法判断用户名是否存在
// alert("用户名合法");
$.getJSON("${pageScope.basePath}userServlet","action=ajaxExistsUsername&username=" + username,
function (data) {
if(data.existsUsername) { // 用户名存在
$("span.errorMsg").text("用户名已存在!");
} else { // 用户名可用
$("span.errorMsg").text("用户名可用!");
}
});
}
});

AJAX添加商品到购物车

我们之前把商品添加到购物车,是刷新了整个页面,这样用户体验不是很好,我们可用使用Ajax请求来完成局部的更新。

  1. 图解商品加入购物车
    加入购物车流程

  2. CartServlet程序添加ajaxAddItem方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 调用 bookService.queryBookById(id):Book 得到图书的信息
Book book = bookService.queryBookById(id);
// 把图书信息,转换为CartItem商品项
CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());
// 调用Cart.addItem(CartItem);添加商品项
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart == null) {
cart = new Cart();
req.getSession().setAttribute("cart", cart);
}
cart.addItem(cartItem);
System.out.println(cart);

// 最后一个添加的商品名称
req.getSession().setAttribute("lastName", cartItem.getName());

// 返回购物车总的商品数量和最后一个添加的商品名称
Map<String, Object> resultMap = new HashMap<String, Object>();

resultMap.put("totalCount", cart.getTotalCount());
resultMap.put("lastName", cartItem.getName());

Gson gson = new Gson();
String resultMapJsonString = gson.toJson(resultMap);

resp.getWriter().write(resultMapJsonString);
}
  1. 修改index.jsp页面

html代码

js代码

  1. 修改BaseServlet程序解决中文乱码问题
    解决中文乱码问题
-------------本文结束感谢您的阅读-------------