Filter和事务管理
Filter
1.使用
package com.donggei.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* doFilter方法,专门用于拦截请求。可以做权限检查
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
Object user = req.getSession().getAttribute("user");
if (user == null){
//说明没有登入
req.getRequestDispatcher("login").forward(request,response);
}else {
//这行代码很重要 没有得话将不会继续访问应该访问的页面
//让程序继续往下访问用户的目标资源
chain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
<filter>
<filter-name>AdminFilter</filter-name>
<filter-class>com.donggei.servlet.AdminFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<url-pattern>/a.jsp</url-pattern>
<url-pattern>/manager</url-pattern>
</filter-mapping>
规定那些路径要检查,检查的Filter类是那个。
2.生命周期
3.FilterConfig类
4.FilteChain类
chain:链 过滤器链(多个过滤器如何工作)
6.Filter的拦截路径
精确匹配
目录匹配
后缀名匹配
例:
事务管理
1.知识储备
如果忘记了事务的操作 点击:java中的数据库事务处理
事务管理遇到的问题:
【图是Controller 就是Servlet】
解决方法:
过滤器的典型应用:
2.Threadlocal
一个线程绑定了一个值,单独线程内不管怎么操作不会影响这个线程的数据传递
3.例子:
服务层出错,抛到了web(Servlet)层
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);
}
}
}
try {
orderId = orderService.createOrder(cart, userId);
JdbcUtils.commitAndClose();
} catch (Exception e) {
JdbcUtils.rollbackAndClose();
e.printStackTrace();
}
只是对dao层的Base进行了修改,继承了BaseDao的DaoImpl类,执行数据库操作时如果发生了异常,就会先高层Service抛出
RuntimeException(运行时异常一般是不用捕捉的,我们这里对这个Service进行了捕捉),发生异常时就回滚,整个Service结束后才提交事务。
Runtime异常会自己先上抛出;需要我们手动捕捉
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();
}
}
4.实际项目中的应用
例子中的方法 很笨拙,每次用Service都需要异常处理
使用Filter过滤器统一给所有的Service方法都加上try-catch。来进行实现的管理。
创建事务过滤器
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@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();
//把异常抛给Tomcat管理展示友好的错误页面
throw new RuntimeException(e);
}
}
@Override
public void destroy() {
}
}
所有请求都加上了事务管理
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.donggei.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /* 表示当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
特别注意的是:如果 从 BaseDao 一层一层向外抛的过程中 有try…catch…的地方 还要再向外抛出
eg:BaseServlet中
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
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();
// !!增加的部分!!
// 向上抛给filter
throw new RuntimeException(e);
}
}
}
错误页面
try {
filterChain.doFilter(servletRequest,servletResponse);
JdbcUtils.commitAndClose();// 提交事务
} catch (Exception e) {
JdbcUtils.rollbackAndClose();//回滚事务
e.printStackTrace();
//把异常抛给Tomcat管理展示友好的错误页面
throw new RuntimeException(e);
}
然后再在web.xml配置中
<!--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>