使用Spring与JDK动态代理实现事务管理
在现代企业级应用开发中,事务管理是一项关键的技术,它可以保证一系列操作要么全部成功,要么全部失败,从而确保数据的一致性和完整性。Spring框架提供了强大的事务管理能力,但有时为了更细粒度地控制事务边界,我们可能需要自己实现事务管理逻辑。本文将介绍如何结合Spring框架和JDK动态代理技术来实现一个简单的事务管理系统。
引言
在本示例中,我们将创建一个基于Spring框架的应用程序,该程序包含一个账户服务接口(IAccountService
),以及其实现类(AccountServiceImp
)。为了增强该服务的事务处理能力,我们将创建一个工厂类(ProxyBeanFactory
),它会为IAccountService
生成一个动态代理对象,该对象能够在调用真实服务方法前后自动开启和提交事务。
XML配置
首先,我们需要配置Spring的bean定义。下面是一个简化版的Spring配置文件示例:
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 数据源配置略 -->
<!-- QueryRunner配置略 -->
<!-- 连接工具类配置略 -->
<!-- 事务工具类配置略 -->
<!-- 数据访问层配置略 -->
<!-- 业务逻辑层配置略 -->
<bean id="proxyService" class="org.example.service.IAccountService" factory-bean="factory" factory-method="createProxy"></bean>
<bean id="factory" class="org.example.factory.ProxyBeanFactory">
<property name="transactionUtil" ref="transactionUtil"></property>
<property name="toProxyService" ref="service"/>
</bean>
<!-- 控制器配置略 -->
</beans>
如上所示,ProxyBeanFactory
将创建一个实现了IAccountService
接口的代理对象,并且会在执行业务逻辑之前和之后自动管理事务。
动态代理工厂类
接下来,我们来看一下ProxyBeanFactory
类的具体实现:
java
package org.example.factory;
import org.example.service.IAccountService;
import org.example.util.TransactionUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyBeanFactory {
private IAccountService toProxyService;
private TransactionUtil transactionUtil;
public void setToProxyService(IAccountService toProxyService) {
this.toProxyService = toProxyService;
}
public void setTransactionUtil(TransactionUtil transactionUtil) {
this.transactionUtil = transactionUtil;
}
public IAccountService createProxy() {
return (IAccountService) Proxy.newProxyInstance(
toProxyService.getClass().getClassLoader(),
toProxyService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
transactionUtil.beginTx();
result = method.invoke(toProxyService, args);
transactionUtil.commitTx();
} catch (Exception e) {
transactionUtil.rollbackTx();
} finally {
transactionUtil.closeTx();
}
return result;
}
}
);
}
}
ProxyBeanFactory
类的核心在于createProxy
方法,它使用JDK动态代理机制来创建代理对象。当任何IAccountService
接口方法被调用时,都会触发InvocationHandler
中的invoke
方法,从而开启事务、执行业务逻辑并最终提交或回滚事务。
示例代码:
applicationContext.xml:
xml
<!-- 加载资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 注入数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${msg1}"/>
<property name="jdbcUrl" value="${msg2}"/>
<property name="user" value="${msg3}"/>
<property name="password" value="${msg4}"/>
</bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!-- 连接工具类 -->
<bean id="connectionUtil" class="org.example.util.ConnectionUtil">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务工具类 -->
<bean id="transactionUtil" class="org.example.util.TransactionUtil">
<property name="connectionUtil" ref="connectionUtil"/>
</bean>
<bean id="mapperImp" class="org.example.dao.AccountMapperImp">
<property name="queryRunner" ref="queryRunner"></property>
<property name="connectionUtil" ref="connectionUtil"></property>
</bean>
<bean id="service" class="org.example.service.AccountServiceImp">
<property name="dao" ref="mapperImp"></property>
</bean>
<bean id="proxyService" class="org.example.service.IAccountService" factory-bean="factory" factory-method="createProxy"></bean>
<bean id="factory" class="org.example.factory.ProxyBeanFactory">
<property name="transactionUtil" ref="transactionUtil"></property>
<property name="toProxyService" ref="service"/>
</bean>
<bean id="controller" class="org.example.controller.AccountControllerImp">
<property name="service" ref="proxyService"></property>
</bean>
</beans>
ProxyBeanFactory:
java
public class ProxyBeanFactory {
IAccountService toProxyService;;
//装配事务工具类
TransactionUtil transactionUtil;
public void setAccountServiceImp(IAccountService accountServiceImp) {
toProxyService = accountServiceImp;
}
public void setToProxyService(IAccountService toProxyService) {
this.toProxyService = toProxyService;
}
public void setTransactionUtil(TransactionUtil transactionUtil) {
this.transactionUtil = transactionUtil;
}
public IAccountService getAccountServiceImp() {
return toProxyService;
}
//2.创建代理
public IAccountService createProxy(){
IAccountService proxy = (IAccountService) Proxy.newProxyInstance(toProxyService.getClass().getClassLoader(), toProxyService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
transactionUtil.beginTx();
obj = method.invoke(toProxyService, args);
transactionUtil.commitTx();
} catch (Exception e) {
e.printStackTrace();
transactionUtil.rollbackTx();
} finally {
transactionUtil.closeTx();
}
return obj;
}
});
return proxy;
}
}