Spring 事務(wù)介紹(二)之 事務(wù)的傳播機(jī)制

spring.png

Spring 事物介紹(二)之 事物的傳播機(jī)制

Spring中對事物的支持

Spring 事物相關(guān)API:

spring事物是在數(shù)據(jù)庫事物的基礎(chǔ)上進(jìn)行封裝擴(kuò)展嘹害,其主要特性如下:

  • 支持原有的數(shù)據(jù)事物的隔離級別
  • 加入了事物傳播的概念慎玖,提供多個事物的合并和隔離的功能
  • 提供聲明式事物咽弦,讓業(yè)務(wù)代碼與事物分離赡译,事物更易用

spring提供了三個接口用來使用事物:

  • TransactionDefinition :事物定義
  • PlatformTransactionManager :事物的管理
  • TransactionStatus : 事物運(yùn)行狀態(tài)

TransactionDefinition.java

package org.springframework.transaction;

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    int getPropagationBehavior();

    int getIsolationLevel();

    int getTimeout();

    boolean isReadOnly();

    String getName();
}

PlatformTransactionManager.java

package org.springframework.transaction;

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionStatus.java

package org.springframework.transaction;

import java.io.Flushable;

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

API應(yīng)用demo:

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo.spring</groupId>
    <artifactId>spring-tx-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.8.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.0.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

SpringTransactionTest.java

package com.demo.spring;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/13 20:51
 */
public class SpringTransactionTest {

    private static String jdbcUrl = "jdbc:mysql://192.168.5.104:3306/spring";
    private static String userName = "root";
    private static String password = "root";

    public static Connection openConnection() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);
        return conn;
    }

    public static void main(String[] args) {
        final DataSource dataSource = new DriverManagerDataSource(jdbcUrl, userName, password);
        TransactionTemplate template = new TransactionTemplate();
        template.setTransactionManager(new DataSourceTransactionManager(dataSource));

        template.execute(new TransactionCallback<Object>() {
            public Object doInTransaction(TransactionStatus transactionStatus) {
                Connection connection = DataSourceUtils.getConnection(dataSource);
                Object savePoint = null;

                try {
                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                                "insert into account(accountName,user,money) VALUES (?,?,?)");
                        preparedStatement.setString(1,"111");
                        preparedStatement.setString(2,"a");
                        preparedStatement.setInt(3,100);
                        preparedStatement.executeUpdate();
                    }
                    savePoint = transactionStatus.createSavepoint();

                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                                "insert into account(accountName,user,money) VALUES (?,?,?)");
                        preparedStatement.setString(1,"222");
                        preparedStatement.setString(2,"b");
                        preparedStatement.setInt(3,100);
                        preparedStatement.executeUpdate();
                    }
                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                            "update account set money= money+1 where user=?");
                        preparedStatement.setString(1,"333");
                        //手動設(shè)置異常
                        int i = 1/0;
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    System.out.println("update failed");
                    if (savePoint != null) {
                        transactionStatus.rollbackToSavepoint(savePoint);
                    } else {
                        transactionStatus.setRollbackOnly();
                    }
                }
                return null;
            }
        });
    }
}

執(zhí)行結(jié)果:

update failed

但是savePoint之前的插入是成功的狸相。

這是因?yàn)?/p>

 public Object doInTransaction(TransactionStatus transactionStatus) {
            Connection connection = DataSourceUtils.getConnection(dataSource);
            ...
}            

TransactionStatus transactionStatus中的Connection與Connection與DataSourceUtils.getConnection(dataSource)返回的是同一個Connection矮男。

相關(guān)源碼:

org.springframework.transaction.support.TransactionSynchronizationManager#bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Assert.notNull(value, "Value must not be null");
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        map = new HashMap();
        resources.set(map);
    }
    
    Object oldValue = ((Map)map).put(actualKey, value);
    if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) {
        oldValue = null;
    }

    if (oldValue != null) {
        throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
    } else {
        if (logger.isTraceEnabled()) {
            logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
        }

    }
}

其中((Map)map).put(actualKey, value)中的

actualKey

org.springframework.jdbc.datasource.DriverManagerDataSource

value

org.springframework.jdbc.datasource.ConnectionHolder

這個map已經(jīng)放入到resources中了槐秧,resources是一個ThreadLocal,取的時(shí)候也去這里面取

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");

get時(shí)的相關(guān)源碼

org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource

private static Object doGetResource(Object actualKey) {
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        return null;
    } else {
        Object value = map.get(actualKey);
        if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
            map.remove(actualKey);
            if (map.isEmpty()) {
                resources.remove();
            }

            value = null;
        }

        return value;
    }
}

所以

if (savePoint != null) {
    transactionStatus.rollbackToSavepoint(savePoint);
} else {
    transactionStatus.setRollbackOnly();
}

在執(zhí)行回滾時(shí)是同一個Connection.rollback()掖蛤。

聲明式事物

如果僅僅只是提供API杀捻,在進(jìn)行數(shù)據(jù)庫操作時(shí),仍很麻煩蚓庭,所以Spring還提出了聲明式事物致讥,@Transactional注解仅仆。

聲明式事物demo。

pom.xml 同上

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo.spring</groupId>
    <artifactId>spring-tx-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.8.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.0.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

spring-tx.xml

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

    <context:component-scan base-package="com.demo.spring.*"/>

    <aop:aspectj-autoproxy expose-proxy="true"/>

    <bean class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <constructor-arg name="url" value="jdbc:mysql://192.168.5.104/spring"/>
        <constructor-arg name="username" value="root"/>
        <constructor-arg name="password" value="root"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

Account.java

package com.demo.spring.service;

import java.io.Serializable;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 22:00
 */
public class Account implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String accountName;
    private String user;
    private String money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", accountName='" + accountName + '\'' +
                ", user='" + user + '\'' +
                ", money='" + money + '\'' +
                '}';
    }
}

AccountService.java

package com.demo.spring.service;

import java.util.List;
import java.util.Map;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:54
 */
public interface AccountService {
    void addAccount(String name, int money);

    int updateAccount(String name, int money);

    List<Map<String, Object>> queryAccount(String name);

}

AccountServiceImpl.java

package com.demo.spring.service;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:58
 */
@Service
public class AccountServiceImpl implements AccountService{
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addAccount(String name, int money) {
        String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
        jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
    }

    @Transactional
    public List<Map<String, Object>> queryAccount(String name) {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from account where user = ?", name);
        return list;
    }

    @Transactional
    public int updateAccount(String name, int money) {
        return jdbcTemplate.update("update account set money = money + ? where user = ?", money, name);
    }
}

AccountServiceTest.java

package com.demo.spring;

import com.demo.spring.service.AccountService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/14 22:15
 */
public class AccountServiceTest {

    @Test
    public void add() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        accountService.addAccount("ayang",100);
    }

    @Test
    public void update() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        accountService.updateAccount("ayang",110);
    }

    @Test
    public void select() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        List<Map<String, Object>> list = accountService.queryAccount("ayang");
        System.out.println(Arrays.toString(list.toArray()));
    }
}

Spring事物默認(rèn)的傳播機(jī)制:

@Transactional(propagation = Propagation.REQUIRES_NEW)

下面介紹一下spring事物的傳播機(jī)制垢袱。

Spring事物傳播機(jī)制
類別 事物傳播類型 說明
支持當(dāng)前事物 PROPAGATION_REQUIRED(必須的) 如果當(dāng)前沒有事物墓拜,就新建一個事物,如果已經(jīng)存在一個事物中请契,加入到這個事物中咳榜。這是最常見的選擇。
支持當(dāng)前事物 PROPAGATION_SUPPORTS(支持) 支持當(dāng)前事物姚糊,如果當(dāng)前沒有事物,就以非事物方式執(zhí)行授舟。
支持當(dāng)前事物 PROPAGATION_MANDATORY(強(qiáng)制) 使用當(dāng)前的事物救恨,如果當(dāng)前沒有事物,就拋出異常释树。
不支持當(dāng)前事物 PROPAGATION_REQUIRES_NEW(隔離) 新建事物肠槽,如果當(dāng)前存在事物,把當(dāng)前事物掛起奢啥。
不支持當(dāng)前事物 PROPAGATION_NOT_SUPPORTED(不支持) 以非事物方式執(zhí)行操作秸仙,如果當(dāng)前存在事物,就把當(dāng)前事物掛起桩盲。
不支持當(dāng)前事物 PROPAGATION_NEVER(強(qiáng)制非事物) 以非事物方式執(zhí)行寂纪,如果當(dāng)前存在事物,則拋出異常赌结。
嵌套事物 PROPAGATION_NESTED(嵌套事物) 如果當(dāng)前存在事物捞蛋,則在嵌套事物內(nèi)執(zhí)行。如果當(dāng)前沒有事物柬姚,則執(zhí)行與PROPAGATION_REQUIRED類似的操作拟杉。
常用的事物傳播機(jī)制:
  • PROPAGATION_REQUIRED

如果當(dāng)前沒有事物,就新建一個事物量承,如果已經(jīng)存在一個事物中搬设,加入到這個事物中這個是默認(rèn)傳播機(jī)制。

  • PROPAGATION_NOT_SUPPORTED

以非事物方式執(zhí)行操作撕捍,如果當(dāng)前存在事物拿穴,就把當(dāng)前事物掛起∮欠纾可以用于發(fā)送提示信息贞言,站內(nèi)信,郵件提示燈阀蒂。不屬于并且不應(yīng)該影響主體業(yè)務(wù)邏輯该窗,即時(shí)發(fā)送失敗也不應(yīng)該對主題業(yè)務(wù)邏輯回滾弟蚀。

  • PROPAGATION_REQUIRED_NEW

新建一個事物,如果存在當(dāng)前事物酗失,則將事物掛起义钉。總是新啟一個事物规肴,這個傳播機(jī)制適用于不受父類方法事物影響的操作捶闸,比如某些業(yè)務(wù)場景下需要記錄業(yè)務(wù)日志,用于異步反查拖刃,那么不管主體業(yè)務(wù)邏輯是否完成删壮,日志都需要記錄下來,不能因?yàn)橹黧w業(yè)務(wù)邏輯報(bào)錯而丟失日志兑牡。

測試:
表結(jié)構(gòu) 服務(wù)類 功能描述
user userService 創(chuàng)建用戶央碟,并添加賬號
account accountService 添加賬號

相關(guān)代碼:

package com.demo.spring;

import com.demo.spring.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/14 22:15
 */
public class UserServiceTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        UserService userService = context.getBean(UserService.class);
        userService.addUser("ayang");
    }
}


@Transactional(propagation = Propagation.REQUIRED)
    public void addUser(String name) {
        jdbcTemplate.update("insert into user (name) values(?)", name);
        accountService.addAccount(name, 100);
    }


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addAccount(String name, int money) {
    String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
    jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
    int i = 1/0;
}

場景:

場景 createUser addAccount(Exception) 結(jié)果
1 無事物 required user成功,account失敗
2 required 無事物 user失敗均函,account失敗
3 required not_supported user失敗亿虽,account成功
4 required required_new user失敗,account失敗
5 required(異常移至create末尾) required_new user失敗苞也,account成功
6 required(異常移至create末尾)洛勉,add方法至當(dāng)前類 required_new user失敗,account失敗

場景5和6為何會出現(xiàn)不一致如迟,required_new沒有起到作用收毫?

spring 聲明示事物使用動態(tài)代理實(shí)現(xiàn)的。

代理模擬:

TransactionalTest.java

com.demo.spring.TransactionalTest   

package com.demo.spring;

import com.demo.spring.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/18 23:05
 */
public class TransactionalTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-tx.xml");
        final UserService userService = context.getBean(UserService.class);

        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                TransactionalTest.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            System.out.println("開啟事物:" + method.getName());
                            return method.invoke(userService,args);
                        } finally {
                            System.out.println("關(guān)閉事物:" + method.getName());
                        }
                    }
                });

        proxyUserService.addUser("ayang");
    }
}

UserServiceImpl.java

package com.demo.spring.service;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:53
 */
@Service
public class UserServiceImpl implements UserService{
    @Resource
    private AccountService accountService;
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser(String name) {
        jdbcTemplate.update("insert into user (name) values(?)", name);
        this.addAccount(name, 100);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addAccount(String name, int money) {
        String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
        jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
        //int i = 1/0;
    }

}

結(jié)果:

開啟事物:addUser
關(guān)閉事物 :  addUser

當(dāng)我們調(diào)用addUser方法時(shí)殷勘,僅打印addUser的事物開啟和關(guān)閉牛哺,并沒打印addAccount的事物開啟和關(guān)閉,所以addAccount的事物的失效的劳吠。

原因在于spring 聲明示事物使用動態(tài)代理實(shí)現(xiàn)引润,而當(dāng)調(diào)用同一個類的方法時(shí),是不會走代理邏輯的痒玩,自然事物的配置也會失效淳附。

如果遇到這種情況如何處理?

在配置文件中添加:

<!-- 配置暴露proxy -->
<aop:aspectj-autoproxy expose-proxy="true"/>

在spring xml中配置 暴露proxy 對象蠢古,然后在代碼中用AopContext.currentProxy() 就可以獲當(dāng)前代理對象奴曙,然后基于代理對象調(diào)用創(chuàng)建帳戶

((UserSerivce) AopContext.currentProxy()).addAccount(name, 10000);

當(dāng)復(fù)現(xiàn)場景6,發(fā)現(xiàn)事物的配置又生效了草讶,與場景5結(jié)果一致洽糟,addUser失敗,addAcount成功。

github : https://github.com/zhaoyybalabala/spring-test


歡迎留言交流:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坤溃,一起剝皮案震驚了整個濱河市拍霜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薪介,老刑警劉巖祠饺,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汁政,居然都是意外死亡道偷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門记劈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勺鸦,“玉大人,你說我怎么就攤上這事目木』煌荆” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵嘶窄,是天一觀的道長怀跛。 經(jīng)常有香客問我距贷,道長柄冲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任忠蝗,我火速辦了婚禮现横,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阁最。我一直安慰自己戒祠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布速种。 她就那樣靜靜地躺著姜盈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪配阵。 梳的紋絲不亂的頭發(fā)上馏颂,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音棋傍,去河邊找鬼救拉。 笑死,一個胖子當(dāng)著我的面吹牛瘫拣,可吹牛的內(nèi)容都是我干的亿絮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼派昧!你這毒婦竟也來了黔姜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤斗锭,失蹤者是張志新(化名)和其女友劉穎地淀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岖是,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帮毁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豺撑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烈疚。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖聪轿,靈堂內(nèi)的尸體忽然破棺而出爷肝,到底是詐尸還是另有隱情,我是刑警寧澤陆错,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布灯抛,位于F島的核電站,受9級特大地震影響音瓷,放射性物質(zhì)發(fā)生泄漏对嚼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一绳慎、第九天 我趴在偏房一處隱蔽的房頂上張望纵竖。 院中可真熱鬧,春花似錦杏愤、人聲如沸靡砌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽通殃。三九已至,卻和暖如春厕宗,著一層夾襖步出監(jiān)牢的瞬間画舌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工媳瞪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骗炉,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓蛇受,卻偏偏與公主長得像句葵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 最近再次拜讀了spring的源碼乍丈,對關(guān)于spring事物的應(yīng)用部分加以整理,做如下筆記剂碴。如有不正確的地方,歡迎指正...
    默寫流年閱讀 2,013評論 0 7
  • 什么是事務(wù) 事務(wù)就是一組操作數(shù)據(jù)庫的動作集合轻专。 動作集合被完整地執(zhí)行忆矛,我們稱該事務(wù)被提交。動作集合中的某一部分執(zhí)行...
    超級變換形態(tài)閱讀 683評論 0 6
  • 這部分的參考文檔涉及數(shù)據(jù)訪問和數(shù)據(jù)訪問層和業(yè)務(wù)或服務(wù)層之間的交互请垛。 Spring的綜合事務(wù)管理支持覆蓋很多細(xì)節(jié)催训,然...
    竹天亮閱讀 1,038評論 0 0
  • 1. TransactionDefinition接口中定義五個隔離級別: ISOLATION_DEFAULT 這是...
    誰在烽煙彼岸閱讀 14,884評論 0 3
  • 寫這篇文章帶著無限的愧疚漫拭,加入007已經(jīng)有半年,這半年里大部分作業(yè)都是截止當(dāng)天才開始寫作業(yè)混稽。一直都知道繼續(xù)這樣采驻,在...
    Qin薇薇安閱讀 196評論 0 0