SpringBoot annotates the way transactions are declared

  • 2020-09-28 08:54:20
  • OfStack

springboot may be faster for newcomers than springmvc, but for those of you moving from springmvc to springboot, there are some things you need to get used to, especially the xml configuration. I personally prefer annotations ➕ xml is easy to look at, easy to find and clear. However, xml can be replaced with annotations, so today I will use annotations for transactions in springboot.

springboot transaction is mainly divided into two categories, one is xml declarative transaction, 2 is annotations affairs, also can achieve similar declarative transaction annotations affairs, about annotations declarative transaction, the search is less than the appropriate information on the Internet, so here, I will find and summarizes a few methods of write here, everyone to discuss

springboot xml services

You can use @ImportResource("classpath:transaction.xml") The configuration of the xml is introduced and the configuration of the xml is as follows


<?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: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/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
  <bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" ></property>
  </bean>
  <tx:advice id="cftxAdvice" transaction-manager="txManager">
    <tx:attributes>
      <tx:method name="query*" propagation="SUPPORTS" read-only="true" ></tx:method>
      <tx:method name="get*" propagation="SUPPORTS" read-only="true" ></tx:method>
      <tx:method name="select*" propagation="SUPPORTS" read-only="true" ></tx:method>
      <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" ></tx:method>
    </tx:attributes>
  </tx:advice>
   <aop:config>
    <aop:pointcut id="allManagerMethod" expression="execution (* com.exmaple.fm..service.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod" order="0" />
  </aop:config>
</beans>

The springboot startup class is as follows:


package com.example.fm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@ImportResource("classpath:transaction.xml")
@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Once started, transactions can be started, but the xml configuration is imported into the project, and if you do not want to import the xml configuration, you can use annotations.

springboot annotations transaction

Before explaining transactions, you need to understand the classes in which spring creates agents. Within spring, the automatic creation of agents is done through BeanPostProcessor. The implementation of the BeanPostProcessor interface loads automatically only when ApplicationContext is initialized, whereas regular BeanFactory can only be called programmatically. According to the different matching rules, it can be roughly divided into three categories:

The name of a, matching Bean automatically creates the proxy matching to Bean, implementing class BeanNameAutoProxyCreator


<bean id="testInterceptor" class="com.example.service.config.testInerceptor " ></bean>
<bean id="profileAutoProxyCreator" class="org.springframework.aop.framework.
autoproxy.BeanNameAutoProxyProxyCreator">
<bean>
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<value> testInterceptor </value>
</property>
</bean>

b, according to AspectJ annotations in Bean automatically create agents, implementation of class AnnotationAwareAspectJAutoProxyCreator


<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="annotationAwareAspectJAutoProxyCreatorTest" class="com.example.service.AnnotationAwareAspectJAutoProxyCreatorTest"/>
<aop:config>
  <aop:aspect ref="annotationAwareAspectJAutoProxyCreatorTest">
    <aop:around method="process" pointcut="execution (* com.example.service.fm..*.*(..))"/>
  </aop:aspect>
</aop:config>

c. According to the matching mechanism of Advisor, the agent is automatically created, all Advisor in the container are scanned, and these slices are automatically applied to the matching Bean, and class DefaultAdvisorAutoProxyCreator is implemented

Let's move on to annotating how to open a transaction:

1. Transactional annotation transactions

It is necessary to add @Transactional annotation to the method of doing business management, or to add this annotation directly on the class in lazy words, so that all methods can do business management, but it still needs to add on the class that needs transaction management, the workload is relatively heavy, here just briefly, specific can be google or bing

2. Annotate declarative transactions

The difference between Component or Configuration and bean, I will write a special article to explain it when I have time

a. Method 1, where Component or Configuration transactions can be used


package com.exmple.service.fm9.config;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
 * Created by guozp on 2017/8/28.
 */
@Aspect
//@Component  Transaction still in effect 
@Configuration
public class TxAdviceInterceptor {
  private static final int TX_METHOD_TIMEOUT = 5;
  private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.alibaba.fm9..service.*.*(..))";
  @Autowired
  private PlatformTransactionManager transactionManager;
  @Bean
  public TransactionInterceptor txAdvice() {
    NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
     /* Read - only transactions do not update */
    RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
    readOnlyTx.setReadOnly(true);
    readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
    /* The current transaction is used if it exists, and created if it does not 1 A new transaction */
    RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
    requiredTx.setRollbackRules(
      Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
    requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    requiredTx.setTimeout(TX_METHOD_TIMEOUT);
    Map<String, TransactionAttribute> txMap = new HashMap<>();
    txMap.put("add*", requiredTx);
    txMap.put("save*", requiredTx);
    txMap.put("insert*", requiredTx);
    txMap.put("update*", requiredTx);
    txMap.put("delete*", requiredTx);
    txMap.put("get*", readOnlyTx);
    txMap.put("query*", readOnlyTx);
    source.setNameMap( txMap );
    TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
    return txAdvice;
  }
  @Bean
  public Advisor txAdviceAdvisor() {
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
    return new DefaultPointcutAdvisor(pointcut, txAdvice());
    //return new DefaultPointcutAdvisor(pointcut, txAdvice);
  }
}

b. Method 1, where Component or Configuration transactions can be used


package com.exmple.service.fm9.config;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
 * Created by guozp on 2017/8/29.
 */
//@Component  Transaction still in effect 
@Configuration
public class TxAnoConfig {
  /* Transaction interception type */
  @Bean("txSource")
  public TransactionAttributeSource transactionAttributeSource(){
    NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
     /* Read - only transactions do not update */
    RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
    readOnlyTx.setReadOnly(true);
    readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
    /* The current transaction is used if it exists, and created if it does not 1 A new transaction */
    //RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
    //requiredTx.setRollbackRules(
    //  Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
    //requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED,
      Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
    requiredTx.setTimeout(5);
    Map<String, TransactionAttribute> txMap = new HashMap<>();
    txMap.put("add*", requiredTx);
    txMap.put("save*", requiredTx);
    txMap.put("insert*", requiredTx);
    txMap.put("update*", requiredTx);
    txMap.put("delete*", requiredTx);
    txMap.put("get*", readOnlyTx);
    txMap.put("query*", readOnlyTx);
    source.setNameMap( txMap );
    return source;
  }
  /** Section intercept rule   Parameters are automatically injected from the container */
  @Bean
  public AspectJExpressionPointcutAdvisor pointcutAdvisor(TransactionInterceptor txInterceptor){
    AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
    pointcutAdvisor.setAdvice(txInterceptor);
    pointcutAdvisor.setExpression("execution (* com.alibaba.fm9..service.*.*(..))");
    return pointcutAdvisor;
  }
  /* Transaction interceptor */
  @Bean("txInterceptor")
  TransactionInterceptor getTransactionInterceptor(PlatformTransactionManager tx){
    return new TransactionInterceptor(tx , transactionAttributeSource()) ;
  }
} 

c. Method 1, where Component or Configuration transactions can be used


package com.exmple.service.fm9.config;
import java.util.Properties;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
 * Created by guozp on 2017/8/28.
 *
 */
//@Component
@Configuration
public class TxConfigBeanName {
  @Autowired
  private DataSourceTransactionManager transactionManager;
  //  Create transaction notifications 
  @Bean(name = "txAdvice")
  public TransactionInterceptor getAdvisor() throws Exception {
    Properties properties = new Properties();
    properties.setProperty("get*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    properties.setProperty("add*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    properties.setProperty("save*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    properties.setProperty("update*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    properties.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    TransactionInterceptor tsi = new TransactionInterceptor(transactionManager,properties);
    return tsi;
  }
  @Bean
  public BeanNameAutoProxyCreator txProxy() {
    BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
    creator.setInterceptorNames("txAdvice");
    creator.setBeanNames("*Service", "*ServiceImpl");
    creator.setProxyTargetClass(true);
    return creator;
  }
}

d. Method 1, not all transactions can take effect when using Component or Configuration, for example, if Configuration opens the comment section and does not move all the code to defaultPointcutAdvisor(), things will become invalid, the specific reason is not clear for the moment, if you have clear, you can point to me.


ackage com.alibaba.fm9.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
 * Created by guozp on 2017/8/28.
 *            ???????
 */
@Configuration // Transaction failure , Have moved to the 1 All methods do not fail 
//@Component //  Transactions are possible, not all of them are moved to 1 A method of 
public class TxOtherConfigDefaultBean {
  public static final String transactionExecution = "execution (* com.alibaba.fm9..service.*.*(..))";
  @Autowired
  private PlatformTransactionManager transactionManager;
  //@Bean
  //@ConditionalOnMissingBean
  //public PlatformTransactionManager transactionManager() {
  //  return new DataSourceTransactionManager(dataSource);
  //}
  @Bean
  public TransactionInterceptor transactionInterceptor() {
    Properties attributes = new Properties();
    attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
    //TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager(), attributes);
    TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);
    return txAdvice;
  }
  //@Bean
  //public AspectJExpressionPointcut aspectJExpressionPointcut(){
  //  AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  //  pointcut.setExpression(transactionExecution);
  //  return pointcut;
  //}
  @Bean
  public DefaultPointcutAdvisor defaultPointcutAdvisor(){
    //AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    //pointcut.setExpression(transactionExecution);
    //DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    //advisor.setPointcut(pointcut);
    //advisor.setAdvice(transactionInterceptor());
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression(transactionExecution);
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    advisor.setPointcut(pointcut);
    Properties attributes = new Properties();
    attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
    attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
    TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);
    advisor.setAdvice(txAdvice);
    return advisor;
  }
} 

In short, springboot USES the above annotations to turn things on in a few ways to achieve the same effect as xml claims, but says goodbye to xml, keeping your code away from configuration files.

conclusion


Related articles: