zoukankan      html  css  js  c++  java
  • 自定义ThreadLocal和事务(基于自定义AOP)

    参考《架构探险--从零开始写javaweb框架》4.6章节

    自定义ThreadLocal

    package smart;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    public class MyThreadLocal<T> {
        private Map<Thread, T> container = Collections.synchronizedMap(new HashMap<>());
        public void set(T value) {
            container.put(Thread.currentThread(), value);
        }
        public T get() {
            Thread thread = Thread.currentThread();
            T value = container.get(thread);
            if(value == null && !container.containsKey(thread)) {
                value = initialValue();
                container.put(thread, value);
            }
            return value;
        }
        
        public void remove() {
            container.remove(Thread.currentThread());
        }
    
        protected T initialValue() {
            return null;
        }
    }
    

    ThreadLocal容器,存放线程局部变量,设计目标:解决线程并发问题,使用场景:存放JDBC Connection,达到事务控制的能力。

    将connection放在DBUtil中的静态变量,多个线程操作DBUtil会导致线程不安全,使用多线程测试,线程A关闭线程B的连接,导致出错。所以在DBUtil中使用ThreadLocal存储connection来线程隔离,避免线程不安全。实际工作中推荐使用连接池而不是DBUtil。

    Spring事务传播行为7种:
    方法A传播到方法B,4种情况:
    方法A有事务,方法B也有事务
    方法A有事务,方法B没有事务
    方法A没有事务,方法B有事务
    方法A没有事务,方法B也没有事务

    假设事务从方法A传播到方法B,用户需要面对方法B,考虑方法A有事务吗?
    propagation_required 没有则新建,有则加入当前事务,spring的默认事务传播行为
    propagation_required_new 没有则新建,有则将当前事务挂起,新建一个事务且和原来的事务没关系
    propagation_nested 没有则新建,有则嵌套在当前事务中,嵌套子事务与主事务关联(主事务提交或回滚,子事务也提交或回滚),在方法A调用方法B,应该用在方法A而不是方法B上
    propagation_supports 没有就以非事务方式执行,有就使用当前事务,有就有,没有就没有,无所谓,反正支持
    propagation_not_supported 没有就以非事务方式执行,有就将当前事务挂起,很强硬,没有就没有,有也不支持,挂起来不管它
    propagation_never 没有就以非事务方式执行,有就抛异常,很强硬,没有就没有,有反而报错,不支持事务
    propagation_mandatory 没有抛异常,有就使用当前事务,很强硬,没有事务要报错,必须要有事务

    附加功能:
    事务超时:transaction timeout,解决事务时间长消耗资源多的问题,故意给事务设置一个最大时长,超过了就回滚
    只读事务:readonly transaction,为了忽略不需要事务的方法,比如读取数据,可以提高性能

    xml式事务配置:不推荐,tx:annotation-driven/
    注解式事务配置:推荐,方法上使用@Transactional,可在里面设置事务隔离级别,事务传播行为,事务超时时间,是否只读事务。

    事务管理思维导图:

    基于自定义AOP实现自定义事务:
    connection.setAutoCommit(false);关闭自动提交事务(开启事务);
    connection.commit();提交事务
    connection.rollback();回滚事务
    这3个方法放在代理的钩子中:

    package smart.myaop;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 方法级别,自定义事务注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Transaction {
    }
    
    package smart.myaop;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import smart.myaop.framework.Proxy;
    import smart.myaop.framework.ProxyChain;
    
    import java.lang.reflect.Method;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    /**
     * 事务代理切面类
     */
    public class TransactionProxy implements Proxy {
        private static final Logger logger = LoggerFactory.getLogger(TransactionProxy.class);
        private static final ThreadLocal<Connection> CONNECTION_HOLDER = new ThreadLocal<>();
        /**
         * FLAG_HOLDER本地线程变量是个标注,保证同一个线程中事务控制相关逻辑只执行一次
         */
        private static final ThreadLocal<Boolean> FLAG_HOLDER = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return false;
            }
        };
    
        /**
         * proxyChain对象获取目标方法判断方法是否带有Transaction注解,然后调用beginTransaction方法开启事务,调用proxyChain对象的doProxyChain方法执行目标方法
         * 然后调用commitTransaction提交事务或在异常中执行rollbackTransaction方法回滚事务,最后一处FLAG_HOLDER本地线程变量中的标志
         * @param proxyChain
         * @return
         * @throws Throwable
         */
        @Override
        public Object doProxy(ProxyChain proxyChain) throws Throwable {
            Object result;
            boolean flag = FLAG_HOLDER.get();
            Method method = proxyChain.getTargetMethod();
            if(!flag && method.isAnnotationPresent(Transaction.class)) {
                FLAG_HOLDER.set(true);
                try {
                    beginTransaction();
                    logger.debug("begin transaction");
                    result = proxyChain.doProxyChain();
                    commitTransaction();
                    logger.debug("commit transaction");
                } catch (Exception e) {
                    rollbackTransaction();
                    logger.debug("rollback transaction");
                    throw e;
                } finally {
                    FLAG_HOLDER.remove();
                }
            } else {
                result = proxyChain.doProxyChain();
            }
            return result;
        }
    
        private void rollbackTransaction() {
            Connection connection = getConnection();
            if(connection != null) {
                try {
                    connection.rollback();
                    connection.close();
                } catch (SQLException e) {
                    logger.error("rollback transaction failure", e);
                    throw new RuntimeException(e);
                } finally {
                    CONNECTION_HOLDER.remove();
                }
            }
        }
    
        /**
         * 提交事务,默认事务自动提交,所以将自动提交属性设置为false,开启事务后,将连接对象放入本地线程变量中
         * 事务提交或回滚后,移除本地线程变量中的连接对象
         */
        private void commitTransaction() {
            Connection connection = getConnection();
            if(connection != null) {
                try {
                    connection.commit();
                    connection.close();
                } catch (SQLException e) {
                    logger.error("commit transaction failure", e);
                    throw new RuntimeException(e);
                } finally {
                    CONNECTION_HOLDER.remove();
                }
            }
        }
    
        /**
         * 开启事务
         */
        private void beginTransaction() {
            Connection connection = getConnection();
            if(connection != null) {
                try {
                    connection.setAutoCommit(false);
                } catch (SQLException e) {
                    logger.error("begin transaction failure", e);
                    throw new RuntimeException(e);
                } finally {
                    CONNECTION_HOLDER.set(connection);
                }
            }
        }
    
        private Connection getConnection() {
            return null; //获取connection过程略,开启事务时,应从外界获取连接对象,然后放入本地线程变量,提交和回滚事务从本地线程对象中拿连接对象
        }
    }
    

    改造自定义AOP时实现的获取增强与被增强类的映射关系获取的方法:

        /**
         * 给createTargetMap方法用
         * 代理类(切面类)与目标类集合之间的一对多映射关系
         * 在全部class对象集合中搜索满足1是AspectProxy子类,2被@Aspect注解,这样的类(代理类),根据@Aspect注解指定的注解属性去获取该注解对应的目标类集合
         * 然后建立代理类与目标类集合之间的映射关系,据此分析出目标类与代理对象列表之间的映射关系
         * @return
         * @throws Exception
         */
        private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
            Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<>();
            Set<Class<?>> proxyClassSet = new HashSet<>();
            for (Class<?> aClass : allClassSet) {
                if(AspectProxy.class.isAssignableFrom(aClass) && !AspectProxy.class.equals(aClass)) {
                    proxyClassSet.add(aClass); //获取AspectProxy子类class对象集合
                }
            }
            for (Class<?> aClass : proxyClassSet) {
                if(aClass.isAnnotationPresent(Aspect.class)) {
                    Aspect aspect = aClass.getAnnotation(Aspect.class);
                    Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                    proxyMap.put(aClass, targetClassSet);
                }
            }
    
            /**
             * 将事务代理机制添加到AOP中,上面是普通切面代理,这里添加事务代理
             * 事务增强类映射到所有@Service类
             */
            Set<Class<?>> serviceClassSet = new HashSet<>();
            for (Class<?> aClass : allClassSet) {
                if(aClass.isAnnotationPresent(Service.class)) {
                    serviceClassSet.add(aClass); //这里从所有的class集合中挑出被@Service注解的class对象集合
                }
            }
            proxyMap.put(TransactionProxy.class, serviceClassSet);
    
            return proxyMap;
        }
    
  • 相关阅读:
    Calendar类的应用
    使用Date和SimpleDateFormat类表示时间
    java中基本类型和字符串类型的转换
    java中的包装类
    java中的字符
    List的增删改插
    异常总结
    java中的异常链
    java中的异常抛出以及自定义异常
    spring mvc异常统一处理常见的有3种方式:
  • 原文地址:https://www.cnblogs.com/kibana/p/11762564.html
Copyright © 2011-2022 走看看