spring事务原理二

(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,那么就可以进行事务管理了


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值