您的位置:首页 > 编程语言 > Java开发

spring事务原理二

2015-11-30 22:36 399 查看
(1)对业务方法aop横切

如果我们从原来硬编码的事务管理系统中,将事务管理相关代码剥离出来,我们会怎么做?最直观的方法就是提供一个工具类,将事务管理逻辑几种到这个类中,而service实现类必须在执行前后调用工具类即可。



虽然这种方法可以让事务管理代码和业务代码分离,但是也是有很大缺陷的。实际上最好的方法就是使用aop,在执行前后进行事务管理操作。现在用拦截器实现,代码如下:
public class PrototypeTransactionInterceptor implements MethodInterceptor
{
private PlatformTransactionManager transactionManager;

public Object invoke(MethodInvocation invocation) throws Throwable
{
Method method = invocation.getMethod();
TransactionDefinition definition = getTransactionDefinitionByMethod(method);
TransactionStatus txStatus = transactionManager.getTransaction(definition);//开启事务

Object result = null;
try
{
result = invocation.proceed();
}
catch (Throwable t)
{
if (needRollbackOn(t))
{
transactionManager.rollback(txStatus);
}
else
{
transactionManager.commit(txStatus);
}
throw t;
}
transactionManager.commit(txStatus);
return result;
}

private boolean needRollbackOn(Throwable t)
{
// TODO ...
return false;
}

private TransactionDefinition getTransactionDefinitionByMethod(
Method method)
{
// TODO ...
return null;
}

public PlatformTransactionManager getTransactionManager()
{
return transactionManager;
}

public void setTransactionManager(PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
}


(2)保证spring和DAO框架使用的是同一个Connection

1.为何必须保证在同一个连接中?

类比jdbc操作,都是先setAutoCommit(false),然后再进行CURD操作,最后commit

只有这样才能保证能够对事务中的操作进行回滚和提交

所以,必须要保证spring进行的事务操作和DAO框架使用的Connection是同一个

2.如何保证在同一个连接中?

1)先看一下spring中定义

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>

<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">  <!-- name 为configLocation或s 不可为其他 -->
<value>sqlMap.xml</value> <!-- 不区分大小写,路径前可加'/' -->
</property>
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>


可以看出spring的TransactionManager和ibatis使用的是同一个dataSource

2)spring可以偷偷将这个dataSource替换为动态代理类

有了这个动态代理类,spring就可以在getConnection方法前后进行一系列操作了

public class DatasourceHandler implements InvocationHandler {

private DataSource dataSource;
/**
* @param dataSource
*/
public DatasourceHandler(DataSource dataSource) {
super();
this.dataSource = dataSource;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("getConnection")){

if(ResourceHolder.getResource(proxy)==null){
Connection connection =	(Connection) method.invoke(this.dataSource, args);
ResourceHolder.addResource(proxy, connection);
}
return ResourceHolder.getResource(proxy);
}else{
return method.invoke(this.dataSource, args);
}
}
}
可以看出:先判断是否是getConnection方法,如果是,就判断ResourceHolder的resource中是否存在连接,如果不存在,就通过原来的DataSource(也就是spring中直接定义的那个)获取数据库连接,然后存放在ResourceHolder的resource中

那么再来看下ResourceHolder

public class ResourceHolder {

private static ThreadLocal<Map<Object,Object>> resources= new ThreadLocal<Map<Object,Object>>();

public static void addResource(Object key,Object value){
if(resources.get()==null){
resources.set(new HashMap<Object,Object>());
}
resources.get().put(key, value);
}

public static Object getResource(Object key){

return resources.get().get(key);
}

public static void clear(){
resources.remove();
}
}
可以看出resources实际上是ThreadLocal类型的,也就保证了一个线程中的connection肯定是同一个,这就保证了spring和DAO框架使用的是同一个connection,那么就可以进行事务管理了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: