手写Spring:第20章-事务处理

news2024/11/24 8:00:55

文章目录

  • 一、目标:事务处理
  • 二、设计:事务处理
    • 2.1 事务单元测试
    • 2.2 事务设计
  • 三、实现:事务处理
    • 3.1 工程结构
    • 3.2 事务管理的核心类图
    • 3.3 定义事务注解
      • 3.3.1 定义事务注解
      • 3.3.2 定义事务接口
      • 3.3.3 默认事务定义实现类
      • 3.3.4 委托事务定义实现类
    • 3.4 事务属性
      • 3.4.1 事务属性接口
      • 3.4.2 默认事务属性实现类
      • 3.4.3 委托事务属性实现类
      • 3.4.4 回滚规则属性
      • 3.4.5 基于规则的事务属性实现类
    • 3.5 注解工具包
      • 3.5.1 注解属性
      • 3.5.2 注解类型工具类
    • 3.6 解析事务注解
      • 3.6.1 用于解析已知事务注释类型的策略接口
      • 3.6.2 Spring的事务注释类型解析实现类
    • 3.7 事务状态
      • 3.7.1 事务执行接口
      • 3.7.2 存档管理接口
      • 3.7.3 事务状态接口
      • 3.7.4 事务状态抽象类
      • 3.7.5 默认事务状态
    • 3.8 事务属性来源
      • 3.8.1 事务属性来源接口
      • 3.8.2 事务属性来源抽象类
      • 3.8.3 注解事务属性来源实现类
    • 3.9 事务拦截处理
      • 3.9.1 事务管理器平台接口
      • 3.9.2 事务管理器平台抽象类
      • 3.9.3 事务处理
      • 3.9.4 事务拦截器
      • 3.9.5 事务同步管理器
    • 3.10 获取数据库连接
      • 3.10.1 数据库连接处理器接口
      • 3.10.2 简单数据库连接处理器
      • 3.10.3 数据库连接处理器
      • 3.10.4 JDBC事务支撑对象
      • 3.10.5 数据源事务管理器
      • 3.10.6 数据源操作抽象类
      • 3.10.7 JDBC操作模板
  • 四、测试:事务处理
    • 4.1 添加测试配置
      • 4.1.1 事务测试类
      • 4.1.2 Spring属性配置文件
      • 4.1.3 初始化测试
    • 4.2 单元测试
      • 4.2.1 有事务测试
      • 4.2.2 无事务测试
  • 五、总结:事务处理

一、目标:事务处理

💡 如何运用 @Transaction 注解标识、AOP 切面包装、将 JDBC 中的事务操作功能整合到 Spring 的框架结构中?

  • 事务: 数据必须保证完整性和一致性。这也是事务的4个特性:ACID
    • 原子性(Atomicity):事务是一个原子操作,由一系列的动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
    • 一致性(Consistency):一旦事务完成(无论成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不是部分完成、部分失败。现实中的数据不应该被破坏。
    • 隔离性(Isolation):许多事务可能会同时处理相同的数据,因此每个事务都应该与其他事务隔离,防止数据被损坏。
    • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。在通常情况下,事务的结果会被写到持久化存储器中。

二、设计:事务处理

2.1 事务单元测试

JdbcTest.java

package com.lino.springframework.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.mysql.cj.jdbc.Driver;
import org.junit.Before;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @description: JDBC测试类
 */
public class JdbcTest {

    private DruidDataSource dataSource;
    private Connection connection;
    private Statement statement;

    @Before
    public void init() throws SQLException {
        dataSource = new DruidDataSource();
        dataSource.setDriver(new Driver());
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
    }

    @Test
    public void test_translation() throws SQLException {
        connection = dataSource.getConnection().getConnection();
        statement = connection.createStatement();

        connection.setAutoCommit(false);
        try {
            statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
            statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }
        connection.commit();
    }
}
  • 这就是使用 JDBC 直接操作提交事务的方式,首先设置自动提交 connection.setAutoCommit(false) 来关闭事务,然后在程序处理过程中进行手动提交事务和回滚事务的操作。

测试结果

信息: {dataSource-1} inited
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.lino.springframework.test.JdbcTest.test_translation(JdbcTest.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

在这里插入图片描述

2.2 事务设计

💡 设计:事务的核心逻辑处理该如何完成?

  • 简单来说,我们最终目的就是把数据库的事务操作用自定义注解和 AOP 切面进行管理,并扩充相应的事务特性。

在这里插入图片描述

  • 有了这个结构,就可以把这部分操作数据库事务的功能迁移到 Spring 中,使用注解标记和 AOP 切面进行拦截处理。
  • 如果想要提交事务,则需要保证是一个链接下进行处理的。这是需要引入事务管理器进行处理,使用 ThreadLocal 进行保存。
  • 只有定义了事务注解,才能对需要处理的事务进行方法拦截。在拦截到事务之后,把这种操作方法交给代理类,也就是把用户对数据库的操作包装到事务的代码库中,方便对操作方法进行事务提交和回滚等操作。

三、实现:事务处理

3.1 工程结构

spring-step-19
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-aop
	|			|	|-aspectj
	|			|	|	|-AspectJExpressionPointcut.java
	|			|	|	|-AspectJExpressionPointcutAdvisor.java
	|			|	|-framework
	|			|	|	|-adapter
	|			|	|	|	|-MethodBeforeAdviceInterceptor.java
	|			|	|	|-autoproxy
	|			|	|	|	|-DefaultAdvisorAutoProxyCreator.java
	|			|	|	|-AopProxy.java
	|			|	|	|-Cglib2AopProxy.java
	|			|	|	|-JdkDynamicAopProxy.java
	|			|	|	|-ProxyFactory.java
	|			|	|	|-ReflectiveMethodInvocation.java
	|			|	|-AdvisedSupport.java
	|			|	|-Advisor.java
	|			|	|-BeforeAdvice.java
	|			|	|-ClassFilter.java
	|			|	|-MethodBeforeAdvice.java
	|			|	|-MethodMatcher.java
	|			|	|-Pointcut.java
	|			|	|-PointcutAdvisor.java
	|			|	|-TargetSource.java
	|			|-beans
	|			|	|-factory
	|			|	|	|-annotation
	|			|	|	|	|-Autowired.java
	|			|	|	|	|-AutowiredAnnotationBeanPostProcessor.java
	|			|	|	|	|-Qualifier.java
	|			|	|	|	|-Value.java
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-InstantiationAwareBeanPostProcessor.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-FactoryBeanRegistrySupport.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-xml
	|			|	|	|	|-XmlBeanDefinitionReader.java
	|			|	|	|-Aware.java
	|			|	|	|-BeanClassLoaderAware.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-BeanFactoryAware.java
	|			|	|	|-BeanNameAware.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-FactoryBean.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-InitializingBean.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|	|-ObjectFactory.java
	|			|	|	|-PropertyPlaceholderConfigurer.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-annotation
	|			|	|	|-ClassPathBeanDefinitionScanner.java
	|			|	|	|-ClassPathScanningCandidateComponentProvider.java
	|			|	|	|-Scope.java
	|			|	|-event
	|			|	|	|-AbstractApplicationEventMulticaster.java
	|			|	|	|-ApplicationContextEvent.java
	|			|	|	|-ApplicationEventMulticaster.java
	|			|	|	|-ContextclosedEvent.java
	|			|	|	|-ContextRefreshedEvent.java
	|			|	|	|-SimpleApplicationEventMulticaster.java
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ApplicationContextAwareProcessor.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|	|-ConversionServiceFactoryBean.java
	|			|	|-ApplicationContext.java
	|			|	|-ApplicationContextAware.java
	|			|	|-ApplicationEvent.java
	|			|	|-ApplicationEventPublisher.java
	|			|	|-ApplicationListener.java
	|			|	|-ConfigurableApplicationContext.java
	|			|-core
	|			|	|-annotation
	|			|	|	|-AbstractAliasAwareAnnotationAttributeExtractor.java
	|			|	|	|-AliasFor.java
	|			|	|	|-AnnotatedElementUtils.java
	|			|	|	|-AnnotationAttributeExtractor.java
	|			|	|	|-AnnotationAttributes.java
	|			|	|	|-AnnotationConfigurationException.java
	|			|	|	|-AnnotationUtils.java
	|			|	|	|-DefaultAnnotationAttributeExtractor.java
	|			|	|	|-SynthesizedAnnotation.java
	|			|	|	|-SynthesizedAnnotationInvocationHandler.java
	|			|	|-convert
	|			|	|	|-converter
	|			|	|	|	|-Converter.java
	|			|	|	|	|-ConverterFactory.java
	|			|	|	|	|-ConverterRegistry.java
	|			|	|	|	|-GenericConverter.java
	|			|	|	|-support
	|			|	|	|	|-DefaultConversionService.java
	|			|	|	|	|-GenericConversionService.java
	|			|	|	|	|-StringToNumberConverterFactory.java
	|			|	|	|-ConversionService
	|			|	|-io
	|			|	|	|-ClassPathResource.java
	|			|	|	|-DefaultResourceLoader.java
	|			|	|	|-FileSystemResource.java
	|			|	|	|-Resource.java
	|			|	|	|-ResourceLoader.java
	|			|	|	|-UrlResource.java
	|			|	|-util
	|			|	|	|-ConcurrentReferenceHashMap.java
	|			|	|	|-ObjectUtils.java
	|			|	|	|-StringUtils.java
	|			|	|-BridgeMethodResolver.java
	|			|	|-GraalDetector.java
	|			|	|-MethodClassKey.java
	|			|	|-MethodParameter.java
	|			|	|-ResolvableType.java
	|			|	|-SerializableTypeWrapper.java
	|			|-jdbc
	|			|	|-core
	|			|	|	|-ColumnMapRowMapper.java
	|			|	|	|-JdbcOperations.java
	|			|	|	|-JdbcTemplate.java
	|			|	|	|-ResultSetExtractor.java
	|			|	|	|-RowMapper.java
	|			|	|	|-RowMapperResultSetExtractor.java
	|			|	|	|-SqlProvider.java
	|			|	|	|-StatementCallback.java
	|			|	|-datasource
	|			|	|	|-ConnectionHandle.java
	|			|	|	|-ConnectionHolder.java
	|			|	|	|-DataSourceTransactionManager.java
	|			|	|	|-DataSourceUtils.java
	|			|	|	|-JdbcTransactionObjectSupport.java
	|			|	|	|-SimpleConnectionHandle.java
	|			|	|-support
	|			|	|	|-JdbcAccessor.java
	|			|	|	|-JdbcUtils.java
	|			|	|-CannotGetJdbcConnectionException.java
	|			|	|-UncategorizedSQLException.java
	|			|-stereotype
	|			|	|-Component.java
	|			|-tx.transaction
	|			|	|-annotation
	|			|	|	|-AnnotationTransactionAttributeSource.java
	|			|	|	|-SpringTransactionAnnotationParser.java
	|			|	|	|-Transactional.java
	|			|	|	|-TransactionAnnotationParser.java
	|			|	|-interceptor
	|			|	|	|-AbstractFallbackTransactionAttributeSource.java
	|			|	|	|-DefaultTransactionAttribute.java
	|			|	|	|-DelegatingTransactionAttribute.java
	|			|	|	|-RollbackRuleAttribute.java
	|			|	|	|-RuleBasedTransactionAttribute.java
	|			|	|	|-TransactionAspectSupport.java
	|			|	|	|-TransactionAttribute.java
	|			|	|	|-TransactionAttributeSource.java
	|			|	|	|-TransactionInterceptor.java
	|			|	|-support
	|			|	|	|-AbstractPlatformTransactionManager.java
	|			|	|	|-AbstractTransactionStatus.java
	|			|	|	|-DefaultTransactionDefinition.java
	|			|	|	|-DefaultTransactionStatus.java
	|			|	|	|-DelegatingTransactionDefinition.java
	|			|	|	|-TransactionSynchronizationManager.java
	|			|	|-CannotCreateTransactionException.java
	|			|	|-NestedTransactionNotSupportedException.java
	|			|	|-PlatformTransactionManager.java
	|			|	|-SavepointManager.java
	|			|	|-TransactionDefinition.java
	|			|	|-TransactionException.java
	|			|	|-TransactionExecution.java
	|			|	|-TransactionStatus.java
	|			|-util
	|			|	|-ClassUtils.java
	|			|	|-NumberUtils.java
	|			|	|-ReflectionUtils.java
	|			|	|-StringValueResolver.java
	|-test
		|-java
			|-com.lino.springframework.test
				|-bean
					|-JdbcService.java
                |-ApiTest.java
                |-JdbcTest.java
		|-resources
			|-spring.xml

3.2 事务管理的核心类图

在这里插入图片描述

  • 以自定义事务注解 @Transactional 进行方法标记,用于事务管理扫描。把需要被事务管理的方法拦截 AOP 切面。
  • 因为事务需要在同一个链接下操作,所以需要使用 TransactionSynchronizationManager 类提供的 ThreadLocal 方法记录当前线程下的链接信息。
  • 事务的控制都需要被切面事务支撑类 TransactionAspectSupport 进行包装和处理,以便提交和回滚事务。

3.3 定义事务注解

3.3.1 定义事务注解

Transactional.java

package com.lino.springframework.tx.transaction.annotation;

import java.lang.annotation.*;

/**
 * @description: 事务注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    /**
     * 回滚
     */
    Class<? extends Throwable>[] rollbackFor() default {};
}
  • 只有作用到方法上的事务标记性注解,才能被 SpringTransactionAnnotationParser 类扫描并管理事务。

3.3.2 定义事务接口

TransactionDefinition.java

package com.lino.springframework.tx.transaction;

import java.sql.Connection;

/**
 * @description: 事务定义接口
 */
public interface TransactionDefinition {

    /**
     * 这个是Spring默认的事务传播行为
     * 如果正处于一个事务中,则加入,否则创建一个新的事务
     */
    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 = Connection.TRANSACTION_READ_UNCOMMITTED;

    /**
     * 读已提交
     */
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

    /**
     * 可重复读
     */
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

    /**
     * 串行化
     */
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

    /**
     * 默认超时时间
     */
    int TIMEOUT_DEFAULT = -1;

    /**
     * 获取传播行为
     */
    int getPropagationBehavior();

    /**
     * 获取事务隔离级别
     */
    int getIsolationLevel();

    /**
     * 获取事务超时时间
     */
    int getTimeout();

    /**
     * 是否总是读
     */
    boolean isReadOnly();

    /**
     * 获取事务名称
     */
    String getName();
}

3.3.3 默认事务定义实现类

DefaultTransactionDefinition.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;

/**
 * @description: 默认事务定义
 */
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {

    private int propagationBehavior = PROPAGATION_REQUIRED;

    private int isolationLevel = ISOLATION_DEFAULT;

    private int timeout = TIMEOUT_DEFAULT;

    private boolean readOnly = false;

    private String name;

    public DefaultTransactionDefinition() {
    }

    public DefaultTransactionDefinition(TransactionDefinition other) {
        this.propagationBehavior = other.getPropagationBehavior();
        this.isolationLevel = other.getIsolationLevel();
        this.timeout = other.getTimeout();
        this.readOnly = other.isReadOnly();
        this.name = other.getName();
    }

    public DefaultTransactionDefinition(int propagationBehavior) {
        this.propagationBehavior = propagationBehavior;
    }

    public void setPropagationBehavior(int propagationBehavior) {
        this.propagationBehavior = propagationBehavior;
    }

    @Override
    public int getPropagationBehavior() {
        return this.propagationBehavior;
    }

    public void setIsolationLevel(int isolationLevel) {
        this.isolationLevel = isolationLevel;
    }

    @Override
    public int getIsolationLevel() {
        return this.isolationLevel;
    }

    public void setTimeout(int timeout) {
        if (timeout < TIMEOUT_DEFAULT) {
            throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT");
        }
        this.timeout = timeout;
    }

    @Override
    public int getTimeout() {
        return this.timeout;
    }

    public void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
    }

    @Override
    public boolean isReadOnly() {
        return this.readOnly;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

3.3.4 委托事务定义实现类

DelegatingTransactionDefinition.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;
import java.util.Objects;

/**
 * @description: 委托事务定义
 */
public class DelegatingTransactionDefinition implements TransactionDefinition, Serializable {

    private final TransactionDefinition targetDefinition;

    public DelegatingTransactionDefinition(TransactionDefinition targetDefinition) {
        this.targetDefinition = targetDefinition;
    }

    @Override
    public int getPropagationBehavior() {
        return this.targetDefinition.getPropagationBehavior();
    }

    @Override
    public int getIsolationLevel() {
        return this.targetDefinition.getIsolationLevel();
    }

    @Override
    public int getTimeout() {
        return this.targetDefinition.getTimeout();
    }

    @Override
    public boolean isReadOnly() {
        return this.targetDefinition.isReadOnly();
    }

    @Override
    public String getName() {
        return this.targetDefinition.getName();
    }

    @Override
    public boolean equals(Object other) {
        return this.targetDefinition.equals(other);
    }

    @Override
    public int hashCode() {
        return this.targetDefinition.hashCode();
    }

    @Override
    public String toString() {
        return this.targetDefinition.toString();
    }
}

3.4 事务属性

3.4.1 事务属性接口

TransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.TransactionDefinition;

/**
 * @description: 事务属性
 */
public interface TransactionAttribute extends TransactionDefinition {

    /**
     * 回滚
     *
     * @param ex 异常
     * @return 回滚结果
     */
    boolean rollbackOn(Throwable ex);
}

3.4.2 默认事务属性实现类

DefaultTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.support.DefaultTransactionDefinition;

/**
 * @description: 默认事务属性
 */
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {

    public DefaultTransactionAttribute() {
        super();
    }

    @Override
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }

    @Override
    public String toString() {
        return "DefaultTransactionAttribute{}";
    }

}

3.4.3 委托事务属性实现类

DelegatingTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.support.DelegatingTransactionDefinition;
import java.io.Serializable;

/**
 * @description: 委托事务属性
 */
public class DelegatingTransactionAttribute extends DelegatingTransactionDefinition implements TransactionAttribute, Serializable {

    private final TransactionAttribute targetAttribute;

    public DelegatingTransactionAttribute(TransactionAttribute targetAttribute) {
        super(targetAttribute);
        this.targetAttribute = targetAttribute;
    }

    @Override
    public boolean rollbackOn(Throwable ex) {
        return this.targetAttribute.rollbackOn(ex);
    }
}

3.4.4 回滚规则属性

RollbackRuleAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import java.io.Serializable;

/**
 * @description: 回滚规则属性
 */
public class RollbackRuleAttribute implements Serializable {

    private final String exceptionName;

    public RollbackRuleAttribute(Class<?> clazz) {
        this.exceptionName = clazz.getName();
    }
}

3.4.5 基于规则的事务属性实现类

RuleBasedTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import java.io.Serializable;
import java.util.List;

/**
 * @description: 基于规则的事务属性
 */
public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute implements Serializable {

    private List<RollbackRuleAttribute> rollbackRules;

    public RuleBasedTransactionAttribute() {
        super();
    }

    public void setRollbackRules(List<RollbackRuleAttribute> rollbackRules) {
        this.rollbackRules = rollbackRules;
    }
}

3.5 注解工具包

3.5.1 注解属性

AnnotationAttributes.java

package com.lino.springframework.core.annotation;

import cn.hutool.core.lang.Assert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.LinkedHashMap;

/**
 * @description: 注解属性
 */
public class AnnotationAttributes extends LinkedHashMap<String, Object> {

    private final Class<? extends Annotation> annotationType;

    boolean validated = false;

    final String displayName;

    public AnnotationAttributes(Class<? extends Annotation> annotationType) {
        Assert.notNull(annotationType, "'annotationType' must not be null");
        this.annotationType = annotationType;
        this.displayName = annotationType.getName();
    }

    public Class<?>[] getClassArray(String attributeName) {
        return getRequiredAttribute(attributeName, Class[].class);
    }

    private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
        Object value = get(attributeName);

        if (!expectedType.isInstance(value) && expectedType.isArray() && expectedType.getComponentType().isInstance(value)) {
            Object array = Array.newInstance(expectedType.getComponentType(), 1);
            Array.set(array, 0, value);
            value = array;
        }

        return (T) value;
    }

    public Class<? extends Annotation> annotationType() {
        return this.annotationType;
    }
}

3.5.2 注解类型工具类

AnnotatedElementUtils.java

package com.lino.springframework.core.annotation;

import cn.hutool.core.collection.CollUtil;
import com.lino.springframework.core.BridgeMethodResolver;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @description: 注解类型工具类
 */
public class AnnotatedElementUtils {

    private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];

    public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
                                                                      Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
        return searchWithFindSemantics(element, annotationType, null, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Class<? extends Annotation> annotationType,
                                                 String annotationName, Processor<T> processor) {
        return searchWithFindSemantics(element, (null != annotationType ? Collections.singleton(annotationType) : Collections.emptySet()),
                annotationName, null, processor);
    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Set<Class<? extends Annotation>> annotationType,
                                                 String annotationName,
                                                 Class<? extends Annotation> containerType,
                                                 Processor<T> processor) {
        if (containerType != null && !processor.aggregates()) {
            throw new IllegalArgumentException("Search for repeatable annotations must supply an aggregating Processor");
        }

        return searchWithFindSemantics(element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0);

    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Set<Class<? extends Annotation>> annotationTypes, String annotationName,
                                                 Class<? extends Annotation> containerType, Processor<T> processor,
                                                 Set<AnnotatedElement> visited, int metaDepth) {
        if (visited.add(element)) {
            try {
                // Locally declared annotations (ignoring @Inherited)
                Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
                if (annotations.length > 0) {
                    List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);

                    // Search in local annotations
                    for (Annotation annotation : annotations) {
                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
                        if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
                            if (annotationTypes.contains(currentAnnotationType) ||
                                    currentAnnotationType.getName().equals(annotationName) ||
                                    processor.alwaysProcesses()) {
                                T result = processor.process(element, annotation, metaDepth);
                                if (result != null) {
                                    if (aggregatedResults != null && metaDepth == 0) {
                                        aggregatedResults.add(result);
                                    } else {
                                        return result;
                                    }
                                }
                            }
                            // Repeatable annotations in container?
                            else if (currentAnnotationType == containerType) {
                                for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
                                    T result = processor.process(element, contained, metaDepth);
                                    if (aggregatedResults != null && result != null) {
                                        // No need to post-process since repeatable annotations within a
                                        // container cannot be composed annotations.
                                        aggregatedResults.add(result);
                                    }
                                }
                            }
                        }
                    }

                    // Recursively search in meta-annotations
                    for (Annotation annotation : annotations) {
                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
                        if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
                            T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth + 1);
                            if (result != null) {
                                processor.postProcess(currentAnnotationType, annotation, result);
                                if (aggregatedResults != null && metaDepth == 0) {
                                    aggregatedResults.add(result);
                                } else {
                                    return result;
                                }
                            }
                        }
                    }

                    if (!CollUtil.isEmpty(aggregatedResults)) {
                        // Prepend to support top-down ordering within class hierarchies
                        processor.getAggregatedResults().addAll(0, aggregatedResults);
                    }
                }

                if (element instanceof Method) {
                    Method method = (Method) element;
                    T result;

                    // Search on possibly bridged method
                    Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
                    if (resolvedMethod != method) {
                        result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth);
                        if (result != null) {
                            return result;
                        }
                    }

                    // Search on methods in interfaces declared locally
                    Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
                    if (ifcs.length > 0) {
                        result = searchOnInterfaces(method, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth, ifcs);
                        if (result != null) {
                            return result;
                        }
                    }

                    // Search on methods in class hierarchy and interface hierarchy
                    Class<?> clazz = method.getDeclaringClass();
                    while (true) {
                        clazz = clazz.getSuperclass();
                        if (clazz == null || clazz == Object.class) {
                            break;
                        }
                        Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
                        if (!annotatedMethods.isEmpty()) {
                            for (Method annotatedMethod : annotatedMethods) {
                                if (AnnotationUtils.isOverride(method, annotatedMethod)) {
                                    Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
                                    result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
                                            annotationName, containerType, processor, visited, metaDepth);
                                    if (result != null) {
                                        return result;
                                    }
                                }
                            }
                        }
                        // Search on interfaces declared on superclass
                        result = searchOnInterfaces(method, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth, clazz.getInterfaces());
                        if (result != null) {
                            return result;
                        }
                    }
                } else if (element instanceof Class) {
                    Class<?> clazz = (Class<?>) element;
                    if (!Annotation.class.isAssignableFrom(clazz)) {
                        // Search on interfaces
                        for (Class<?> ifc : clazz.getInterfaces()) {
                            T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth);
                            if (result != null) {
                                return result;
                            }
                        }
                        // Search on superclass
                        Class<?> superclass = clazz.getSuperclass();
                        if (superclass != null && superclass != Object.class) {
                            T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth);
                            if (result != null) {
                                return result;
                            }
                        }
                    }
                }
            } catch (Throwable ex) {
                AnnotationUtils.handleIntrospectionFailure(element, ex);
            }
        }
        return null;
    }

    private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
                                            String annotationName, Class<? extends Annotation> containerType,
                                            Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {

        for (Class<?> ifc : ifcs) {
            Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc);
            if (!annotatedMethods.isEmpty()) {
                for (Method annotatedMethod : annotatedMethods) {
                    if (AnnotationUtils.isOverride(method, annotatedMethod)) {
                        T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth);
                        if (result != null) {
                            return result;
                        }
                    }
                }
            }
        }

        return null;
    }

    private static <A extends Annotation> A[] getRawAnnotationsFromContainer(AnnotatedElement element, Annotation container) {

        try {
            A[] value = (A[]) AnnotationUtils.getValue(container);
            if (value != null) {
                return value;
            }
        } catch (Throwable ex) {
            AnnotationUtils.handleIntrospectionFailure(element, ex);
        }
        // Unable to read value from repeating annotation container -> ignore it.
        return (A[]) EMPTY_ANNOTATION_ARRAY;
    }

    public static AnnotatedElement forAnnotations(final Annotation... annotations) {

        return new AnnotatedElement() {
            @Override
            public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
                for (Annotation annotation : annotations) {
                    if (annotation.annotationType() == annotationClass) {
                        return (T) annotation;
                    }
                }
                return null;
            }

            @Override
            public Annotation[] getAnnotations() {
                return annotations;
            }

            @Override
            public Annotation[] getDeclaredAnnotations() {
                return annotations;
            }
        };
    }

    private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {

        private final boolean classValuesAsString;

        private final boolean nestedAnnotationsAsMap;

        private final boolean aggregates;

        private final List<AnnotationAttributes> aggregatedResults;

        MergedAnnotationAttributesProcessor() {
            this(false, false, false);
        }

        MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
            this(classValuesAsString, nestedAnnotationsAsMap, false);
        }

        MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
                                            boolean aggregates) {

            this.classValuesAsString = classValuesAsString;
            this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
            this.aggregates = aggregates;
            this.aggregatedResults = (aggregates ? new ArrayList<>() : Collections.emptyList());
        }

        @Override
        public boolean alwaysProcesses() {
            return false;
        }

        @Override
        public boolean aggregates() {
            return this.aggregates;
        }

        @Override
        public List<AnnotationAttributes> getAggregatedResults() {
            return this.aggregatedResults;
        }

        @Override
        public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
            return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
                    this.classValuesAsString, this.nestedAnnotationsAsMap);
        }

        @Override
        public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
            annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
            Class<? extends Annotation> targetAnnotationType = attributes.annotationType();

            // Track which attribute values have already been replaced so that we can short
            // circuit the search algorithms.
            Set<String> valuesAlreadyReplaced = new HashSet<>();

            for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
                String attributeName = attributeMethod.getName();
                String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);

                // Explicit annotation attribute override declared via @AliasFor
                if (attributeOverrideName != null) {
                    if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
                        continue;
                    }

                    List<String> targetAttributeNames = new ArrayList<>();
                    targetAttributeNames.add(attributeOverrideName);
                    valuesAlreadyReplaced.add(attributeOverrideName);

                    // Ensure all aliased attributes in the target annotation are overridden. (SPR-14069)
                    List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
                    if (aliases != null) {
                        for (String alias : aliases) {
                            if (!valuesAlreadyReplaced.contains(alias)) {
                                targetAttributeNames.add(alias);
                                valuesAlreadyReplaced.add(alias);
                            }
                        }
                    }

                    overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
                }
                // Implicit annotation attribute override based on convention
                else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
                    overrideAttribute(element, annotation, attributes, attributeName, attributeName);
                }
            }
        }

        private void overrideAttributes(AnnotatedElement element, Annotation annotation,
                                        AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) {

            Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName);

            for (String targetAttributeName : targetAttributeNames) {
                attributes.put(targetAttributeName, adaptedValue);
            }
        }

        private void overrideAttribute(AnnotatedElement element, Annotation annotation,
                                       AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) {

            attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName));
        }

        private Object getAdaptedValue(
                AnnotatedElement element, Annotation annotation, String sourceAttributeName) {

            Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
            return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
        }
    }

    private interface Processor<T> {

        T process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);

        void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);

        boolean alwaysProcesses();

        boolean aggregates();

        List<T> getAggregatedResults();
    }
}
  • AnnotatedElementUtils 工具包源码包在 springframework.core 包下面,具体代码请查看源码。

3.6 解析事务注解

3.6.1 用于解析已知事务注释类型的策略接口

TransactionAnnotationParser.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.lang.reflect.AnnotatedElement;

/**
 * @description: 用于解析已知事务注释类型的策略接口
 */
public interface TransactionAnnotationParser {

    /**
     * 基于该解析器理解的注释类型,解析给定方法或类的事务属性
     *
     * @param element 注释类型
     * @return 事务属性
     */
    TransactionAttribute parseTransactionAnnotation(AnnotatedElement element);
}

3.6.2 Spring的事务注释类型解析实现类

SpringTransactionAnnotationParser.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.core.annotation.AnnotatedElementUtils;
import com.lino.springframework.core.annotation.AnnotationAttributes;
import com.lino.springframework.tx.transaction.interceptor.RollbackRuleAttribute;
import com.lino.springframework.tx.transaction.interceptor.RuleBasedTransactionAttribute;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: Spring的事务注释类型解析实现类
 */
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {

    @Override
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                element, Transactional.class, false, false);
        if (null != attributes) {
            return parseTransactionAnnotation(attributes);
        } else {
            return null;
        }
    }

    protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

        List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
        for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
            rollbackRules.add(new RollbackRuleAttribute(rbRule));
        }
        rbta.setRollbackRules(rollbackRules);
        return rbta;
    }
}
  • SpringTransactionAnnotationParser#parseTransactionAnnotation 方法用于提取配置注解 @Transactional 的方法。
    • 这里的提取操作使用了 AnnotatedElementUtils.findMergedAnnotationAttributes 方法,此方法来自 Spring Core 核心包提供的功能。
  • 接下来调用内部方法 parseTransactionAnnotation(AnnotationAttributes attributes),用于解析 @Transactional,并设置相关的事务属性。

3.7 事务状态

3.7.1 事务执行接口

TransactionExecution.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 事务执行接口
 */
public interface TransactionExecution {

    /**
     * Return whether the present transaction is new; otherwise participating
     * in an existing transaction, or potentially not running in an actual
     * transaction in the first place.
     */
    boolean isNewTransaction();

    /**
     * Set the transaction rollback-only. This instructs the transaction manager
     * that the only possible outcome of the transaction may be a rollback, as
     * alternative to throwing an exception which would in turn trigger a rollback.
     */
    void setRollbackOnly();

    /**
     * Return whether the transaction has been marked as rollback-only
     * (either by the application or by the transaction infrastructure).
     */
    boolean isRollbackOnly();

    /**
     * Return whether this transaction is completed, that is,
     * whether it has already been committed or rolled back.
     */
    boolean isCompleted();
}

3.7.2 存档管理接口

SavepointManager.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 存档管理接口
 */
public interface SavepointManager {

    /**
     * 保存存档
     */
    Object createSavepoint() throws TransactionException;

    /**
     * 回滚存档
     */
    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    /**
     * 释放存档
     */
    void releaseSavepoint(Object savepoint) throws TransactionException;
}

3.7.3 事务状态接口

TransactionStatus.java

package com.lino.springframework.tx.transaction;

import java.io.Flushable;
import java.io.IOException;

/**
 * @description: 事务状态接口
 */
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    /**
     * 判断是否有保存点
     *
     * @return 判断是否有保存点
     */
    boolean hasSavepoint();

    /**
     * 刷新
     *
     * @throws IOException IO异常
     */
    @Override
    void flush() throws IOException;
}

3.7.4 事务状态抽象类

AbstractTransactionStatus.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.NestedTransactionNotSupportedException;
import com.lino.springframework.tx.transaction.SavepointManager;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.IOException;

/**
 * @description: 事务状态抽象类
 */
public abstract class AbstractTransactionStatus implements TransactionStatus {

    private boolean rollbackOnly = false;

    private boolean completed = false;

    private Object savepoint;

    @Override
    public boolean isNewTransaction() {
        return false;
    }

    @Override
    public void setRollbackOnly() {
        this.rollbackOnly = true;
    }

    @Override
    public boolean isRollbackOnly() {
        return isLocalRollbackOnly() || isGlobalRollbackOnly();
    }

    public boolean isLocalRollbackOnly() {
        return this.rollbackOnly;
    }

    public boolean isGlobalRollbackOnly() {
        return false;
    }

    @Override
    public void flush() throws IOException {

    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }

    @Override
    public boolean isCompleted() {
        return completed;
    }

    public Object getSavepoint() {
        return savepoint;
    }

    public void setSavepoint(Object savepoint) {
        this.savepoint = savepoint;
    }

    @Override
    public boolean hasSavepoint() {
        return this.savepoint != null;
    }

    @Override
    public Object createSavepoint() throws TransactionException {
        return getSavepointManager().createSavepoint();
    }

    @Override
    public void rollbackToSavepoint(Object savepoint) throws TransactionException {
        getSavepointManager().rollbackToSavepoint(savepoint);
    }

    @Override
    public void releaseSavepoint(Object savepoint) throws TransactionException {
        getSavepointManager().releaseSavepoint(savepoint);
    }

    protected SavepointManager getSavepointManager() {
        throw new NestedTransactionNotSupportedException("This transaction does not support savepoints");
    }
}

3.7.5 默认事务状态

DefaultTransactionStatus.java

package com.lino.springframework.tx.transaction.support;

/**
 * @description: 默认事务状态
 */
public class DefaultTransactionStatus extends AbstractTransactionStatus {

    private final Object transaction;

    private final boolean newTransaction;

    public DefaultTransactionStatus(Object transaction, boolean newTransaction) {
        this.transaction = transaction;
        this.newTransaction = newTransaction;
    }

    public Object getTransaction() {
        return transaction;
    }

    public boolean hasTransaction() {
        return this.transaction != null;
    }

    @Override
    public boolean isNewTransaction() {
        return hasTransaction() && this.newTransaction;
    }
}

3.8 事务属性来源

3.8.1 事务属性来源接口

TransactionAttributeSource.java

package com.lino.springframework.tx.transaction.interceptor;

import java.lang.reflect.Method;

/**
 * @description: 事务属性来源接口
 */
public interface TransactionAttributeSource {

    /**
     * 获取事务属性
     *
     * @param method      方法
     * @param targetClass 类类型
     * @return 事务属性
     */
    TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass);
}

3.8.2 事务属性来源抽象类

AbstractFallbackTransactionAttributeSource.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.core.MethodClassKey;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 事务属性来源接口抽象类
 */
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {

    private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);

    private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
        @Override
        public String toString() {
            return "null";
        }
    };

    @Override
    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class) {
            return null;
        }
        Object cacheKey = getCacheKey(method, targetClass);
        TransactionAttribute cached = this.attributeCache.get(cacheKey);
        if (null != cached) {
            if (cached == NULL_TRANSACTION_ATTRIBUTE) {
                return null;
            } else {
                return cached;
            }
        } else {
            TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
            if (null == txAttr) {
                this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
            } else {
                this.attributeCache.put(cacheKey, txAttr);
            }
            return txAttr;
        }
    }

    protected Object getCacheKey(Method method, Class<?> targetClass) {
        return new MethodClassKey(method, targetClass);
    }

    protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
        if (!Modifier.isPublic(method.getModifiers())) {
            return null;
        }
        TransactionAttribute txAttr = findTransactionAttribute(method);
        if (null != txAttr) {
            return txAttr;
        }
        return findTransactionAttribute(method.getDeclaringClass());
    }

    /**
     * 在方法上查找事务的相关属性
     *
     * @param method 方法
     * @return 事务属性
     */
    protected abstract TransactionAttribute findTransactionAttribute(Method method);

    /**
     * 在类上查找事务的相关属性
     *
     * @param clazz 类
     * @return 事务属性
     */
    protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz);
}

3.8.3 注解事务属性来源实现类

AnnotationTransactionAttributeSource.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.tx.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;

/**
 * @description: 注解事务属性来源接口抽象类
 */
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {

    private final boolean publicMethodsOnly;

    private final Set<TransactionAnnotationParser> annotationParsers;

    public AnnotationTransactionAttributeSource() {
        this(true);
    }

    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
        this.publicMethodsOnly = publicMethodsOnly;
        this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }

    @Override
    protected TransactionAttribute findTransactionAttribute(Method method) {
        return determineTransactionAttribute(method);
    }

    @Override
    public TransactionAttribute findTransactionAttribute(Class<?> clazz) {
        return determineTransactionAttribute(clazz);
    }

    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
        for (TransactionAnnotationParser annotationParser : annotationParsers) {
            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
            if (attr != null) {
                return attr;
            }
        }
        return null;
    }
}

3.9 事务拦截处理

3.9.1 事务管理器平台接口

PlatformTransactionManager.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 事务管理器平台
 */
public interface PlatformTransactionManager {

    /**
     * 获取事务
     *
     * @param definition 事务定义
     * @return 事务状态
     * @throws TransactionException 事务异常
     */
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    /**
     * 提交事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    void commit(TransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    void rollback(TransactionStatus status) throws TransactionException;
}

3.9.2 事务管理器平台抽象类

AbstractPlatformTransactionManager.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.Serializable;

/**
 * @description: 抽象事务管理器平台
 */
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();
        if (null == definition) {
            definition = new DefaultTransactionDefinition();
        }
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new TransactionException("Invalid transaction timeout " + definition.getTimeout());
        }
        // 暂定事务传播为默认的行为
        DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true);
        // 开始事务
        doBegin(transaction, definition);
        return status;
    }

    private DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, Object transaction, boolean newTransaction) {
        return new DefaultTransactionStatus(transaction, newTransaction);
    }

    @Override
    public void commit(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processCommit(defStatus);
    }

    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
        doCommit(status);
    }

    @Override
    public void rollback(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processRollback(defStatus, false);
    }

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        doRollback(status);
    }

    /**
     * 获取事务
     *
     * @return 事务
     * @throws TransactionException 事务异常
     */
    protected abstract Object doGetTransaction() throws TransactionException;

    /**
     * 开始事务
     *
     * @param transaction 事务对象
     * @param definition  事务定义
     * @throws TransactionException 事务异常
     */
    protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;

    /**
     * 提交事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
}
  • 在事务的拦截处理中看到,我们使用 TransactionAspectSupport#invokeWithinTransaction 对事务进行了获取、提交和回滚操作。
  • 这些功能都是来自 AbstractPlatformTransactionManager 抽象事务管理器平台,在这个类中可以设置事务的传播行为、事务的操作。
    • 事务的获取、提交、回滚操作也来自数据库连接池提供的功能。

3.9.3 事务处理

TransactionAspectSupport.java

package com.lino.springframework.tx.transaction.interceptor;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionStatus;
import com.lino.springframework.util.ClassUtils;
import java.lang.reflect.Method;

/**
 * @description: 事务切面
 */
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {

    private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
            new NamedThreadLocal<>("Current aspect-driven transaction");

    private BeanFactory beanFactory;

    private TransactionAttributeSource transactionAttributeSource;

    private PlatformTransactionManager transactionManager;

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        // 查找事务注解 Transactional
        TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

        PlatformTransactionManager manager = determineTransactionManager();
        String joinPointIdentification = methodIdentification(method, targetClass);
        TransactionInfo txInfo = createTransactionIfNecessary(manager, txAttr, joinPointIdentification);

        Object retVal = null;
        try {
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable e) {
            completeTransactionAfterThrowing(txInfo, e);
            throw e;
        } finally {
            cleanupTransactionInfo(txInfo);
        }
        commitTransactionAfterReturning(txInfo);

        return retVal;
    }

    public TransactionAttributeSource getTransactionAttributeSource() {
        return transactionAttributeSource;
    }

    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionAttributeSource = transactionAttributeSource;
    }

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * 当前使用DataSourceTransactionManager
     */
    protected PlatformTransactionManager determineTransactionManager() {
        return getTransactionManager();
    }

    /**
     * 获取目标方法的唯一标识
     */
    private String methodIdentification(Method method, Class<?> targetClass) {
        return ClassUtils.getQualifiedMethodName(method, targetClass);
    }

    protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification) {
        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);
            }
        }
        return prepareTransactionInfo(tm, txAttr, joinPointIdentification, status);
    }

    protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification, TransactionStatus status) {
        TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinPointIdentification);
        if (txAttr != null) {
            txInfo.newTransactionStatus(status);
        }
        txInfo.bindToThread();
        return txInfo;
    }

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (null != txInfo && null != txInfo.getTransactionStatus()) {
            if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            } else {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
        }
    }

    protected void cleanupTransactionInfo(TransactionInfo txInfo) {
        if (null != txInfo) {
            txInfo.restoreThreadLocalStatus();
        }
    }

    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (null != txInfo && null != txInfo.getTransactionStatus()) {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }

    protected interface InvocationCallback {
        Object proceedWithInvocation() throws Throwable;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    protected final class TransactionInfo {

        private final PlatformTransactionManager transactionManager;
        private final TransactionAttribute transactionAttribute;
        private final String joinPointIdentification;
        private TransactionStatus transactionStatus;
        private TransactionInfo oldTransactionInfo;

        public TransactionInfo(PlatformTransactionManager transactionManager,
                               TransactionAttribute transactionAttribute, String joinpointIdentification) {
            this.transactionManager = transactionManager;
            this.transactionAttribute = transactionAttribute;
            this.joinPointIdentification = joinpointIdentification;
        }

        public PlatformTransactionManager getTransactionManager() {
            Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
            return transactionManager;
        }

        public String getJoinPointIdentification() {
            return joinPointIdentification;
        }

        public TransactionAttribute getTransactionAttribute() {
            return transactionAttribute;
        }

        public void newTransactionStatus(TransactionStatus status) {
            this.transactionStatus = status;
        }

        public TransactionStatus getTransactionStatus() {
            return transactionStatus;
        }

        public boolean hasTransaction() {
            return null != this.transactionStatus;
        }

        private void bindToThread() {
            this.oldTransactionInfo = transactionInfoHolder.get();
            transactionInfoHolder.set(this);
        }

        private void restoreThreadLocalStatus() {
            transactionInfoHolder.set(this.oldTransactionInfo);
        }

    }
}
  • 方法拦截器中的 invokeWithinTransaction 方法,就是 TransactionAspectSupport#invokeWithinTransaction 提供的具体拦截操作。
    • invokeWithinTransaction 方法中的操作包括:提取注解方法、开启事务的提交和回滚,以及把方法包装到 invocation.proceedWithInvocation 中并提交。
  • 到这里,已经完成了最基本的事务注解查找到提交的链路。

3.9.4 事务拦截器

TransactionInterceptor.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.io.Serializable;

/**
 * @description: 事务拦截器
 */
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource transactionAttributeSource) {
        setTransactionManager(ptm);
        setTransactionAttributeSource(transactionAttributeSource);
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return invokeWithinTransaction(invocation.getMethod(), invocation.getThis().getClass(), invocation::proceed);
    }
}
  • 因为需要使用一个被 AOP 拦截操作处理事务的类,所以需要实现一个事务拦截功能。

3.9.5 事务同步管理器

TransactionSynchronizationManager.java

package com.lino.springframework.tx.transaction.support;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 事务同步管理器
 */
public abstract class TransactionSynchronizationManager {

    /**
     * 当前线程的连接存储
     */
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
    /**
     * 事务的名称
     */
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");

    /**
     * 事务是否是只读
     */
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");

    /**
     * 事务的隔离级别
     */
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");

    /**
     * 事务是否开启
     */
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");

    public static Object getResource(Object key) {
        return doGetResource(key);
    }

    private static Object doGetResource(Object actualKey) {
        Map<Object, Object> map = resources.get();
        if (null == map) {
            return null;
        }
        return map.get(actualKey);
    }

    public static void bindResource(Object key, Object value) throws IllegalStateException {
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = resources.get();
        if (null == map) {
            map = new HashMap<>(16);
            resources.set(map);
        }
        map.put(key, value);
    }
}
  • TransactionSynchronizationManager 类主要使用 ThreadLocal 来记录本地线程的特性,保存一次线程下的 DB 链接,便于用户操作数据,以及保证 Spring 管理事务时获取的是同一个链接。否则在不同链接下,就不能再提交事务了。

3.10 获取数据库连接

3.10.1 数据库连接处理器接口

ConnectionHandle.java

package com.lino.springframework.jdbc.datasource;

import java.sql.Connection;

/**
 * @description: 数据库连接处理器接口
 */
public interface ConnectionHandle {

    /**
     * 获取连接
     *
     * @return 连接
     */
    Connection getConnection();

    /**
     * 释放连接
     *
     * @param con 连接
     */
    default void releaseConnection(Connection con) {}
}

3.10.2 简单数据库连接处理器

SimpleConnectionHandle.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import java.sql.Connection;

/**
 * @description: 简单数据库连接处理器
 */
public class SimpleConnectionHandle implements ConnectionHandle {

    private final Connection connection;

    public SimpleConnectionHandle(Connection connection) {
        Assert.notNull(connection, "Connection must not be null");
        this.connection = connection;
    }

    @Override
    public Connection getConnection() {
        return this.connection;
    }
}

3.10.3 数据库连接处理器

ConnectionHolder.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import java.sql.Connection;

/**
 * @description: 数据库连接处理器
 */
public class ConnectionHolder {

    private ConnectionHandle connectionHandle;

    private Connection currentConnection;

    public ConnectionHolder(ConnectionHandle connectionHandle) {
        this.connectionHandle = connectionHandle;
    }

    public ConnectionHolder(Connection connection) {
        this.connectionHandle = new SimpleConnectionHandle(connection);
    }

    public ConnectionHandle getConnectionHandle() {
        return connectionHandle;
    }

    protected boolean hasConnection() {
        return this.connectionHandle != null;
    }

    protected void setConnection(Connection connection) {
        if (null != this.currentConnection) {
            if (null != this.connectionHandle) {
                this.connectionHandle.releaseConnection(this.currentConnection);
            }
            this.currentConnection = null;
        }
        if (null != connection) {
            this.connectionHandle = new SimpleConnectionHandle(connection);
        } else {
            this.connectionHandle = null;
        }
    }

    protected Connection getConnection() {
        Assert.notNull(this.connectionHandle, "Active connection is required.");
        if (null == this.currentConnection) {
            this.currentConnection = this.connectionHandle.getConnection();
        }
        return this.currentConnection;
    }
}

3.10.4 JDBC事务支撑对象

JdbcTransactionObjectSupport.java

package com.lino.springframework.jdbc.datasource;

/**
 * @description: JDBC事务对象
 */
public abstract class JdbcTransactionObjectSupport {

    private ConnectionHolder connectionHolder;

    public void setConnectionHolder(ConnectionHolder connectionHolder) {
        this.connectionHolder = connectionHolder;
    }

    public ConnectionHolder getConnectionHolder() {
        return connectionHolder;
    }

    public boolean hasConnectionHolder() {
        return null != this.connectionHolder;
    }
}

3.10.5 数据源事务管理器

DataSourceTransactionManager.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.CannotCreateTransactionException;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.support.AbstractPlatformTransactionManager;
import com.lino.springframework.tx.transaction.support.DefaultTransactionStatus;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @description: 数据源事务管理器
 */
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean {

    private DataSource dataSource;

    public DataSourceTransactionManager() {
    }

    public DataSourceTransactionManager(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    protected DataSource obtainDataSource() {
        DataSource dataSource = getDataSource();
        Assert.notNull(dataSource, "No DataSource set");
        return dataSource;
    }

    @Override
    protected Object doGetTransaction() throws TransactionException {
        DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
        txObject.setConnectionHolder(conHolder, false);
        return txObject;
    }

    @Override
    protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        try {
            con.commit();
        } catch (SQLException e) {
            throw new TransactionException("Could not commit JDBC transaction", e);
        }
    }

    @Override
    protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        try {
            con.rollback();
        } catch (SQLException e) {
            throw new TransactionException("Could not roll back JDBC transaction", e);
        }
    }

    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;
        try {
            Connection newCon = obtainDataSource().getConnection();
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

            con = txObject.getConnectionHolder().getConnection();
            if (con.getAutoCommit()) {
                con.setAutoCommit(false);
            }
            prepareTransactionalConnection(con, definition);

            TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

        } catch (SQLException e) {
            try {
                assert con != null;
                con.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            txObject.setConnectionHolder(null, false);
            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", e);
        }

    }

    @Override
    public void afterPropertiesSet() {
        if (null == getDataSource()) {
            throw new IllegalArgumentException("Property 'datasource' is required");
        }
    }

    protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) throws SQLException {
        if (definition.isReadOnly()) {
            try (Statement stmt = con.createStatement()) {
                stmt.execute("set transaction read only");
            }
        }
    }

    public static class DataSourceTransactionObject extends JdbcTransactionObjectSupport {
        private boolean newConnectionHolder;
        private boolean mustRestoreAutoCommit;

        public void setConnectionHolder(ConnectionHolder connectionHolder, boolean newConnectionHolder) {
            super.setConnectionHolder(connectionHolder);
            this.newConnectionHolder = newConnectionHolder;
        }
    }
}

3.10.6 数据源操作抽象类

DataSourceUtils.java

package com.lino.springframework.jdbc.datasource;

import com.lino.springframework.jdbc.CannotGetJdbcConnectionException;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @description: 数据源操作抽象类
 */
public abstract class DataSourceUtils {

    /**
     * 获取数据库连接
     *
     * @param dataSource 数据库对象
     * @return 数据库连接
     */
    public static Connection getConnection(DataSource dataSource) {
        try {
            return doGetConnection(dataSource);
        } catch (SQLException e) {
            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", e);
        }
    }

    /**
     * Actually obtain a JDBC Connection from the given DataSource.
     * Same as {@link #getConnection}, but throwing the original SQLException.
     * <p>Is aware of a corresponding Connection bound to the current thread, for example
     * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread
     * if transaction synchronization is active (e.g. if in a JTA transaction).
     *
     * @param dataSource the DataSource to obtain Connections from
     * @return a JDBC Connection from the given DataSource
     * @throws SQLException if thrown by JDBC methods
     * @see #doReleaseConnection
     */
    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (null != conHolder && conHolder.hasConnection()) {
            return conHolder.getConnection();
        }
        return fetchConnection(dataSource);
    }

    private static Connection fetchConnection(DataSource dataSource) throws SQLException {
        Connection conn = dataSource.getConnection();
        if (null == conn) {
            throw new IllegalArgumentException("DataSource return null from getConnection():" + dataSource);
        }
        return conn;
    }

    public static void releaseConnection(Connection con, DataSource dataSource) {
        try {
            doReleaseConnection(con, dataSource);
        } catch (Exception ignore) {
        }
    }

    public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
        if (con == null) {
            return;
        }
        doCloseConnection(con, dataSource);
    }

    public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
        con.close();
    }
}
  • DataSourceUtils 数据源工具操作类中,最初只是用 dataSource.getConnection 方法获取数据源链接。
  • 为了扩展事务的功能,需要使用 TransactionSynchronizationManager.getResource(dataSource) 存储数据源链接,便于操作事务时获取同一个链接。

3.10.7 JDBC操作模板

JdbcTemplate.java

package com.lino.springframework.jdbc.core;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.jdbc.UncategorizedSQLException;
import com.lino.springframework.jdbc.datasource.DataSourceUtils;
import com.lino.springframework.jdbc.support.JdbcAccessor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * @description: JDBC 操作模板
 */
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    /**
     * 查询大小
     */
    private int fetchSize = -1;
    /**
     * 最大行数
     */
    private int maxRows = -1;
    /**
     * 查询时间
     */
    private int queryTimeout = -1;

    ...

    @Override
    public <T> T execute(StatementCallback<T> action) {
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        try {
            Statement stmt = con.createStatement();
            applyStatementSettings(stmt);
            return action.doInStatement(stmt);
        } catch (SQLException ex) {
            throw new UncategorizedSQLException("ConnectionCallback", getSql(action), ex);
        }
    }

    private static String getSql(Object sqlProvider) {
        if (sqlProvider instanceof SqlProvider) {
            return ((SqlProvider) sqlProvider).getSql();
        } else {
            return null;
        }
    }

    ...
}
  • 修改了 execute 方法中,添加了 getSql 方法。

四、测试:事务处理

4.1 添加测试配置

4.1.1 事务测试类

JdbcService.java

package com.lino.springframework.test.bean;

import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.tx.transaction.annotation.Transactional;

/**
 * @description: JDBC服务类
 */
public class JdbcService {

    @Transactional(rollbackFor = Exception.class)
    public void saveData(JdbcTemplate jdbcTemplate) {
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
    }

    public void saveDataNoTransaction(JdbcTemplate jdbcTemplate) {
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
    }
}
  • 模拟一个用户操作事务的 Bean 对象有两种方法,一种是使用 @Transactional 注解管理事务,另一种是不使用 @Transactional 注解管理事务。
  • 按照预期,使用事务注解提交两条一样的语句,数据库不会有任何记录,因为存在主键冲突。没有使用事务注解的语句会成功提交一条记录。

4.1.2 Spring属性配置文件

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

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <bean id="jdbcTemplate" class="com.lino.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="jdbcService" class="com.lino.springframework.test.bean.JdbcService"/>

</beans>
  • spring.xml 配置文件中,先配置数据库的链接信息及库表。
    • 再将 dataSource 注入 JdbcTemplate 中,由 JdbcTemplate 完成数据库的操作。
  • 配置 jdbcService Bean 对象是一个用于操作验证数据库事务的方法。

4.1.3 初始化测试

ApiTest.java

package com.lino.springframework.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.lino.springframework.aop.AdvisedSupport;
import com.lino.springframework.aop.TargetSource;
import com.lino.springframework.aop.aspectj.AspectJExpressionPointcut;
import com.lino.springframework.aop.framework.Cglib2AopProxy;
import com.lino.springframework.context.support.ClassPathXmlApplicationContext;
import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.lino.springframework.test.bean.JdbcService;
import com.lino.springframework.tx.transaction.annotation.AnnotationTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor;
import org.junit.Before;
import org.junit.Test;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @description: 测试类
 */
public class ApiTest {

    private JdbcTemplate jdbcTemplate;
    private JdbcService jdbcService;
    private DataSource dataSource;

    @Before
    public void init() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
        dataSource = applicationContext.getBean(DruidDataSource.class);
        jdbcService = applicationContext.getBean(JdbcService.class);
    }
}
  • init 方法是一个初始化操作,用于获取需要的 Bean 对象。

4.2 单元测试

4.2.1 有事务测试

ApiTest.java

@Test
public void test_Transaction() throws SQLException {
    AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
    transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());

    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);

    // 组装代理对象
    AdvisedSupport advisedSupport = new AdvisedSupport();
    advisedSupport.setTargetSource(new TargetSource(jdbcService));
    advisedSupport.setMethodInterceptor(interceptor);
    advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));

    // 代理对象(Cglib2AopProxy)
    JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();

    // 测试调用,有事务【不能同时提交2条有主键冲突的数据】
    proxy_cglib.saveData(jdbcTemplate);
}

测试结果

信息: {dataSource-1} inited

com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())

	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
	at com.lino.springframework.test.bean.JdbcService.saveData(JdbcService.java:16)
	at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
	at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
	at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
	at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveData(<generated>)
	at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
	at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
	... 35 more

在这里插入图片描述

  • 使用事务注解的测试结果为报错主键冲突,数据库不会有任何记录。

4.2.2 无事务测试

ApiTest.java

@Test
public void test_Transaction() throws SQLException {
    AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
    transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());

    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);

    // 组装代理对象
    AdvisedSupport advisedSupport = new AdvisedSupport();
    advisedSupport.setTargetSource(new TargetSource(jdbcService));
    advisedSupport.setMethodInterceptor(interceptor);
    advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));

    // 代理对象(Cglib2AopProxy)
    JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();

    // 测试调用,无事务【提交2条有主键冲突的数据,成功一条】
    proxy_cglib.saveDataNoTransaction(jdbcTemplate);

}

测试结果

信息: {dataSource-1} inited

com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())

	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
	at com.lino.springframework.test.bean.JdbcService.saveDataNoTransaction(JdbcService.java:21)
	at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
	at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
	at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
	at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveDataNoTransaction(<generated>)
	at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:62)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
	at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
	... 35 more

在这里插入图片描述

  • 没有使用事务注解同样会出现主键冲突的错误,但是数据库中会添加一条新插入的记录。

五、总结:事务处理

  • 先了解事务的特性及其最基本的使用流程,再思考如何把事务拆解到 AOP 中进行包装。这个过程也是对一个复杂问题的细化分析和处理。
  • 关于事务的操作,定义了事务注解,并在 invokeWithinTransaction 方法中使用和提取。
    • 并用此方法控制事务,包括获取事务、提交事务、回滚事务等。
  • ThreadLocal 是一个非常重要的知识点。如果没有使用统一的数据库链接,则无法做到用户的使用链接和切面链接保持为同一个,也就不能提交事务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/995238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 多线程系列Ⅵ(并发编程的六大组件)

JUC 组件 前言一、Callable二、ReentrantLock三、Atomic 原子类四、线程池五、Semaphore六、CountDownLatch 前言 JUC&#xff08;Java.util.concurrent&#xff09;是 Java 标准库中的一个包&#xff0c;它提供了一组并发编程工具&#xff0c;本篇文章就介绍几组常见的 JUC 组…

win10自带wifi共享功能

1、按下【wini】组合键打开windows设置&#xff0c;点击【网络和internet】&#xff1b; 2、按照下图&#xff0c;打开个移动热点&#xff0c;设置名称、密码。

Blender--》页面布局及基本操作讲解

接下来我会在three.js专栏中分享关于3D建模知识的文章&#xff0c;如果学习three朋友并且想了解和学习3D建模&#xff0c;欢迎关注本专栏&#xff0c;关于这款3D建模软件blender的安装&#xff0c;我在前面的文章已经讲解过了&#xff0c;如果不了解的朋友可以去考考古&#xf…

DeepinV20安装MSJDK17

装什么版本的JDK https://learn.microsoft.com/zh-cn/java/openjdk/download#openjdk-17 通常来讲&#xff0c;选择最适应自己应用程序的版本&#xff0c;例如最新开发的程序基本需要运行在jdk17了&#xff0c;又或者前几年的java程序基本都是jdk11,再旧一点的jdk8。尽可能选…

【C++深入浅出】类和对象中篇(六种默认成员函数、运算符重载)

目录 一. 前言 二. 默认成员函数 三. 构造函数 3.1 概念 3.2 特性 四. 析构函数 4.1 概念 4.2 特性 五. 拷贝构造函数 5.1 概念 5.2 特性 六. 运算符重载 6.1 引入 6.2 概念 6.3 注意事项 6.4 重载示例 6.5 赋值运算符重载 6.6 前置和后置运算符重载 七. c…

【Rust日报】2023-09-07 Servo 项目将加入欧洲 Linux 基金会

Servo 项目将加入欧洲 Linux 基金会 Servo 项目由 Mozilla Research 于 2012 年创建&#xff0c;是除编译器本身之外的首个主要 Rust 代码库&#xff0c;自此成为实验性网络引擎设计的标志。Servo 的主要组件已被集成到 Firefox 网络浏览器中&#xff0c;其若干解析器和其他底层…

渗透测试基础之永恒之蓝漏洞复现

渗透测试MS17-010(永恒之蓝)的漏洞复现 目录 渗透测试MS17-010(永恒之蓝)的漏洞复现 目录 前言 思维导图 1,渗透测试 1,1,什么是渗透测试? 1.2,渗透测试的分类: 1.3,渗透测试的流程 1.3.1,前期交互 1.3.2,情报收集 1.3.3,威胁建模 1.3.4,漏洞分析 1.3.5,漏洞验…

软件设计模式(五):代理模式

前言 代理模式是软件设计模式的重中之重&#xff0c;代理模式在实际应用比较多&#xff0c;比如Spring框架中的AOP。在这篇文章中荔枝将会梳理有关静态代理、动态代理的区别以及两种实现动态代理模式的方式。希望能对有需要的小伙伴有帮助~~~ 文章目录 前言 一、静态代理 二…

自定义Dynamics 365实施和发布业务解决方案 - 1. 准备工作

在当前的商业世界中,竞争每时每刻都在加剧每个企业都必须找到在竞争中保持领先的直观方法。其中之一企业面临的主要挑战是在以便为客户提供更好的服务。在这样一个竞争激烈、要求苛刻的时代环境中,对客户关系管理软件的需求是正在增加。 Dynamics 365的CE功能强大且适应性强…

使用JS实现一个简单的观察者模式(Observer)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 手撸Observer⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领…

MySQL数据库——存储引擎(1)-MySQL体系结构、存储引擎简介

目录 MySQL体系结构 连接层 服务层 引擎层 存储层 存储引擎简介 概念 语句 演示 下面开始学习进阶篇的第一个内容——存储引擎 分为四点学习&#xff1a; MySQL体系结构存储引擎简介存储引擎特点存储引擎选择 MySQL体系结构 连接层 最上层是一些客户端和链接服务&am…

小米和金山集团董事长雷军访问武汉:加大投资力度,深化务实合作

小米集团创始人雷军一行在9月6日到访了武汉&#xff0c;受到了当地政府的热情欢迎。武汉方面表示&#xff0c;小米、金山集团作为全球知名的企业集团&#xff0c;与武汉有着良好合作基础。未来&#xff0c;武汉希望小米、金山集团持续深耕武汉&#xff0c;加大投资力度&#xf…

主页整理:8月1日---9月10日

目录 8月1日17点 8月1日20点 8月3日13点 8月3日18点 8月15日19点 8月28日9点 8月28日18点 8月29日8点 8月29日9点 9月2日21点 9月5日17点 9月9日18点 9月10日7点 粉丝变化数 8月1日17点 8月1日20点 8月3日13点 8月3日18点 8月15日19点 8月28日9点 8月28日18点…

Element-ui container常见布局

1、header\main布局 <template> <div> <el-container> <el-header>Header</el-header> <el-main>Main</el-main> </el-container> </div> </template> <style> .el-header { …

日常开发小汇总(3)js类型判断

1.typeof 能判断出字符串、数字、方法和undefined&#xff0c;array、null、object判断不出 let num 1;let str "x";let fn function user(){}let arr [1,2]let obj {name:"zhangs"}let und;let nul null;console.log(typeof num) //numberconsole.l…

深度、广度优先遍历(邻接表)

#include<stdio.h> #include<stdlib.h> #include<iostream> #include<queue> #define MAXVEX 20 typedef char VertexType; using namespace std;//边表结点 typedef struct EdgeNode{int adjvex;struct EdgeNode *next; }EdgeNode;//顶点结点 typedef…

Spring Cloud:构建微服务的最佳实践

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

手机木马远程控制复现

目录 目录 前言 系列文章列表 渗透测试基础之永恒之蓝漏洞复现http://t.csdn.cn/EsMu2 思维导图 1&#xff0c;实验涉及复现环境 2,Android模拟器环境配置 2.1,首先从官网上下载雷电模拟器 2.2,安装雷电模拟器 2.3, 对模拟器网络进行配置 2.3.1,为什么要进行配置…

vagrant 虚拟机扩容磁盘

vagrant 虚拟机扩容磁盘 修改配置安装插件存储扩容 修改配置 参考博客:https://blog.csdn.net/marina_1/article/details/122238721 vagrant 版本 PS D:\vagrant\workplace\node2> vagrant --version Vagrant 2.3.7修改vagrant虚拟机配置文件Vagrantfile&#xff0c;添加磁…

互斥锁,自旋锁,读写锁

目录 互斥体 互斥锁 属性 使用流程&#xff08;相关API函数&#xff09; 互斥锁初始化和销毁的函数原型&#xff1a; 互斥锁的上锁和解锁的函数原型为&#xff1a; pthread_mutex_init() 定义 函数原型 返回值 初始化 pthread_mutex_destroy() 定义 函数原型 pt…