D18 Sping Boot 入门 Sping框架--Java Web之书城项目(八) 过滤器
一、使用Filter过滤器拦截/pages/manager/所有内容,实现权限检查
新建ManagerFilter
1 package com.gychen.filter; 2 3 import javax.servlet.*; 4 import javax.servlet.http.HttpServletRequest; 5 import java.io.IOException; 6 7 public class ManagerFilter implements Filter { 8 9 @Override 10 public void init(FilterConfig filterConfig) throws ServletException { 11 12 } 13 14 @Override 15 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 16 17 HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 18 19 Object user =httpServletRequest.getSession().getAttribute("user"); 20 21 if (user ==null) { 22 httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse); 23 } else { 24 filterChain.doFilter(servletRequest,servletResponse); 25 } 26 } 27 28 @Override 29 public void destroy() { 30 31 } 32 }
二、ThreadLocal的使用
ThreadLocal 的作用,它可以解决多线程的数据安全问题。ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)ThreadLocal 的特点:1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal 对象实例。3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。
三、实现Filter和ThreadLocal组合管理事务
3.1 在JdbcUtils里用ThreadLocal重构
1 package com.gychen.utils; 2 3 import com.alibaba.druid.pool.DruidDataSource; 4 import com.alibaba.druid.pool.DruidDataSourceFactory; 5 6 import java.io.InputStream; 7 import java.sql.Connection; 8 import java.sql.SQLException; 9 import java.util.Properties; 10 11 public class JdbcUtils { 12 13 private static DruidDataSource dataSource; 14 // 用作单线程事务管理 15 private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>(); 16 17 static { 18 19 try { 20 Properties properties = new Properties(); 21 //读取jdbc.properties配置文件属性 22 InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); 23 //从流中加载数据 24 properties.load(inputStream); 25 //创建 数据库 连接池 26 dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); 27 // System.out.println(dataSource.getConnection()); 28 } catch (Exception e) { 29 e.printStackTrace(); 30 } 31 32 } 33 34 35 /** 36 * 获取数据库连接池中的连接 37 * @return 如果返回null 说明获取连接失败<br/> 有值就是获取连接成功 38 */ 39 // public static Connection getConnection(){ 40 // Connection conn = null; 41 // try { 42 // conn = dataSource.getConnection(); 43 // } catch (SQLException e) { 44 // e.printStackTrace(); 45 // } 46 // return conn; 47 // } 48 49 public static Connection getConnection(){ 50 Connection conn = conns.get(); 51 if (conn == null) { 52 try { 53 conn = dataSource.getConnection(); // 从数据库连接池中获取连接 54 55 conns.set(conn); // 保存到ThreadLocal对象中,供后面的JBDC操作使用 56 57 conn.setAutoCommit(false); // 设置为手动管理事务 58 } catch (SQLException e) { 59 e.printStackTrace(); 60 } 61 } 62 return conn; 63 } 64 65 // /** 66 // * 关闭连接,放回数据库连接池 67 // * @param conn 68 // */ 69 // public static void close(Connection conn){ 70 // if(conn != null){ 71 // try { 72 // conn.close(); 73 // } catch (SQLException e) { 74 // e.printStackTrace(); 75 // } 76 // } 77 // } 78 79 /** 80 * 提交事务并关闭释放连接 81 */ 82 public static void commitAndClose(){ 83 Connection connection = conns.get(); 84 if (connection != null) { // 如果不等于null,说明之前使用过连接,操作过数据库 85 try { 86 connection.commit(); // 提交事务 87 } catch (SQLException e) { 88 e.printStackTrace(); 89 } finally { 90 try { 91 connection.close(); // 关闭连接,释放资源 92 } catch (SQLException e) { 93 e.printStackTrace(); 94 } 95 } 96 97 } 98 // 一定要执行remove操作,否则会出错(因为Tomcat服务器底层使用了线程池技术) 99 conns.remove(); 100 } 101 102 103 /** 104 * 回滚事务并关闭释放连接 105 */ 106 public static void rollbackAndClose(){ 107 Connection connection = conns.get(); 108 if (connection != null) { // 如果不等于null,说明之前使用过连接,操作过数据库 109 try { 110 connection.rollback(); // 回滚事务 111 } catch (SQLException e) { 112 e.printStackTrace(); 113 } finally { 114 try { 115 connection.close(); // 关闭连接,释放资源 116 } catch (SQLException e) { 117 e.printStackTrace(); 118 } 119 } 120 121 } 122 // 一定要执行remove操作,否则会出错(因为Tomcat服务器底层使用了线程池技术) 123 conns.remove(); 124 } 125 126 127 public static void main(String[] args) { 128 129 } 130 131 }
在Dao里删除close操作并抛出异常
3.2 使用Filter过滤器统一给所有的Service方法都加上try-catch。来进行实现的管理。
在com.gychen.filter里新建TransactionFilter并配置web.xml
1 package com.gychen.filter; 2 3 import com.gychen.utils.JdbcUtils; 4 5 import javax.servlet.*; 6 import java.io.IOException; 7 8 public class TransactionFilter implements Filter { 9 10 @Override 11 public void init(FilterConfig filterConfig) throws ServletException { 12 13 } 14 15 @Override 16 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 17 18 try { 19 filterChain.doFilter(servletRequest,servletResponse); 20 JdbcUtils.commitAndClose(); // 提交事务 21 } catch (Exception e) { 22 JdbcUtils.rollbackAndClose(); // 回滚事务 23 e.printStackTrace(); 24 25 } 26 } 27 28 @Override 29 public void destroy() { 30 31 } 32 }
在BaseServlet中抛出异常 throw new RuntimeException(e);
3.3 将所有异常都统一交给Tomcat展示友好的错误信息页面
在web.xml中可以通过错误页面配置来进行管理
1 <!--错误页面管理配置--> 2 <!-- error-page标签配置 服务器出错之后自动跳转的页面 --> 3 <error-page> 4 <!-- error-code是错误类型 --> 5 <error-code>500</error-code> 6 <!-- location表示要跳转的页面路径 --> 7 <location>/pages/error/error500.jsp</location> 8 </error-page>
在pages里新建error文件夹,新建erro500.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>error500</title> </head> <body> 很抱歉,您访问的页面服务器出现错误 </body> </html>