使用註解方式簡單模擬事務
<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- spring提供的jdbcTemplate模塊 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.12.RELEASE</version> </dependency> <!-- mysql連接驅動包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.44</version> <scope>runtime</scope> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.12.RELEASE</version> </dependency>
/** * description:聲明式事務配置類,其中@EnableTransactionManagement * 必定要開啓。 * @author 70KG */ @Configuration @ComponentScan("com.nmys.story.springCore.springaop.tx_sample") @EnableTransactionManagement // -- 開啓基於註解的事務管理 public class TxConfig { // -- 配置數據源 @Bean public DataSource dataSource() throws Exception { ComboPooledDataSource pool = new ComboPooledDataSource(); pool.setUser("root"); pool.setPassword("root"); pool.setDriverClass("com.mysql.jdbc.Driver"); pool.setJdbcUrl("jdbc:mysql://localhost:3306/usthe?useSSL=false"); return pool; } // -- 加入模板 @Bean public JdbcTemplate jdbcTemplate() throws Exception { JdbcTemplate template = new JdbcTemplate(dataSource()); return template; } // -- 配置事務管理器,它纔是用來提交回滾事務的主導者 @Bean public DataSourceTransactionManager txManager() throws Exception { DataSourceTransactionManager tx = new DataSourceTransactionManager(dataSource()); return tx; } }
/** * description * @author 70KG */ @Service public class TxService { @Autowired private TxDao txDao; public void insertLog(){ txDao.insertSth(); } }
<!--more-->java
/** * description * @author 70KG */ @Repository public class TxDao { @Autowired private JdbcTemplate jdbcTemplate; // @Transactional僅代表它是一個事務方法,開啓事務僅有註解是不夠的,還須要配置事務管理器 @Transactional public void insertSth() { String sql = "INSERT into sys_log (username) VALUES(?);"; jdbcTemplate.update(sql, "lisi"); System.out.println("------>插入成功"); int i = 10/0; } }
/** * description * @author 70KG */ public class Test01 { public static void main(String[] args) { AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(TxConfig.class); TxService bean = app.getBean(TxService.class); bean.insertLog(); } }
測試結果確定是能正常運行的,下面試着跟一下源碼。mysql
源碼以下,AdviceMode在註解@EnableTransactionManagement默認就是PROXY,可見它向容器中注入了兩個類,分別是AutoProxyRegistrar和ProxyTransactionManagementConfiguration。面試
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); if (candidate == null) { continue; } Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) { // -- 前面的代碼主要是獲取註解類型,註解信息等等。 // -- 主要是這個地方,若是必要的話,就向容器中註冊一個自動代理建立器。 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } ...........
@Nullable private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { // -- 斷言 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 先判斷有沒有org.springframework.aop.config.internalAutoProxyCreator // 首次進來,確定沒有 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } // -- 將cls也就是InfrastructureAdvisorAutoProxyCreator包裝成RootBeanDefinition RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // -- 將beanDefinition註冊到IOC容器中,Bean的 // -- 名字就叫org.springframework.aop.config.internalAutoProxyCreator registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
給容器中注入InfrastructureAdvisorAutoProxyCreator的主要做用就是,利用後置處理器機制在對象建立之後,對對象進行包裝,返回一個代理對象(加強器),代理對象執行方法,利用攔截器鏈進行調用。spring
// -- 向容器中注入名字爲TRANSACTION_ADVISOR_BEAN_NAME的切面 @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); // -- 向切面中注入註解解析器,專門來解析事務註解的 advisor.setTransactionAttributeSource(transactionAttributeSource()); // -- 向切面中注入事務的攔截器,專門來攔截方法,包括事務的提交以及回滾操做 advisor.setAdvice(transactionInterceptor()); if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; }
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = new LinkedHashSet<>(2); // -- Spring註解的解析器 this.annotationParsers.add(new SpringTransactionAnnotationParser()); if (jta12Present) { // -- jta的解析器 this.annotationParsers.add(new JtaTransactionAnnotationParser()); } if (ejb3Present) { // -- ejb的解析器 this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } }
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); rbta.setTimeout(attributes.getNumber("timeout").intValue()); rbta.setReadOnly(attributes.getBoolean("readOnly")); rbta.setQualifier(attributes.getString("value")); ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>(); Class<?>[] rbf = attributes.getClassArray("rollbackFor"); for (Class<?> rbRule : rbf) {
會發現@Transactional中的各類屬性都在這裏,這樣,註解解析器就分析完了sql
@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; }
TransactionInterceptor 是一個實現了MethodInterceptor接口的類,標誌着TransactionInterceptor是一個方法攔截器,進入它的invoke()方法app
@Override @Nullable public Object invoke(final MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
進入invokeWithinTransaction()ide
@Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. TransactionAttributeSource tas = getTransactionAttributeSource(); // -- 拿到事務註解信息包括事務的qualifier和rollback信息 final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); // -- 獲取事務管理器 final PlatformTransactionManager tm = determineTransactionManager(txAttr); // -- 事務鏈接點的定義信息 final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 建立並開啓事務 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; 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) { // target invocation exception // -- 出現異常了,獲取事務管理器,進行事務的回滾 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } // -- 未出現異常,也獲取事務管理器則進行事務的提交 commitTransactionAfterReturning(txInfo); return retVal; } .........
Spring事務源碼梳理