整个项目源代码可以到我的github上下载。前九个部分我们大致完成了书城项目的所有功能,第十部分我们做一个结尾,主要包括使用Filter过滤器进行权限检查,使用Filter和ThreadLocal组合管理事务,将异常同一交给Tomcat并展示友好的错误页面,使用Aajx请求改进功能。
Filter过滤器实现权限检查
我们要使用 Filter 过滤器拦截/pages/manager/所有内容,实现权限检查。
Filter
的工作流程如下
Filter
过滤器的使用步骤:
1 2 3 4 5
| 1、 编写一个类去实现 Filter 接口
2、 实现过滤方法 doFilter()
3、 到 web.xml 中去配置 Filter
|
Filter
的生命周期
1 2 3 4 5 6 7 8
| Filter 的生命周期包含几个方法 1、构造器方法 2、init 初始化方法 第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建) 3、doFilter 过滤方法 第 3 步,每次拦截到请求,就会执行 4、destroy 销毁 第 4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)
|
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 结尾才会拦截
|
- 创建
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() {
} }
|
- 配置
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组合管理事务
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 虚拟自动释放
|
- 使用
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(); InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); properties.load(inputStream); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } }
public static Connection getConnection(){
Connection conn = conns.get();
if (conn == null) { try { conn = dataSource.getConnection(); conns.set(conn); conn.setAutoCommit(false); } catch (Exception e) { e.printStackTrace(); } } return conn; }
public static void commitAndClose() { Connection connection = conns.get(); if (connection != null) { try { connection.commit(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } conns.remove(); }
public static void rollbackAndClose(){ Connection connection = conns.get(); if (connection != null) { try { connection.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } conns.remove(); }
}
|
修改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 {
private QueryRunner queryRunner = new QueryRunner();
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); } }
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); } }
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); } }
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); } } }
|
- 使用
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 { req.setCharacterEncoding("UTF-8");
String action = req.getParameter("action");
try { Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this, req, resp); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); }
}
}
|
Tomcat管理异常
我们将所有异常都统一交给 Tomcat
,让Tomcat
展示友好的错误信息页面,这样用户就不用面对一大堆问题代码了。
- 在
web.xml
中我们可以通过错误页面配置来进行管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <error-page> <error-code>500</error-code> <location>/pages/error/error500.jsp</location> </error-page>
<error-page> <error-code>404</error-code> <location>/pages/error/error404.jsp</location> </error-page>
|
- 编写错误页面,我们就简单做一下
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>
|
TransactionFilter
要把异常抛给Tomcat
使用AJAX验证用户名是否可用
- 图解验证用户名是否可用流程
- 导入相关
jar
包
UserServlet
程序添加ajaxExistsUsername
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
protected void ajaxExistUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); boolean existsUsername = userService.existsUsername(username); Map<String, Object> resultMap = new HashMap<>(); resultMap.put("existsUsername", existsUsername);
Gson gson= new Gson(); String json = gson.toJson(resultMap);
resp.getWriter().write(json); }
|
- 修改
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 () { var username = this.value; var usernamePatt = /^\w{5,12}$/; if(!usernamePatt.test(username)) { $("span.errorMsg").text("用户名不合法!"); } else{ $.getJSON("${pageScope.basePath}userServlet","action=ajaxExistsUsername&username=" + username, function (data) { if(data.existsUsername) { $("span.errorMsg").text("用户名已存在!"); } else { $("span.errorMsg").text("用户名可用!"); } }); } });
|
AJAX添加商品到购物车
我们之前把商品添加到购物车,是刷新了整个页面,这样用户体验不是很好,我们可用使用Ajax
请求来完成局部的更新。
图解商品加入购物车
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); Book book = bookService.queryBookById(id); CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice()); 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); }
|
- 修改
index.jsp
页面
- 修改
BaseServlet
程序解决中文乱码问题