参考《架构探险--从零开始写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;
}

自定义ThreadLocal和事务(基于自定义AOP)的更多相关文章

  1. 010-Spring aop 001-核心说明-拦截指定类与方法、基于自定义注解的切面

    一.概述 面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志.安全.事务.性能监控等等.SpringAOP允许将公共行为从业务 ...

  2. springboot自定义jdbc操作库+基于注解切点AOP

    发布时间:2018-11-08   技术:springboot+aop   概述 springBoot集成了自定义的jdbc操作类及AOP,因为spring自带的JdbcTemplate在实际项目中并 ...

  3. Spring声明式事务管理基于tx/aop命名空间

    目的:通过Spring AOP 实现Spring声明式事务管理; Spring支持编程式事务管理和声明式事务管理两种方式. 而声明式事务管理也有两种常用的方式,一种是基于tx/aop命名空间的xml配 ...

  4. Security » Authorization » 基于自定义策略的授权

    Custom Policy-Based Authorization¶ 基于自定义策略的授权 98 of 108 people found this helpful Underneath the cov ...

  5. 一种基于自定义代码的asp.net网站首页根据IP自动跳转指定页面的方法!

    一种基于自定义代码的asp.net网站首页根据IP自动跳转指定页面的方法! 对于大中型网站,为了增强用户体验,往往需要根据不同城市站点的用户推送或展现相应个性化的内容,如对于一些大型门户网站的新闻会有 ...

  6. 第13章 TCP编程(4)_基于自定义协议的多线程模型

    7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...

  7. 第13章 TCP编程(3)_基于自定义协议的多进程模型

    5. 自定义协议编程 (1)自定义协议:MSG //自定义的协议(TLV:Type length Value) typedef struct{ //协议头部 ];//TLV中的T unsigned i ...

  8. [Asp.Net web api]基于自定义Filter的安全认证

    摘要 对第三方开放的接口,处于安全的考虑需要对其进行安全认证,是否是合法的请求.目前在项目中也遇到这种情况,提供的接口因为涉及到客户铭感数据,所以在调用的时候,不能直接暴露,需要有一个认证的机制.所以 ...

  9. 基于自定义Validator来验证枚举类型

    基于自定义Validator来验证枚举类型 一.背景 二.技术要点 三.实现一个自定义枚举校验. 1.需求. 2.实现步骤 1.自定义一个 Sex 枚举. 2.自定义一个 Enum 注解 3.编写具体 ...

随机推荐

  1. Vue-cli2项目文件目录解析

    前言 不是原创,真的不是原创,主要我是根据CSDN的一篇文章和其他平台上的文章整理而来,在最后我会贴上所有原文的地址,下面正式进入正文. Vue-cli项目文件目录结构 这个是Vue-cli2.0版本 ...

  2. 帝国CMS模板中的多条件筛选方法

    需求:点击某一条目,调出与该条目关键词相关的类似词条数据 要点: 1.帝国CMS灵动标签使用   [e:loop= 2.专题关键词筛选  enewszt 3.SQL语句筛选   select * fr ...

  3. moongoose对象无法新增删除属性

    昨天用nodes中的moongoose去查询一个结果遇到一个大坑,这个坑貌似用moongoose可能会遇到.背景是这样的,我在nodejs中去查询document,得到的可以看作是一个对象list.在 ...

  4. Vue 实现一个分页组件

    实现分页组件要分三个部分 样式,逻辑,和引用 首先新建一个vue文件用来承载组件内容 第一步:构建样式 <template> <nav> <ul class=" ...

  5. em、rpx和px的换算

    rpx:对于小程序开发,所用的单位都是rpx,而不论哪个型号的手机,屏幕宽度都是750rpxrpx与px的转换,根据设计稿换算例如:设计稿750px宽度,ps上量出或者标出的宽度是多少,那么就定义多少 ...

  6. 快速禁止Chrome浏览器缓存

    在前端的开发中,最麻烦的莫过于浏览器的缓存,经常需要清理缓存文件,导致开发效率较低. 但Chrome可以一键禁止浏览器缓存,并且在后续的操作中,无论相同的资源请求多少次,都不会缓存到本地,一起来体验下 ...

  7. 2018-8-10-win10-uwp-读写XML

    title author date CreateTime categories win10 uwp 读写XML lindexi 2018-08-10 19:16:51 +0800 2018-2-13 ...

  8. openstack stein部署手册 8. neutron-api

    # 建立数据库用户及权限 create database neutron; grant all privileges on neutron.* to neutron@'localhost' ident ...

  9. Unity 官网无法访问|国外网站访问过慢|国外网站访问加速器

    目录 1. 文档地址 2. 按 3. 工具下载地址 1. 文档地址 GitHub博客 https://coco5666.github.io/blog/articles/20190704-01/ 2. ...

  10. motd - 当日消息

    描述 (DESCRIPTION) 在 登录 系统 后, 执行 登录 shell 前, login(1) 显示 /etc/motd 中的 内容. "motd" 意思是 "m ...