-
Spring Transaction 示例
1 Transaction sample
1.1 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 用来替换${jdbc.username} 的值 -->
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<!-- 创建数据源对象 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<!-- 引入jdbctemplate 对象,用来进行db操作 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 业务逻辑处理类 -->
<bean id="bookService" class="com.gientech.tx.xml.service.BookService">
<property name="bookDao" ref="bookDao"></property>
</bean>
<bean id="bookDao" class="com.gientech.tx.xml.dao.BookDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 当前myAdvice类应用到txPoint expression所指代的类/方法上 -->
<aop:config>
<!-- 匹配com.gientech.tx.xml类下面的所有类,所有方法。 -->
<aop:pointcut id="txPoint" expression="execution(* com.gientech.tx.xml.*.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>
<!-- -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="checkout" propagation="REQUIRED" />
<tx:method name="updateStock" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
</beans>
1.2 db配置文件
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/gientechtest?useSSL=false
jdbc.driverClassName=com.mysql.jdbc.Driver
1.3 service
package com.gientech.tx.xml.service;
import com.gientech.tx.xml.dao.BookDao;
public class BookService {
BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
public void checkout(String username,int id){
try {
bookDao.updateStock(id);
} catch (Exception e) {
e.printStackTrace();
}
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
// int price = bookDao.getPrice(id);
// bookDao.updateBalance(username,price);
}
}
1.4 dao
package com.gientech.tx.xml.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class BookDao {
JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
public void updateStock(int id){
String sql = "update book_stock set stock='stock-1' where id=?";
jdbcTemplate.update(sql,id);
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
}
}
1.5 启动文件
public class TxTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"d:\\code");
ApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.checkout("zhangsan",1);
System.out.println();
}
}
2 Transaction 过程解析
2.1 加载XML 定义信息。
在obtainFreshBeanFactory()方法中,加载xml配置文件的属性值到当前工厂中,在parseCustomElement(ele, null) 中,
ConfigBeanDefinitionParser.parseAdvisor方法解析Advisor标签时,创建类对象,名称spring会按照一定规则进行创建,如图所示
2.1.2 解析<tx: 标签
<tx开始的标签是在delegate.parseCustomElement(ele)方法中进行解析,在BeanDefinitionParserDelegate.parseCustomElement方法中,第一步根据命名空间获取NamespaceHandler,在spring-tx\src\main\resources\META-INF\spring.handlers文件中进行具体的配置,
xml配置文件加载完成之后,容器中的对象如图,
2.2 调用invokeBeanFactoryPostProcessors(beanFactory)
此时,加载的xml配置文件属性值并未填充为实际值,调用后,会填充为真实值。下图为替换之前的值
XML文件中变量值替换完成之后如图:
2.3 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory) 作用是注册BeanPostProcessor,此时会创建动态代理自动创建器AspectJAwareAdvisorAutoProxyCreator,在执行前,一级缓存中没有AspectJAwareAdvisorAutoProxyCreator对象,执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成。执行前如下图。
执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成,一级缓存截图如下。
2.4 初始化Advisor
创建DefaultBeanFactoryPointcutAdvisor对象,但未创建对应属性对象,内存截图如下:
advice-ref="myAdvice" 对应的是adviceBeanName,是RuntimeBeanNameReference 对象,名称创建完成,pointcut-ref="txPoint" 对应的是pointcut,对象创建完成截图如下:
在此例中,bookservice,bookdao也是需要被动态代理的。动态代理对象创建完成之后截图如下: