手写类似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<JpaDomainString{
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事务(简略实现)的主要内容,如果未能解决你的问题,请参考以下文章

手写Spring事务框架

利用反射手写代码实现spring AOP

JavaEE手写AOP实现,自动代理, AOP 面向切面的编程思想

Java开发Spring之AOP详解(xml--注解->方法增强事务管理(声明事务的实现))

03Spring源码-手写篇-手写AOP实现(上)

03Spring源码-手写篇-手写AOP实现(上)