Spring's declarative transactions

Reprinted from http://blog.csdn.net/u011726984

Original source:  https://blog.csdn.net/u011726984/article/details/45421151

Declarative Transaction Management

The reason most Spring users choose declarative transaction management is that it is the option that has the least impact on application code, and therefore best fits the idea of ​​a non-intrusive  lightweight container. 

  1. Spring declarative transaction management can be used in any environment. It can work with JDBC, JDO, Hibernate or other transaction mechanisms just by changing the configuration file.
  2. Spring's declarative transaction management can be applied to any class (and instances of that class).
  3. Spring provides declarative rollback rules.
  4. Spring allows you to customize transaction behavior through AOP. (For example, you can insert custom behavior in transaction rollbacks if needed. You can also add arbitrary notifications, just like transaction notifications.).
  5. Spring does not provide transaction context propagation across remote calls that high-end application servers provide. If you need these features, we recommend that you use EJB. However, do not use these features lightly. Because usually we don't want transactions to span remote calls.

Understand the implementation of Spring's declarative transaction management

Spring's transaction management is achieved through AOP proxies. Transaction notifications in it are driven by metadata (currently based on XML or annotations). 
The proxy object is combined with transaction metadata to produce an AOP proxy that uses a PlatformTransactionManager  interface with transaction interceptors to implement transactions before and after method invocations.

Conceptually, the working process of invoking a method on a transaction proxy looks like this:

example

1. First define the transactional service interface:

public interface UserService {

    public abstract void addUser(User user);

    public abstract void deleteUser();

    public abstract void updateUser();

    public abstract List queryUser();

}

2. The implementation class of the above service:

public class UserServiceImpl implements UserService {

    private UserDaoImpl userDao;

    @Override
    public void addUser(User user) {
        this.userDao.addUser(user);         
    }

    @Override
    public void deleteUser() {
        this.userDao.deleteUser();          
    }

    @Override
    public void updateUser() {
        this.userDao.updateUser();          
    }

    @Override
    public List queryUser() {
        this.userDao.queryUser();           
    }
}

现在假定,UserService的方法(queryUser())必须执行在只读事务上下文中,其他的方法(addUser(User user)、deleteUser()和updateUser())必须执行在可读写事务上下文中。

下面开始配置Spring的配置文件:

<?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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 声明式事务管理 -->
<!-- 1,引入命名空间:
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 

    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation=http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    -->

<!-- 2,配置数据源DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/hb_dfdc"/>
    <property name="user" value="root"/>
    <property name="password" value="root"></property>
</bean>

<!-- 3,配置JdbcTemplate,如果不用spring的jdbc可以省略 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 4, 配置DAO层-->
<bean id="userDao" class="com.dfdc.spring.declaratx.dao.impl.UserDaoImpl">
    <property name="jdbcTemplate" ref="jdbcTemplate"/><!--如果不用spring的jdbc可以省略-->
</bean>
<!-- 4.1 配置Service层,即要被事务管理的服务对象 -->
<bean id="userService" class="com.dfdc.spring.declaratx.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>

<!-- 5,配置Spring的事务管理器,即PlatformTransactionManager bean-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 6, 配置通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 所有以query开头的方法是只读的 -->
        <tx:method name="query*" read-only="true"/>
        <!-- 其他方法使用默认的事务设置 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!-- 7, 启用以上的事务通知-->
<aop:config>
    <!-- 运行被定义在UserServiceImpl类下的任意方法 -->
    <aop:pointcut expression="execution(* com.dfdc.spring.declaratx.service.impl.UserServiceImpl.*(..))" id="aopCut"/>
    <!-- 将切入点与通知编织在一起 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="aopCut"/>
</aop:config>

<!-- 其他bean -->


</beans>

我们来分析一下上面的配置。我们要把一个服务对象(’userService’ bean)做成事务性的。

我们想施加的事务语义封装在<tx:advice/>定义中。 
<tx:advice/>“把所有以 ‘query’ 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。

其中的 ‘transaction-manager’ 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 ‘transactionManager’), 该bean将会真正管理事务。

提示

事实上,如果 PlatformTransactionManager bean的名字是 ‘transactionManager’ 的话,你的事务通知(<tx:advice/>)中的 ‘transaction-manager’ 属性可以忽略。否则你则需要像上例那样明确指定。

配置中最后一段是 <aop:config/> 的定义, 它确保由 ‘txAdvice’ bean定义的事务通知在应用中合适的点被执行。

首先我们定义了一个切面,它匹配 UserService 接口定义的所有操作, 我们把该切面叫做 ‘aopCut’。然后我们用一个通知器(advisor)把这个切面与 ‘txAdvice’ 绑定在一起, 表示当 ‘aopCut’ 执行时,’txAdvice’ 定义的通知逻辑将被执行。

<aop:pointcut/> 元素定义是AspectJ的切面表示法。

一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如:

<aop:config>
    <aop:pointcut expression="execution(* com.dfdc.spring.declaratx.service.impl.*.*(..))" id="aopCut"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="aopCut"/>
</aop:config>

Now, now that we've analyzed the entire configuration, you might be asking, "Okay, but what does all this configuration do?".

The above configuration will create a proxy object for the 'userService' bean, this proxy object is wired with transaction advice, so when its corresponding method is called, a transaction will be started, suspended, marked read-only, or whatever (according to the transaction semantics configured by this method). Let's take a look at the test code below to test the above configuration.

ApplicationContext context = new ClassPathXmlApplicationContext("com/dfdc/spring/declaratx/test/context-declaratx.xml");
UserService userService = (UserService) context.getBean("userService");
userService.deleteUser();

result:

write picture description here

You can see that the spring container creates a series of singleton beans, and the method deleteUser inputs the "output user" string.

Set breakpoint debugging:

You can see that userService is a proxy object generated by Spring's JDK dynamic proxy.

Summarize

Steps of Spring declarative transaction processing:

  1. Build the environment and introduce the tx and context namespaces;
  2. In the spring configuration file, first import dataSource;
  3. Test whether the dataSource is configured correctly; (can be omitted)
  4. Import the beans of the dao and service layers
  5. Test whether dao and service are configured correctly (can be omitted)
  6. Introduce transaction manager
  7. Configure notifications<tx:advice/>
  8. Enable transaction notifications <aop:config/>, weaving pointcuts and notifiers in
  9. Test the class of the service layer to see if it is a proxy object

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325568540&siteId=291194637