手写spring事务管理器

前面已经手写了数据库查询框架,现在开始进入正题手写事务管理器,什么是事务啥定义的这里不废话,请自行查阅书籍或网络文献。

这里来手写JDBC事务管理器,思考spring中使用注解@Transactional为什么就能进行事务控制?

好了直接进入正题,需要具备的基础知识如下:

  1. Java反射,注解技术
  2. 动态代理技术
  3. ThreadLocal(方便理解,就是一个HashMap,key是当前执行线程的ID,value则是自定义设置的值,每个线程有独立的值)

这里复习下JDBC事务是如何实现的:


Connection conn=getConnection();//自定义实现
try{  
    conn.setAutoCommit(false);
    //做多个数据库update操作
    
    //提交
    conn.commit();
} catch (Exception e) {
    conn.rollback();
}finally{

}

JDBC事务的逻辑就是

  1. 关闭自动提交
  2. 执行数据库更新业务
  3. 手动提交事务
  4. 若发生异常则回滚 

可以看出跨Dao控制事务的原理,就是多个dao共同使用同一个连接不能被关系,且连接关闭自动提交。

我们思考spring的事务管理是在service层,对service层的每个方法进行代理拦截,判断该方法若有@Transactional事务注解,那么就为该方法增强,添加事务控制,实现事务service方法代理增加事务拦截,接下来我们开始实现

新建事务标识注解

@Target({ ElementType.METHOD, ElementType.TYPE })
	@Retention(RetentionPolicy.RUNTIME)
	@Inherited
	@Documented
	public @interface Transactional {
		String value() default "";
	}

新建事务管理器,实现事务控制的几个方法行为,ThreadLocal可以保证每条线程获取的资源为同一个

public class TranscationalManager {

	/**
	 * 开启事务
	 */
	final static ThreadLocal<Boolean> transcational = new ThreadLocal<Boolean>();

	/**
	 * 数据库连接
	 */
	final static ThreadLocal<Connection> currentConnection = new ThreadLocal<Connection>();

	static DataSource dataSource;


    /**
	 * 事务开启
	 */
	static void openTranscational() {
		transcational.set(true);
	}

	/**
	 * 是否开启事务 true开启/false未开启
	 */
	static boolean isOpenTranscational() {
		boolean result = false;
		if (transcational.get() == null) {
			result = false;
		} else {
			result = transcational.get();
		}
		return result;
	}

	/**
	 * 事务关闭
	 */
	static void closeTranscational() {
		transcational.set(false);
	}

	/**
	 * 事务提交
	 */
	static void commit() throws SQLException {
		Connection conn = currentConnection.get();
		if (conn != null) {
			conn.commit();
		}
	}

	/**
	 * 回滚
	 */
	static void rollback() throws SQLException {
		Connection conn = currentConnection.get();
		if (conn != null) {
			conn.rollback();
		}
	}

	/**
	 * 关闭连接,需要先关闭事务在调用此方法
	 */
	static void closeConnection() throws SQLException {
		closeTranscational();
		Connection conn = currentConnection.get();
		if (conn != null) {
			conn.close();
		}
	}

	/**
	 * 释放资源
	 */
	static void release() {
		transcational.remove();
		currentConnection.remove();
	}

}

接下来是数据库连接代理,目的是代理close方法,在开启事务时候,不关闭连接

public class ConnectionProxy implements InvocationHandler {
		Connection targetConn;

		public Connection proxyConnection(Connection targetConn) {
			this.targetConn = targetConn;
			Connection proxyConn = (Connection) Proxy.newProxyInstance(targetConn.getClass().getClassLoader(),
					new Class[] { Connection.class }, this);
			return proxyConn;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			if (method.getName().equals("close")) {
				// 清除当前的数据库连接
				if (isOpenTranscational()) {
					// 若开启事务的 连接将不做操作
					return null;
				}
				targetConn.setAutoCommit(true);
				targetConn.close();
				return null;
			}

			Object result = method.invoke(targetConn, args);
			return result;

		}

		public Connection getTargetConn() {
			return targetConn;
		}
	}

事务管理切面实现,service的方法有加事务注解的,那么将事务管理控制,这里仅仅实现了,如果当前存在事务,那么新的事务就加入该事务,类似spring 事务还有其他传播特性(如新开事务等),这里没有实现,剩下留给大家了,这里主要是学习思想

public class TranscationalInvocationHandler implements InvocationHandler {
		public static Object proxyTranscational(Object service) throws InstantiationException, IllegalAccessException {
			// 此处若有 @Transactional 代理
			Class<? extends Object> clazz = service.getClass();
			Class<?>[] interfaces = clazz.getInterfaces();
			boolean isProxy = false;
			for (Method method : clazz.getDeclaredMethods()) {
				if (method.isAnnotationPresent(Transactional.class)) {
					isProxy = true;
					break;
				}
			}
			if (isProxy) {
				return Proxy.newProxyInstance(clazz.getClassLoader(), interfaces,
						new TranscationalInvocationHandler(service));
			} else {
				return service;
			}
		}

		Object traget;

		public TranscationalInvocationHandler(Object bean) {
			this.traget = bean;
		}

		public Object getTraget() {
			return traget;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

			Object invoke = null;

			Method declaredMethod = traget.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());

			// 不需要事务
			if (!declaredMethod.isAnnotationPresent(Transactional.class)) {
				try {
					return method.invoke(traget, args);
				} catch (InvocationTargetException e) {
					throw e.getTargetException();
				} finally {
					// 原本不存在事务时候 才释放资源
					if (!TranscationalManager.isOpenTranscational()) {
						TranscationalManager.closeConnection();// 防止开发者在dao层面拿取数据库连接 没有关闭导致连接泄漏
						TranscationalManager.release();
					}
				}
			}

			// 如果原本存在事务,那么加入事务
			if (TranscationalManager.isOpenTranscational()) {
				try {
					// 数据库连接处理
					return invoke = method.invoke(traget, args);
				} catch (InvocationTargetException e) {
					TranscationalManager.rollback();
					throw e.getTargetException();
				}
			}

			// 需要开启事务
			try {
				// 有事务注解的开启事务
				TranscationalManager.openTranscational();
				// 数据库连接处理
				invoke = method.invoke(traget, args);
				TranscationalManager.commit();
			} catch (InvocationTargetException e) {
				TranscationalManager.rollback();
				throw e.getTargetException();
			} finally {
				// 关闭连接 释放资源
				TranscationalManager.closeConnection();
				TranscationalManager.release();
			}
			return invoke;
		}

	}

事务管理器获取数据库连接代理主入口

/**
	 * 获取数据库连接
	 */
	public static Connection getConnection() throws SQLException {
		if (currentConnection.get() != null) {
			return currentConnection.get();
		}
		Connection conn = dataSource.getConnection();
		ConnectionProxy connectionProxy = new ConnectionProxy();
		Connection proxyConnection = connectionProxy.proxyConnection(conn);
		currentConnection.set(proxyConnection);
		
		if(TranscationalManager.isOpenTranscational()){
			conn.setAutoCommit(false);
		}else{
			conn.setAutoCommit(true);
		}
		
		return proxyConnection;
	}

事务管理器代理service层接口

	/**
	 * 代理service层的事务控制
	 */
	@SuppressWarnings("unchecked")
	public static <T> T proxyTranscationalService(T serviceBean) {
		try {
			return (T) TranscationalInvocationHandler.proxyTranscational(serviceBean);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

 到这里我们已经实现了事务管理器,接下来开始测试

准备:数据库mysql,在test用户下新建user表

CREATE TABLE `user` (
  `user_name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 新建service层的接口与实现类

/**
	 * 测试接口类
	 */
	public interface UserService{
		void modifyUsers() throws Exception;
	}
	
	

这里实现service俩个dao操作,分别往同一张user表插入数据,获取2次连接,使用完分别关闭连接,并没有手动控制事务(在mybatis更新操作中,用户是屏蔽数据库连接的,这里为了与其他知识解耦,这里就简单JDBC操作来替代了)

public class UserServiceImpl implements UserService{

		/**
		 * 加上事务注解
		 */
		@Transactional
		@Override
		public void modifyUsers() throws Exception {
		
			//操作1与操作2 同属于一个事务,
			//模拟dao操作1
			Connection conn = TranscationalManager.getConnection();
			PreparedStatement pst = conn.prepareStatement(" insert into user(user_name,password) values('姓名1','123' ) ");
			pst.executeUpdate();
			pst.close();
			conn.close();
			if(true) {
//				throw new RuntimeException("强制异常,让后面的执行不了,会发现前者并没有插入到数据库库中");
			}
			//模拟dao操作2
			Connection conn1 = getConnection();
			PreparedStatement pst1 = conn1.prepareStatement(" insert into user(user_name,password) values('姓名2','123' ) ");
			pst1.executeUpdate();
			pst1.close();
			conn1.close();
			
			if(true) {
				throw new RuntimeException("强制异常,让后面的执行不了,会发现前者并没有插入到数据库库中");
			}
		}
	}

  接下来就是主函数测试,执行后发生了异常,可以观察数据库表并没有新增数据,可以自行注释修改 if(true)的代码段,分别表示发生异常的时候,数据将回滚,如果没有异常,那么会发现数据库新增2条数据

/**
	 * 主函数测试
	 */
	public static void main(String[] args) {
		
		//原本的service层的
		UserService userService = new UserServiceImpl();
		
		//事务管理代理
		userService = proxyTranscationalService(userService);
	
		try {
			//执行更新
			userService.modifyUsers();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

跨Dao事务在service层实现了事务管理,可以发现这些框架都是基于动态代理的灵活运用,将逻辑可以在各个切面进行插入,这就是所谓的切面编程AOP

另外本人该分类章节下所有文字与程序代码都是鄙人纯所写,鄙人其实从来没看过开源框架源码,纯靠使用开源框架的特性逆向思考分析它是如何实现的(那些框架在我眼中最初版本也就是我这样的,然后不断迭代,程序框架不断的提炼,使用设计模式程序分层抽象,方便扩展),最后附上源码下载链接:TranscationalManager.java-互联网文档类资源-CSDN下载

接下来可以思考下"分布式事务"是如何的实现的呢? 看完了点个关注,评论下,谢谢支持! 后续我将分享"如何手写分布式事务框架"

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓霖涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值