手写类似Spring的AOP事务(简略实现)
Posted 喜欢你的凌云子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写类似Spring的AOP事务(简略实现)相关的知识,希望对你有一定的参考价值。
记录下自己尝试简略实现spring事务的代码说明
首先,初始化数据库,插入如下两条数据。
1public static void main(String[] args) throws Exception
2 {
3 test t = new test();
4 RuntimeDBForRedeay runtimeDBForRedeay = new RuntimeDBForRedeaySampleImpl();
5 runtimeDBForRedeay.createTable("DROP TABLE IF EXISTS tc_trade_order_line");
6 runtimeDBForRedeay.createTable("CREATE TABLE tc_trade_order_line "
7 + " ( id varchar(64), seller_id varchar(64), seller_name varchar(64),"
8 + "PRIMARY KEY (id) )");
9 runtimeDBForRedeay.insertData("INSERT INTO tc_trade_order_line VALUES ('1', '11', 'jack')");
10 runtimeDBForRedeay.insertData("INSERT INTO tc_trade_order_line VALUES ('2', '22', 'json')");
11 update();
12 query();
13 }
下面模拟常见的service写法,使用@Resource 注入Dao,使用@Transactional使service方法的事物交给框架管理。在transactionTest方法中使用Integer.parseInt("aa");来抛出异常,如果有异常则回滚。
1@Resource
2 private JpaDaoTest jpaDaoTest;
3 private JpaDaoTest other;
4 public static void main(String[] args) throws Exception {
5 JpaTestInterface test1 = ClassLoaderForScan.getInstance(MyJpaTest.class);
6 test1.transactionTest();
7 test1.findAll();
8 }
9 @Override
10 @Transactional
11 public void transactionTest()throws Exception
12 {
13 jpaDaoTest.getById("1");
14 jpaDaoTest.update("update tc_trade_order_line set seller_name='before1' where id=1");
15 jpaDaoTest.getById("1");
16 jpaDaoTest.update("update tc_trade_order_line set seller_name='before2' where id=1");
17 jpaDaoTest.getById("1");
18 Integer.parseInt("aa");
19 jpaDaoTest.update("update tc_trade_order_line set seller_name='after' where id=1");
20 jpaDaoTest.getById("1");
21 }
22 public void findAll() throws Exception{
23 jpaDaoTest.findAll("select * from tc_trade_order_line ");
24 }
这里是抛出异常时的执行日志,id为1的数据在两次更新之后,查询的结果都是被更新的值。但是异常抛出后,最终的findAll结果是初始值heh。这里说明正常回滚了。
去掉异常后,最终findAll结果是最后一次更新的值。这里说事物已经正常提交了。
以上是代码效果。下面具体说下怎么一步步实现。
1,怎么根据@Resource注入 jpaDaoTest?
2,怎么实现@Transactional注解的方法的事物交给框架?
1public static void main(String[] args) throws Exception {
2 JpaTestInterface test1 = ClassLoaderForScan.getInstance(MyJpaTest.class);
3 test1.transactionTest();
4 test1.findAll();
5 }
从main方法可以知道,以上两步功能的实现主要在
JpaTestInterface test1 = ClassLoaderForScan.getInstance(MyJpaTest.class);
这个方法中。
在getInstance方法中主要做三件事:
1,得到要返回的对象
2,根据resource注解给对象设值
3,根据Transactional将对象动态代理,事物在其中实现
(这里实际没有做到根据只为指定方法设置事物,而只是简单的检查到Transactional后,整个对象被动态代理,所有方法都有事物了。还没想到好的处理方法,不过这里主要是想记录事物实现的方法)
1newInstance = clazz.newInstance();//1,得到要返回的对象
2 Field[] declaredFields = clazz.getDeclaredFields();
3 for (int i = 0; i < declaredFields.length; i++) {//2,根据resource注解给对象设值
4 Resource resource = (Resource) declaredFields[i].getAnnotation(Resource.class);
5 if (resource != null) {
6 declaredFields[i].setAccessible(true);
7 declaredFields[i].set(newInstance,
8 new JpaFactory().createJpaImplAddAdvise(declaredFields[i].getType(), advise));
9 }
10 }
11 Method[] methods = clazz.getMethods();
12 for (int i = 0; i < methods.length; i++)
13 {//3,根据Transactional将对象动态代理,事物在其中实现
14 Transactional tran = methods[i].getAnnotation(Transactional.class);
15 if (null != tran)
16 {
17 newInstance = (T) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new ClassLoaderForScan().new TransactionInvocationHandler(newInstance));
18 }
19 }
先看第三步,事物怎么在代理中实现。
1public Object invoke(Object proxy, Method method, Object[] args)
2 throws Throwable
3 {
4 Object invoke = null;
5 PlatformTransactionManager transactionManager = new DefaultTransactionManager();
6 TransactionStatus transactionStatus = transactionManager.geTransaction(null);
7 try
8 {
9 invoke = method.invoke(real, args);
10 transactionManager.commit(transactionStatus);
11 }
12 catch (Exception e)
13 {
14 e.printStackTrace();
15 transactionManager.rollback(transactionStatus);
16 }
17 return invoke;
18 }
这里用到spring里面的一个概念统一事物管理器PlatformTransactionManager。
PlatformTransactionManager是一个接口,有获取事物,提交事物,回滚事物三个方法。
这三个方法围绕着service的method。也就是service的method的事物已经交给管理了,method中不在需要提交回滚的代码。
1public interface PlatformTransactionManager
2{
3
4 /**
5 * 返回一个已经激活的事务或创建一个新的事务(根据给定的TransactionDefinition类型参数定义的事务属性),
6 * 返回的是TransactionStatus对象代表了当前事务的状态,其中该方法抛出TransactionException(未检查异常)表示事务由于某种原因失败。
7 * @param definition
8 * @return
9 * @throws TransactionException
10 */
11 TransactionStatus geTransaction(TransactionDefinition definition) throws TransactionException;
12 /**
13 * 用于提交TransactionStatus参数代表的事务
14 * @param status
15 * @throws TransactionException
16 */
17 void commit(TransactionStatus status) throws TransactionException;
18 /**
19 * 用于回滚TransactionStatus参数代表的事务
20 * @param status
21 * @throws TransactionException
22 */
23 void rollback(TransactionStatus status) throws TransactionException;
24}
具体要怎么做到method的多个数据库操作能在一个事物中,就是具体事物管理器的事。
这里DefaultTransactionManager默认事物管理器实现了PlatformTransactionManager。接着看DefaultTransactionManager是怎么做到事物管理的。
1public class DefaultTransactionManager extends AbstractTransactionManager implements PlatformTransactionManager
2{
3 public Connection getConnection()
4 {
5 try {
6 return TransactionDatasouces.getTransactionConnection().getConnection();
7 } catch (Exception e) {
8 e.printStackTrace();
9 }
10 return null;
11 }
12 public void doBegin() throws TransactionException
13 {
14 try
15 {
16 TransactionDatasouces.beginTransaction();
17 }
18 catch (Exception e)
19 {
20 throw new TransactionException(e.getMessage());
21 }
22 }
23
24
25
26}
DefaultTransactionManager使用了工具类TransactionDatasouces,具体的实现在TransactionDatasouces
1public class TransactionDatasouces {
2
3 private static ThreadLocal<InnerConnection> threadConnection = ThreadLocal.withInitial(() -> null);
4 public static InnerConnection getTransactionConnection() {
5 InnerConnection innerConnection = threadConnection.get();
6 if (null == innerConnection) {
7 innerConnection =new InnerConnection(JDBCHelper1.getConnection("default"));
8 threadConnection.set(innerConnection);
9 }
10 return innerConnection;
11 }
12 public static void beginTransaction() throws Exception {
13 threadConnection.get().getConnection().setAutoCommit(false);
14 }
15 public static void commit() throws Exception {
16 threadConnection.get().getConnection().commit();
17 }
18 public static void rollback() throws Exception {
19 threadConnection.get().getConnection().rollback();
20 }
21 public static void close() throws Exception {
22 JDBCHelper1.close(threadConnection.get().getConnection(),null,null);
23 threadConnection.remove();
24 }
25}
这里简单使用线程绑定数据库连接,那么DefaultTransactionManager的实现逻辑就是同一线程同一数据库连接,也就是说service中的method所有dao操作用到同一个数据库连接,这样TransactionInvocationHandler中PlatformTransactionManager的事物管理就是实现了。
现在回到上面还没说的第二步,2,根据resource注解给对象设值,这里jpaDaoTest在执行数据库操作时只要从TransactionDatasouces获取连接就行了。
后面会写下jpaDaoTest的实现,简略的类似spring的jpa实现。
1public interface JpaDaoTest extends MyJpa<JpaDomain, String> {
2 @Query("form JpaDomain where name = ?1")
3 JpaDomain getByName(String name);
4 @Query("update tc_trade_order_line set ")
5 void update1();
6 @Query("")
7 void update2();
8}
以上是关于手写类似Spring的AOP事务(简略实现)的主要内容,如果未能解决你的问题,请参考以下文章
JavaEE手写AOP实现,自动代理, AOP 面向切面的编程思想