Spring事務是基於Aop,具體而言是通過一個TransactionInterceptor的攔截器來實現。下面整理一下Spring實現事務操作的具體流程,以便於以后復習。
一.注解EnableTransactionManagement
使用EnableTransactionManagement注解可以開啟Spring事務,而EnableTransactionManagement源代碼如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; /** * 默認是使用代理的通知模式 */ AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
閱讀上述類的源代碼,我們需要注意兩點:
- 我們需要去了解TransactionManagementConfigurationSelector.class的功能
- Spring事務默認是使用Proxy代理模式
二. TransactionManagementConfigurationSelector
該類實現了ImportSelector接口,通過覆寫selectImports()方法,手動將java類注冊到Spring框架的IOC容器中
@Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: // 默認就是Proxy模式 return new String[] {AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } }
通過上述源代碼可知,Spring默認將AutoProxyRegistrar.class和ProxyTransactionManagementConfiguration.class注入到IOC容器。
接下來,應該是看看這兩個類到底干了什么事?
三. AutoProxyRegistrar
跟蹤源代碼可知,Spring會將InfrastructureAdvisorAutoProxyCreator.class注入到IOC容器,然后請看該類類圖:
由圖知InfrastructureAdvisorAutoProxyCreator.class是一個BeanPostProcessor.class的子類,理論上它可以在Spring初始化bean之前或之后進行功能增強,Sping框架原生選擇是之后增強,具體的增強方法是在AbstractAutoProxyCreator#postProcessAfterInitialization()方法。
接下來請看看postProcessAfterInitialization()方法源碼:
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
/** Wrap the given bean if necessary * 對bean進行包裝,其實就是創建代理對象 */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 省略。。。。 // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 創建代理,jdk動態代理或者cglib代理 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } }
上述代碼的作用就是,Spring在bean初始化之后,會判斷該bean是否需要創建一個代理對象,如果需要,就會使用jdk動態代理或者cglib創建一個代理類(這一塊不是本篇文章分析的重點)。
到此,AutoProxyRegistrar.class類的功能就分析完畢。
四. ProxyTransactionManagementConfiguration
該類主要是在IOC容器中注入事務攔截器TransactionInterceptor.class和Spring的事務注冊解析器,具體源碼如下:
@Configuration public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { /** * 事務注冊解析器 */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } /** * 事務攔截器 */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
繼續跟蹤源碼,咱們會發現AnnotationTransactionAttributeSource.class的底層調用了SpringTransactionAnnotationParser.class,由類名就可知道這個類的作用。事實上它也確實是解析@Transactional,具體可以參看該類源碼,很簡單的。
查看類圖可知TransactionInterceptor.class是Advice的一個實現,而且是MethodInterceptor接口的一個實現,這說明它跟Spring Aop中前置、后置通知一樣,事實上它就是一個環繞通知(around advice),完全遵守底層的責任鏈調用模式。
五. 事務調用鏈
為了便於理解,先創建一個測試demo
- DAO層
@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void addUser() { jdbcTemplate.update("INSERT t_user(userName,age) VALUES ('test',100);"); } }
- Service層
public interface UserService { public void addUser(); }
@Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @Transactional public void addUser() { userDao.addUser(); // int i = 1 / 0; } }
- 配置類
@Configuration @ComponentScan("qinfeng.zheng.spring01.v6") @EnableTransactionManagement // 開啟事務 public class SpringConfig { @Bean public DataSource dataSource() { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("xxxxxxx"); dataSource.setURL("jdbc:mysql://xxx.xxx.xx.xx:3306/tb_user?useUnicode=true&characterEncoding=UTF-8"); // dataSource.setDatabaseName("tb_user"); return dataSource; } @Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } @Bean public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dataSource()); } }
- 啟動類
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService orderService = context.getBean(UserService.class); orderService.addUser(); } }
直接在啟動類上的orderService.addUser();這一行代碼上戳個斷點,然后debug走起來。。。
由上圖知orderService其實是一個JdkDynamicAopProxy的代理對象,然后咱們F7,會進入到JdkDynamicAopProxy#invoke()方法。
@Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // .... // 獲取當前方法的攔截器鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { // 如果攔截鏈為空 //..... } else { // 創建一個MethodInvocation對象,其實就是對proxy, target, method, args, targetClass, chain這些參數的封裝 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 調用攔截器鏈 retVal = invocation.proceed(); } //...... return retVal; } //..... }
通過debug可知,chain鏈中有且只有一個 元素,而且這個元素就是前面注冊的TransactionInterceptor.class, 還記得這個類嗎?Spring的事務全靠它呢!
繼續debug....
通過debug很容易發現invocation對象的真實類型是ReflectiveMethodInvocation.class, 這個類在分析Spring Aop的調用鏈時超級重要。因為本篇主要講Spring事務,所以咱們直接進入該類的proceed()方法。
@Override @Nullable public Object proceed() throws Throwable { // 調用真實業務方法,底層也是通過反射 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = his.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { }else { // this很重要,這里會遞歸調用proceed() return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
因為interceptorOrInterceptionAdvice的真實類型是TransactionInterceptor.class,所以debug進入TransactionInterceptor#invoke()。
public Object invoke(MethodInvocation invocation) throws Throwable { //真實目標類 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); //重點分析該方法 return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
@Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable { if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 開啟事務,底層會啟用jdbc開啟事務, TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); // 目標方法返回值 Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // 這是一個環繞通知: 即將調用攔截鏈中的下一個攔截器 // This will normally result in a target object being invoked. // 調用目標方法,返回正常結果` retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 如果目標方法拋出異常,這里會回滾事務 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { // 清埋事務 cleanupTransactionInfo(txInfo); } // commit事務 commitTransactionAfterReturning(txInfo); return retVal; }else{ //..... } }
當程序運行到invocation.proceedWithInvocation();這句代碼時,注釋寫得很明白,它會去執行鏈中的下一個攔截器,所以程序又會回到ReflectiveMethodInvocation#proceed()方法。 我們debug跟蹤一下。
通過debug很清楚看到了程序的調用步驟。
程序本次進入ReflectiveMethodInvocation#proceed()方法后,會進入if條件,然后執行invokeJoinpoint()方法,該方法的底層就是使用反射機制調用自定義的業務方法,於本例而言就是調用addUser()方法。
調用addUser()方法, 獲取方法返回值 ,然后程序又會回到invokeWithinTransaction()方法。
六. 結束
Spring的事務的大體流程就說到這里了,示例結合debug應該還是很清楚的,至於其中的許多細節,還有待研究,后面繼續補充