zoukankan      html  css  js  c++  java
  • Spring注解开发(五)声明式事务

    先看一个简单的例子:

    新建Dao类,代码如下:

    @Repository
    public class PersonDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void insert() {
            String sql = "insert into tb_person(personName,age) values (?,?)";
            String personName = UUID.randomUUID().toString().substring(0, 8);
            jdbcTemplate.update(sql, personName, 20);
        }
    }

    新建Service类,代码如下:

    @Service
    public class PersonService {
        @Autowired
        private PersonDao personDao;
    
        @Transactional
        public void insert() {
            personDao.insert();
            System.out.println("插入完成");
            int a = 10 / 0;
            System.out.println(a);
        }
    }

    新建配置类,代码如下:

    @Configuration
    @EnableTransactionManagement
    @ComponentScan("com.practice.bean")
    public class MainConfig {
        @Bean
        public DataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/test");
            return dataSource;
        }
    
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            return jdbcTemplate;
        }
    
        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dataSource());
        }
    }

    执行测试类,代码如下:

       @Test
        public void test01() {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            PersonService bean = applicationContext.getBean(PersonService.class);
            bean.insert();
        }

    结果为:

    插入完成
    java.lang.ArithmeticException: / by zero

    但是数据库中并没有插入数据,

    去除service类insert()方法上的@Transactional注解,再次执行测试类,结果如下:

    插入完成
    java.lang.ArithmeticException: / by zero

    依旧会报错,但是数据库中插入了一条,说明没有受到事务控制

    Spring的声明式事务的处理方式和AOP类似,我们依旧从@EnableTransactionManagement注解入手。

    查看源码可知:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(TransactionManagementConfigurationSelector.class)
    public @interface EnableTransactionManagement 

    该注解为我们导入了TransactionManagementConfigurationSelector组件。

    源码中的方法为:

    	@Override
    	protected String[] selectImports(AdviceMode adviceMode) {
    		switch (adviceMode) {
    			case PROXY:
    				return new String[] {AutoProxyRegistrar.class.getName(),
    						ProxyTransactionManagementConfiguration.class.getName()};
    			case ASPECTJ:
    				return new String[] {determineTransactionAspectClass()};
    			default:
    				return null;
    		}
    	}

    adviceMode在注解中的默认值为AdviceMode.PROXY:

    AdviceMode mode() default AdviceMode.PROXY;

    根据上面的方法可以看到为容器导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration:

    1.AutoProxyRegistrar做了什么?

    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
    ---》
    @Nullable
    public static BeanDefinition registerAutoProxyCreatorIfNecessary(
    		BeanDefinitionRegistry registry, @Nullable Object source) {
    
    	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
    }

    给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件,可以看到该组件实现了:SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware这两个接口,和AOP的一致。所以InfrastructureAdvisorAutoProxyCreator:

    • 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器)。

    在创建Bean的时候调用的还是AbstractAutoProxyCreator的wrapIfNecessary方法(AOP)

    先调用了下面的方法去查找当前类使用的拦截器:

    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

    根据org.springframework.transaction.config.internalTransactionAdvisor找到了BeanFactoryTransactionAttributeSourceAdvisor:

    advisors.add(this.beanFactory.getBean(name, Advisor.class));

    包装成MethodInterceptor并返回。

    执行下面的方法:

    bject proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

    最后包装成一个代理对象并返回。

    • 注入了BeanFactory;

    2.ProxyTransactionManagementConfiguration做了什么?

    根据源码上的@Configuration注解,我们可以看出这是一个配置类:

    @Configuration
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration 

    2.1、给容器中注册事务增强器;

    注册了BeanFactoryTransactionAttributeSourceAdvisor,并将AnnotationTransactionAttributeSource和transactionInterceptor赋值给

    2.2.注册了事务属性解析器

    注册了AnnotationTransactionAttributeSource,解析SPring提供的事务注解(也支持JTA1.2和EJB3的相关注解(如果存在的话)):

    @Bean
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public TransactionAttributeSource transactionAttributeSource() {
    		return new AnnotationTransactionAttributeSource();
    	}

    上述简单示例中我们根据Debug可以看出,最终是进入了注解解析器的如下方法,读取到了@Transactional的所有属性信息

    this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    ----》
    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"));
    
    		List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
    		for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
    			rollbackRules.add(new RollbackRuleAttribute(rbRule));
    		}
    		for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
    			rollbackRules.add(new RollbackRuleAttribute(rbRule));
    		}
    		for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
    			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    		}
    		for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
    			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    		}
    		rbta.setRollbackRules(rollbackRules);
    
    		return rbta;
    	}

    2.3、注册了事务拦截器:

    注册了TransactionInterceptor,实际上就是一个MethodInterceptor(和AOP的拦截器链里的一样):

    @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,在目标方法执行的时候执行拦截器链;

    invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);

    先获取事务相关的属性,再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
     最终会从容器中按照类型获取一个PlatformTransactionManager,在获取目标类的joinpointIdentification,上实例此处为com.practice.bean.PersonService.insert方法,在创建事务信息。

    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

    返回的txInfo如下图:

    执行目标方法:

    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;

     如果异常,获取到事务管理器,利用事务管理回滚操作:

    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());

    如果正常,利用事务管理器,提交事务

    commitTransactionAfterReturning(txInfo);
  • 相关阅读:
    hbase 自定义过滤器
    idea的protobuf使用
    Docker自动补全容器名
    Docker普通用户不使用sudo提权
    Hadoop安装错误总结
    Git中撤销提交
    Python经典算法片段
    Git设置彩色输出
    Git同步远程fork的项目
    Git错误汇总
  • 原文地址:https://www.cnblogs.com/demo-alen/p/13547229.html
Copyright © 2011-2022 走看看