Dynamic agent (based on interface) way to realize thread binding and transaction control (non-aop)

Realize thread binding and transaction control based on interface dynamic agent

First, let's take a look at the two methods of implementing Service

1. It is an implementation class itself

Very conventional configurations are highly coupled

    <!-- 本身就是一个实现类 -->
    <!-- 业务层对象 配置Service-->
    <bean id="accountService" class="bruce.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!-- 配置dao对象 -->
    <bean id="accountDao" class="bruce.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner-->
        <property name="runner" ref="runner"></property>
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

2. The way of dynamic proxy

The ones used here are interface-based and sub-class-based. You can refer to my previous introduction to dynamic proxy.

    <!-- 配置代理的service -->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService">

    </bean>
    <!-- 配置beanfactory -->
    <bean id="beanFactory" class="bruce.factory.BeanFactory">
        <!-- 注入service -->
        <property name="accountService" ref="accountService"></property>
        <!-- 注入事务管理器 -->
        <property name="txManager" ref="txManager"></property>
    </bean>

What is introduced here is the way of dynamic proxy

Tool preparation

Structure
insert image description here
Some other source codes are put at the end

1. Transaction control

In order to avoid the need to open the transaction, commit the transaction, roll back the transaction and release the connection every time (as shown in the figure below

Write a transaction management tool class for transaction control
Tool classes related to transaction management, which include, open transactions, commit transactions, rollback transactions and release connections
insert image description here

public class TransactionManager {
    
    
    private ConnectionUtils connectionUtils;

    public ConnectionUtils getConnectionUtils() {
    
    
        return connectionUtils;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
    
    
        this.connectionUtils = connectionUtils;
    }
    public void beginTransaction(){
    
    
        try {
    
    
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
    public void commit(){
    
    
        try {
    
    
            connectionUtils.getThreadConnection().commit();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

    }
    public void rollback(){
    
    
        try {
    
    
            connectionUtils.getThreadConnection().rollback();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
    public void release(){
    
    
        try {
    
    
            connectionUtils.getThreadConnection().close();//还回连接池
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
    //线程和连接得解绑 要不然下次拿一个线程上面是由连接的 不能用
}

2. Binding of connected tool threads

In order to prevent an error from occurring halfway through the execution of a method, but the previous steps have been executed and cannot be recovered.
For example: you spent money on the transfer, the system made a mistake, and the money did not go to someone else’s card
Connection tool class, which is used to obtain a connection from the data source, and realize the binding with the thread
insert image description here

public class ConnectionUtils {
    
    
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    private DataSource dataSource;
    public DataSource getDataSource() {
    
    
        return dataSource;
    }
    public void setDataSource(DataSource dataSource) {
    
    
        this.dataSource = dataSource;
    }
    public Connection getThreadConnection() {
    
    
        //1.先从ThreadLocal上获取
        Connection conn = tl.get();
        try {
    
    
            //2.判断当前线程上是否有连接
            if (conn == null) {
    
    
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
    }
    public void removeConnection(){
    
    
        tl.remove();
    }
}

3. Interface-based dynamic proxy

User's persistence layer implementation class
insert image description here

focus
Bind threads when executing sql statements
insert image description here

focus! ! ! ! !

Realize dynamic proxy for Service in BeanFactory

The factory used to create the proxy object of the Service
insert image description here
is generally
insert image description here
, but in order to realize the dynamic proxy
and
let all the methods in the Service go through it,
the transaction is controlled

as follows

public IAccountService getAccountService() {
    
    
        return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
    
    
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                        Object returnValue = null;
                        try {
    
    
                            //1.开启事务
                            txManager.beginTransaction();
                            //2.执行操作
                            returnValue = method.invoke(accountService, args);
                            //3.提交事务
                            txManager.commit();
                            //4.返回结果
                            return returnValue;
                        } catch (Exception e) {
    
    
                            //5.回滚操作
                            txManager.rollback();
                            throw new RuntimeException();
                        } finally {
    
    
                            //6.释放连接
                            txManager.release();
                        }
                    }
                });
    }
}

This completes the dynamic proxy method to achieve thread binding and transaction control

For a detailed introduction to the dynamic proxy method, please see my previous article

Introduction to dynamic proxy (non-aop) Based on interface and based on subclasses

Attaching miscellaneous code might help

Test test class

/**
 * 使用Junit单元测试:测试配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
    
    
    @Autowired
    //有多个且没有一个beanId一样
    //手动匹配
    @Qualifier("proxyAccountService")
    private IAccountService as;

    @Test
    public void testTransfer(){
    
    
        as.transfer("aaa","bbb",100f);
    }
}

bean.xml configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 都实现了IAccountService -->
    <!-- 1.动态代理 -->
    <!-- 配置代理的service -->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService">

    </bean>
    <!-- 配置beanfactory -->
    <bean id="beanFactory" class="bruce.factory.BeanFactory">
        <!-- 注入service -->
        <property name="accountService" ref="accountService"></property>
        <!-- 注入事务管理器 -->
        <property name="txManager" ref="txManager"></property>
    </bean>

    <!-- 本身就是一个实现类 -->
    <!-- 业务层对象 配置Service-->
    <bean id="accountService" class="bruce.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!-- 配置dao对象 -->
    <bean id="accountDao" class="bruce.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner-->
        <property name="runner" ref="runner"></property>
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!-- 配置QueryRunner
    默认是单例对象 多个dao在使用同一个对象 可能用完它的时候一个在用另一个还没用完 导致线程互相干扰
    多例的话会保证每次使用这个对象都是创建一个新的-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!-- 注入数据源 QueryRunner是没有set方法和注入 需要使用构造函数注入(前面的都是set方法注入 -->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!-- 配置数据源 (导c3p0的包-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 连接数据库的必备信息 -->
        <!--mysql的驱动-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <!--连接字符串-->
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springxmlioc?serverTimezone=Asia/Shanghai"></property>
        <property name="user" value="root"></property>
        <property name="password" value="530203402"></property>
    </bean>

    <!-- 配置Connection的工具类 ConnectionUtils
        谁在用呢 是dao在用,需要在上面 dao注入一下ConnectionUtils
    -->
    <bean id="connectionUtils" class="bruce.utils.ConnectionUtils">
        <!-- 注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置事务管理器-->
    <bean id="txManager" class="bruce.utils.TransactionManager">
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
</beans>

I cook, I hope to help you.
Recently, I am worried about the pressure of the postgraduate entrance examination.
I also want to experience Wuhu~~ The feeling of taking off~~~
Unfortunately, come on

Guess you like

Origin blog.csdn.net/weixin_42727032/article/details/104980165