spring 事务源码赏析(二)
我们在spring 事务源码赏析(一) 中分析了spring事务是如何找到目标方法,并如何将事务的逻辑织入到我们的业务逻辑中。本篇我们将会看到spring事务的核心实现:
1.事务传播机制的实现
2.事务隔离级别的实现
3.事务的回滚提交等操作的实现
好了,废话不多说,接着上一篇,我们讲到了TransactionInterceptor这个切面的逻辑,先看一下类图:
简单介绍一下这个类图:
Advice是一个切面的最顶层接口,是一个空接口,用于表示这是一个切面,integrceptor接口也是一个空接口,顾名思义,实现这个接口表示是拦截器,至于是什么样的拦截器呢,有方法拦截器,构造函数拦截器,属性拦截器,我们这里就是方法拦截器MethodInterceptor,
再看另外一个InitializingBean接口,这个是一个由容器调用的接口,当我们bean完成初始化后,容器会调用这个接口的afterPropertiesSet方法。
还有一个Aware接口,是所有带有aware接口的最顶层接口,同样是空接口,这里实现的是BeanFactoryAware,在BeanFactory完成组装的时候会调用setBeanFactory方法,将BeanFactory传递给调用方,具体的调用请看我的另一个系列:spring5之容器始末源码赏析 (一)总览。
最后TransactionAspectSupport是一个抽象模板类,抽象类事务拦截器的逻辑。
在正真读代码之前我们先明确几个重要的类:
PlatformTransactionManager:这是事务的管理类,事务的核心逻辑包括事务的获取,提交,回滚都在这。
TransactionStatus:事务的状态,这个类可以说是穿针引线,贯穿整个事务。
好了,我们开始。我们知道,事务的最小粒度是方法级别的,所以它实现了MethodInterceptor接口,在Pointcut切点匹配到具体的方法时(@Transactional注解所注释的方法),MethodInterceptor接口的invoke()的逻辑会被织入业务逻辑中,我们来看一下invoke的实现:
@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);
}
我们看到通过处理,spring获取了目标类的Class对象,并传递给了invokeWithinTransaction方法,invocation::proceed是函数式编程的写法,实际上是InvocationCallback的一个实现,它会在代理逻辑执行完以后继续执行原有逻辑。
我们来看一下invokeWithinTransaction方法:
@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();
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)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
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;
} else {
......
}
}
这里我们只看一半,为什么,第一因为我们平时默认经常使用的TransactionManager是DataSourceTransactionManager(后面会介绍),它并没有实现CallbackPreferringPlatformTransactionManager,所以我们经常使用的其实只有前一半的逻辑,
第二后一半的事务处理与前一半逻辑基本一样,区别在后者将事务的逻辑放到一个内部类中交给第三方管理可以看一下:WebSphereUowTransactionManager,这里不再详细叙述。
我们梳理一下整个流程,再一步步的看:
第一步,spring先获取了TransactionAttribute,这类的实例是存储了@Transactional注解的解析结果,这个在上一篇已经分析过了。
第二步,spring开始准备事务了,所谓准备事务,就是开启事务,下面我们会着重分析这里。
第三步,如果没有异常则会继续执行原有逻辑。实际上是继续执行拦截链的下一个节点。(这里注意了,如果一个方法被多个拦截器发现,那么会根据先后顺序组成一个链表一次执行,这里要注意一下事务拦截器实行的位置,有时候动态数据源要考虑这个问题)。
第四步,如果有异常,则根据规则处理异常,如果没有则跳过
第五步,事务的后置处理,将之前被挂起的事务设置回来(如果有的话)
第六步, 提交事务
我们来看一下spring如何准备事务:
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
} TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
可以看到,贯穿整个事务的核心类就是TransactionStatus,这个类的实例记录了当前事务的运行状态,而这个实例的获取委托给了TransactionManager实例,我们看下DatasourceTransactionManager的抽象类AbstractPlatformTransactionManager的
实现:
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
} if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
} // Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
} // No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
这里就是事务的传播机制的实现了,首先,spring会先获取当前的事务,并判断当前事务是否存在,如果存在则走存在的逻辑(这个比较复杂,后面详细介绍).如果不存在,则会新开启一个事务,获取当前事务和判断是否存在事务的逻辑在具体的子类中实现:
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
红色的方法其实是从ThreadLocal中获取的,也就是说获取和当前线程绑定的数据库链接并赋值给DataSourceTransactionObject
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
如果当前链接存在,并且是活跃状态的话,那么可以确定,之前已经有事务开启了,否则当前事务为一个新的事务。
我们来看一下如果当前事务是新的事务的话,spirng的处理过程:
如果传播行为是TransactionDefinition.PROPAGATION_MANDATORY的话,就会抛出异常,如果传播行为是PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED的话就新建事务,可以看到,在之前没有事务的情况下,这三种传播行为的逻辑是一样的。注意,这里suspend一个null,目的是为了把当前线程的ThreadLocal中的值清空,suspend中的逻辑我们会在分析当前已存在事务的逻辑中展示。我们先看一下doBegin的逻辑:
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null; try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
} txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel); // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
} prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true); int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
} // Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
doBegin的逻辑很简单,通过datasource获取connection,设置autocommi为false,通过connection设置数据库的事务隔离级别,最后,如果是新的链接的话,会将connectionHolder与当前线程绑定。我们发现,事务的状态是放在transactionstatus对象中的,而事务的核心:数据库的Connection对象则是维护在ThreadLocal中,与当前线程绑定。
我们接着看prepareSynchronization方法:
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
TransactionSynchronizationManager里面是一堆ThreadLocal,这里是为了spring做数据同步用的,看看就可以了。
到此,事务算是真正开启了,不过,这只是当前事务不存在的情况下的逻辑,我们来看一下若当前事务已经存在了,spring的处理逻辑:
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
} if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
} if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
} if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
} // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
首先,若传播行为是PROPAGATION_NEVER的话,直接抛出异常,若是PROPAGATION_NOT_SUPPORTED,则挂起当前事务,若是PROPAGATION_REQUIRES_NEW,则挂起当前事务,并开启新的事务,可以看到,开启新的事务的逻辑和上面的一模一样。
若是PROPAGATION_NESTED,则记录一个savepoint,然后继续。所以关键的地方就是这个挂起的逻辑了,spring是如何挂起,又是在什么时候如何把挂起的事务继续的,我们接下来揭晓:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
......
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
核心就两行,我把不需要关注的删掉了,那个是spring同步数据的,继续看:
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
还记得之前执行doBegin方法的时候向TransactionSynchronizationManager中放入的是什么吗?没错,是ConnectionHolder,就是承载了数据库Connection对象的实例,这里将这个对象与当前线程解绑,并返回给SuspendedResourcesHolder,这个SuspendedResourcesHolder会被传递给TransactionStatus:
protected DefaultTransactionStatus newTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
return new DefaultTransactionStatus(
transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}
TranscationStatus不仅存有事务的状态,还有被挂起的事务的链接。到这里,spring根据配置,已经完成了事务的传播行为,和隔离级别的设置,接下来便是执行业务代码。
执行完业务代码会有两个结果,1.业务代码正常完成,需要提交事务2.业务代码发生异常,需要回滚事务。
我们先看第一种情况:
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
} DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
} if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
} processCommit(defStatus);
}
在判断是否需要roolback后,便处理提交sql的逻辑:
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false; try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true; if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
} // Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
} // Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
} }
finally {
cleanupAfterCompletion(status);
}
}
可以看到,如果当前有savePoint则释放掉savepoint,如果当前是新的事务,则提交事务:
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
后面的cleanupAfterCompletion方法中会释放链接,这里就不详细介绍了,至此,我们一个事务的整个生命周期已经结束,下面要做的,就是把transactionstatus中存放的suspendsource释放出来(如果有的话),与当前线程绑定,让被挂起的事务重新提上日程。
好了spring事务源码的分析就告一段落了,因个人水平有限,有任何不足或者理解错误的地方希望大家谅解并指正。谢谢~
转载请注明出处
spring 事务源码赏析(二)的更多相关文章
- spring 事务源码赏析(一)
在本系列中,我们会分析:1.spring是如何开启事务的.2.spring是如何在不影响业务代码的情况下织入事务逻辑的.3.spirng事务是如何找到相应的的业务代码的.4.spring事务的传播行为 ...
- spring事务源码研读1
转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void test ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- spring事务源码解析
前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...
- 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!
在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...
- [心得体会]spring事务源码分析
spring事务源码分析 1. 事务的初始化注册(从 @EnableTransactionManagement 开始) @Import(TransactionManagementConfigurati ...
- Spring系列(六):Spring事务源码解析
一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
随机推荐
- 菜鸟系列 Golang 实战 Leetcode —— 面试题24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点. 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3- ...
- .NET Core C#目录
.NET Core技术开发指南 简介 本系列教程是一份关于微软.NET Core技术栈的全面的教程,其中涉及了C#.Typescript.Angular.Redis等一系列的教程.其中教程中通常会含有 ...
- py基础之模块与包
'''模块:当代码越来越多时,将所有代码放入一个文件中便会无法维护,所以放到多个文件中去,这样每一个py文件就是一个模块 包:模块越来越多时容易重名,便将模块放入不同的包中,每个包中必须有一个__in ...
- Springboot与Maven多环境配置文件夹解决方案
Profile用法 我们在application.yml中为jdbc.name赋予一个值,这个值为一个变量 jdbc: username: ${jdbc.username} Maven中的profil ...
- Vue项目一、node.js和npm的安装和环境搭建
一.为什么安装node.js及npm npm npm是Node.js的包管理工具(package manager),是全球最大的生态系统,同过npm可以找到很多丰富的插件来满足项目的需求. a1.现在 ...
- SUCTF checkin
复现的时候看了源码...... 发现文件上传时会对文件内容以及后缀进行严格的检测 同时还有exif_imagetype 这个就用图片马就行绕过,绕过文件后缀试一下传图片马解析为php 但是常规解析 ...
- 谈谈集合.Map
本文来谈谈我们平时使用最多的HashMap. 1. 简介 HashMap是我们在开发过程中用的最多的一个集合结构,没有之一.HashMap实现了Map接口,内部存放Key-Value键值对,支持泛型. ...
- 织梦cms文章内容页上下篇单独获得URL和文章名称修改
1.打开/include/arc.archives.class.php ,查找 $this->PreNext['pre'] //约813行 在其下一行插入: $this->Pre ...
- 【图文+视频新手也友好】Java一维数组详细讲解(内含练习题答案+详解彩蛋喔~)
目录 视频讲解: 一.数组的概述 二.一维数组的使用 三.Arrays工具类中的sort方法(sort方法用的多,我们具体讲一下) 四.数组中的常见异常 五.一维数组练习题 六.彩蛋(本期视频使用的P ...
- Core + Vue 后台管理基础框架1——运行系统
1.down源码 git clone https://github.com/KINGGUOKUN/SystemManagement.git,项目目录如下: 2.还原数据库 找到项目根目录下System ...