ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ThreadLocal+Filter解决事务、友好响应错误页面

2021-12-09 15:58:16  阅读:146  来源: 互联网

标签:try Filter ThreadLocal static new public conn 页面


ThreadLocal+Filter解决事务

前言

ThreadLocal+Filter解决事务,以及如何友好的响应错误页面。

一、ThreadLocal

1、特点

1)可以给当前线程关联一个数据(若是要给一个线程关联多个数据,就应该使用多个ThreadLocal对象),该数据可以是普通变量、对象、数组、集合。(就像是一个线程安全的HashMap,键为当前线程名,值为关联的值)
2)一般定义为static类型对象,保证只有一个且快速获取。
3)在线程销毁时,该对象会由虚拟机自动释放。

2、用处

1)保证每个线程都使用一个数据(变量、对象、数组、集合)。
如Servlet是单例多线程,一个请求一个线程,一次处理业务逻辑一个线程,让这个线程共用一个数据库连接对象,可以来管理事务。

3、如何使用?

package com.xhu.test;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class TestThreadLocal {
    public static Map<String, Object> m = new ConcurrentHashMap<>();
    public static Map<String, Object> unsafeM = new HashMap<>();
    public static ThreadLocal<Object> threadL = new ThreadLocal<>();
    public static List<Integer> list = new ArrayList<>();
    private static Random random = new Random();

    public static class Test implements Runnable {

        @Override
        public void run() {
            //int i = random.nextInt(1000);
            //m.put(Thread.currentThread().getName(), i);
            //threadL.set(i);
            //threadL.get(Thread.currentThread().getName())
            list.add(1);
            unsafeM.put(Thread.currentThread().getName(), 1);
            try {
                //System.out.println(Thread.currentThread().getName() + "的数字为:" + i);
                //System.out.println(Thread.currentThread().getName() + "的数字为:" + i);
                Thread.sleep(0);
                //new Service1().service1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        for (int i = 0; i < 10000; i++) {
            new Thread(test).start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ConcurrentHashMap:" + m.size());
        System.out.println("ArrayList:" + list.size());
        System.out.println("HashMap:" + unsafeM.size());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ConcurrentHashMap:" + m.size());//线程安全
        System.out.println("ArrayList:" + list.size());//不安全
        System.out.println("HashMap:" + unsafeM.size());//不安全
    }
}

4、管理事务

JDBC中如何处理事务和改造图,
在这里插入图片描述

A.改造事务的源头getConnection

源头进行管理,就可以管理好事务,所以对一个线程获取连接时进行改造。

	private static ThreadLocal<Connection> threadL = new ThreadLocal<>();
    private static DataSource ds = null;
    static{
        Properties pro = new Properties();
        InputStream is = new BufferedInputStream(类名.class.getClassLoader().getResourceAsStream("druid.properties"));
        try {
            pro.load(is);
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取线程中的连接
	public static Connection getConnection(){
        Connection conn = threadL.get();
        if(conn == null){
            try {
                conn = ds.getConnection();
                threadL.set(conn);
                conn.setAutoCommit(false);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return conn;
    }
    //统一commit和close
    public void closeAndCommit() {
        Connection conn = threadL.get();
        if(conn != null){
            try {
                conn.commit();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    //统一rollback和close
    public void closeAndRollback() {
        Connection conn = threadL.get();
        if(conn != null){
            try {
                conn.rollback();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

B.配合Filter给每个请求加上try-catch

在这里插入图片描述

Filter给每个请求加上try-catch来判断是否回滚,所以关于事务中的异常都要上抛,而且不能提前关闭连接。交给Filter,

	<filter>
        <filter-name>TXFilter</filter-name>
        <filter-class>com.xhu.filter.TXFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TXFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
   
package com.xhu.filter;

import com.xhu.utils.JDBCUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 统一管理所有事务
 */
public class TXFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest, servletResponse);
            JDBCUtils.closeAndCommit();
        } catch (Exception e) {
            //conn回滚且关闭
            JDBCUtils.closeAndRollback();
            //此时需要把异常交给Tomcat,Tomcat有对应的处理
            throw new RuntimeException();
        }
    }
}

二、tomcat友好的响应异常

方式1)通过异常上抛给tomcat,让tomcat捕获并做相应处理。方式2)或者上面不是由统一的Filter,可以自己处理。

1、web.xml

 <!-- 配置服务器响应异常页面 -->
    <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/notFound404.jsp</location>
    </error-page>

2、Filter

package com.xhu.filter;

import com.xhu.utils.JDBCUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 统一管理所有事务
 */
public class TXFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest, servletResponse);
            JDBCUtils.closeAndCommit();
        } catch (Exception e) {
            //conn回滚且关闭
            JDBCUtils.closeAndRollback();
            
            //既然能捕获整个请求报的错误,除了上抛给tomcat处理(在web.xml中配置),还可以统一跳到友好的error页面。

            HttpServletRequest req = (HttpServletRequest) servletRequest;
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            int status = resp.getStatus();
            if (status == 404)
                resp.sendRedirect(req.getContextPath() + "/404.jsp");
            else if (status == 500)
                resp.sendRedirect(req.getContextPath() + "/error.jsp");
        }
    }
}

总结

1)Filter+ThreadLocal解决事务问题。
2)Tomcat或Filter捕获异常来友好的响应错误页面。

参考文献

[1] JavaWeb 尚硅谷

标签:try,Filter,ThreadLocal,static,new,public,conn,页面
来源: https://blog.csdn.net/qq_43164662/article/details/121823336

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有