Spring事务源码:事务管理

参考文章:

《Spring 源码解析—事务执行》

《Spring事务源码解析之事务回滚篇》\

《Spring事务源码解析之事务提交篇》

前文:

《Spring事务源码:创建代理类》

        写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。

前言

        在前文中我们介绍了Spring事务管理是如何开启以及创建代理类对象的,本文我们来深入解析下代理类是如何实现事务的管控。

目录

前言

一、代理类中方法执行

二、事务管理

        1、invokeWithinTransaction

        2、createTransactionIfNecessary

三、开启事务

        1、获取当前事务

        2、获取事务对象

        2.1、dataSource

        2.2、getResource

        3、判断是否存在事务

        4、挂起事务

        4.1、suspend

        4.2、doSuspend

        5、开启新事务

        6、绑定线程与数据库连接

        7、创建默认status

        8、存在原事务时的处理方案


一、代理类中方法执行

        在以前讲AOP的时候我们介绍过在执行代理类的目标方法时,会调用Advisor的getAdvice方法获取MethodInterceptor接口实现类并执行其invoke方法。前一篇文章中我们看到了BeanFactoryTransactionAttributeSourceAdvisor的adviceBeanName便是TransactionInterceptor,该类继承了MethodInterceptor接口。

RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);


RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

        我们从TransactionInterceptor类的invoke方法开始分析,首先获取被代理类,然后调用invokeWithinTransaction方法,该方法内部将会把被代理类的方法纳入事务中。

	@Override
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        // 在事务中执行被代理类的方法
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			@Override
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}

        

二、事务管理

        1、invokeWithinTransaction

        invokeWithinTransaction方法首先获取了事务属性和事务管理器,然后针对声明式事务与编程式事务进行了区分处理,我们这里只对声明式事务进行分析。

        不同的事务处理方式使用不同的逻辑。对于声明式事务的处理与编程式事务的处理,第一点区别在于事务属性上,因为声明式事务处理是可以没有事务属性的,第二点区别就是在TransactionManager上,CallbackPreferringPlatformTransactionManager实现PlatformTransactionManager接口,暴露出一个方法用于执行事务处理中的回调。所以,这两种方式都可以用作事务处理方式的判断。

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
    // 获取事务属性
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    // 获取beanFactory中的transactionManager
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    // 声明式事务处理
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
       ...
    }
    else {
    	// 编程式事务处理
        try {
          ...
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }
    }
}

        声明式事务的处理过程中,首先会创建事务,即开启事务(这里会返回一个TransactionInfo用来保存事务的数据,便于后续流程中回滚与提交),然后调用被代理类中的方法,方法执行完后根据执行结果判断是回滚还是提交事务。注意无论是回滚还是提交最后都需要将事务信息清空。

final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
	// 创建事务,事务属性等信息会被保存进TransactionInfo中
	TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
	Object retVal;
	try {
            // 执行目标方法
		    retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
		    // 异常处理
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
            // 清空事务信息
			cleanupTransactionInfo(txInfo);
		}
        // 提交事务
		commitTransactionAfterReturning(txInfo);
	    return retVal;
}

        2、createTransactionIfNecessary

        首先调用getTransaction方法,该方法内部将会判断是否需要开启事务并绑定线程与数据库连接,是非常重要的过程,我们下面会做详细介绍。

        然后调用prepareTransactionInfo方法根据事务属性与status准备一个TransactionInfo。这是因为当已经建立事务连接并完成了事务信息的提取后,我们需要将所有的事务信息统一记录在TransactionInfo类型的实例中,这个实例包含了目标方法开始前的所有状态信息,一旦事务执行失败,Spring会通过TransactionInfo类型的实例中的信息来进行回滚等后续工作。

	protected TransactionInfo createTransactionIfNecessary(
			PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
        // 如果事务属性没有名称指定则使用方法唯一标识,并使用DelegatingTransactionAttribute封装txAttr
		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) {
                // 获取事务状态TransactionStatus
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
        // 构建事务信息,根据指定的属性与status构建一个TransactionInfo
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

三、开启事务

        1、获取当前事务

        首先根据doGetTransaction获取当前事务,然后调用isExistingTransaction判断是否存在事务,如果已经存在则进入handleExistingTransaction方法进行处理。

        如果不存在事务属性为REQUIRED、REQUIRES_NEW、NESTED则表明需要开启新事务,将会先将空事务挂起(之所以没有原事务也挂起是为了后续回滚或提交时做判断),然后doBegin开启事务,并调用prepareSynchronization将新事务的信息保存到当前线程中。

        如果都不是(例如SUPPORTS)则创建一个DefaultTransactionStatus。

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
	// 获取事务
    Object transaction = doGetTransaction();
    boolean debugEnabled = logger.isDebugEnabled();
    if (definition == null) {
        definition = new DefaultTransactionDefinition();
    }
    // 判断当前线程是否已经存在事务
    if (isExistingTransaction(transaction)) {
        // 处理已经存在的事务
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    // 事务超时设置验证
    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }
    // 如果当前线程不存在事务并且事务传播行为为 MANDATORY 报错
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    // 这三种事务传播行为需要开启新事务
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        // 挂起原事务,因为这里不存在原事务因此是null
        SuspendedResourcesHolder suspendedResources = suspend(null); 
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 开启事务
            doBegin(transaction, definition);
            // 同步事务状态
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException ex) {
            resume(null, suspendedResources);
            throw ex;
        }
        catch (Error err) {
            resume(null, suspendedResources);
            throw err;
        }
    }
    else {
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
}

        2、获取事务对象

        doGetTransaction方法可以获取事务对象

protected Object doGetTransaction() {
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    // this.dataSource即数据源,判断当前线程如果已经记录数据库连接则使用原有连接
    ConnectionHolder conHolder =
    (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
    // false表示非新创建连接
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
}

        2.1、dataSource

        这里的this.dataSource是我们在设置DataSourceTransactionManager时传入的(回顾下前文的事务配置)

<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
	public DataSourceTransactionManager(DataSource dataSource) {
		this();
        // 设置数据源
		setDataSource(dataSource);
		afterPropertiesSet();
	}

        2.2、getResource

        TransactionSynchronizationManager持有一个ThreadLocal的实例,其中存放了一个Map,该Map的key为datasource,value为ConnectionHolder。

// 用 ThreadLocal 来存放  ConnectionHolder 信息
private static final ThreadLocal<Map<Object, Object>> resources =
        new NamedThreadLocal<>("Transactional resources");

// 获取ConnectionHolder
public static Object getResource(Object key) {
    // 包装下传入的 key
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    // 在这里获取连接信息
    Object value = doGetResource(actualKey);
    return value;
}

// 具体的获取操作
private static Object doGetResource(Object actualKey) {
    // 从ThreadLocal中获取
    Map<Object, Object> map = resources.get();
    if (map == null) {
        return null;
    }
    Object value = map.get(actualKey);
    if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
        map.remove(actualKey);
        if (map.isEmpty()) {
            resources.remove();
        }
        value = null;
    }
    return value;
}

        这个ConnectionHolder可以简单地将其理解为Connection(数据库连接)的包装类,其中最重要的属性就是Connection了。

public class ConnectionHolder extends ResourceHolderSupport {
    // 当前数据库连接
	private final Connection connection;
	public ConnectionHolder(Connection connection) {this.connection = connection;}
	public Connection getConnection() {return this.connection;}
}

        3、判断是否存在事务

        判断当前线程是否存在事务,判读依据为当前线程记录的连接不为空且连接中(connectionHolder)中的transactionActive属性不为空。

	@Override
	protected boolean isExistingTransaction(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
	}

        4、挂起事务

        4.1、suspend

        suspend方法对有无同步的事务采取了不同的方案,但真正挂起事务都是使用的doSuspend

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) 
        throws TransactionException {
    // 如果有同步的事务,则优先挂起同步的事务
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
        try {
            Object suspendedResources = null;
            if (transaction != null) {
                // 挂起操作
                suspendedResources = doSuspend(transaction);
            }
            // 重置事务名称
            String name = TransactionSynchronizationManager.getCurrentTransactionName();
            TransactionSynchronizationManager.setCurrentTransactionName(null);
            // 重置只读状态
            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
            // 重置隔离级别
            Integer isolationLevel = TransactionSynchronizationManager
                    .getCurrentTransactionIsolationLevel();
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
            // 重置事务激活状态
            boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
            TransactionSynchronizationManager.setActualTransactionActive(false);
            // 返回挂起的事务
            return new SuspendedResourcesHolder(
                    suspendedResources, suspendedSynchronizations, name, readOnly, 
                    isolationLevel, wasActive);
        }
        catch (RuntimeException | Error ex) {
            doResumeSynchronization(suspendedSynchronizations);
            throw ex;
        }
    }
    else if (transaction != null) {
        Object suspendedResources = doSuspend(transaction);
        return new SuspendedResourcesHolder(suspendedResources);
    }
    else {
        return null;
    }
}

        4.2、doSuspend

        可以看出挂起操作主要是将当前connectionHolder置为null,返回原有事务信息,以便于后续恢复原有事务,并将当前正在进行的事务信息进行重置。

	protected Object doSuspend(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		txObject.setConnectionHolder(null);
        // 解除绑定
		return TransactionSynchronizationManager.unbindResource(this.dataSource);
	}

         继续进入TransactionSynchronizationManager.unbindResource来看解绑的操作,其实就是将现有的事务信息remove并返回上一级以便保存。

// 解除绑定操作
public static Object unbindResource(Object key) throws IllegalStateException {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    // 继续调用
    Object value = doUnbindResource(actualKey);
    if (value == null) {
        throw new IllegalStateException(...);
    }
    return value;
}

// 解除绑定操作
private static Object doUnbindResource(Object actualKey) {
    Map<Object, Object> map = resources.get();
    if (map == null) {
        return null;
    }
    // 移除资源
    Object value = map.remove(actualKey);
    if (map.isEmpty()) {
        resources.remove();
    }
    if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
        value = null;
    }
    return value;
}

        5、开启新事务

        这里我们看到了数据库连接的获取,如果是新事务需要获取新一个新的数据库连接,并为其设置了隔离级别、是否只读等属性。

protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;
    try {
        // 新事务开启时将ConnectionHolder置为了null
        if (txObject.getConnectionHolder() == null ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = this.dataSource.getConnection(); // 获取新的数据库连接
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();
        // 设置事务隔离级别和readOnly属性
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
            // 设置由Spring控制事务提交
            con.setAutoCommit(false); 
        }
        prepareTransactionalConnection(con, definition);
        // 设置当前线程的事务激活状态
        txObject.getConnectionHolder().setTransactionActive(true); 
        int timeout = determineTimeout(definition);
        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
            // 设置超时时间
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout); 
        }
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
        }
    }
    catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, this.dataSource);
            txObject.setConnectionHolder(null, false);
        }
        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    }
}

        6、绑定线程与数据库连接

        将事务信息记录到当前线程中

protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
        TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
            definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
            definition.getIsolationLevel() : null);
        TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
        TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
        TransactionSynchronizationManager.initSynchronization();
    }
}

        7、创建默认status

        如果不需要开启事务(例如SUPPORTS)则返回一个默认的status对象。

	protected final DefaultTransactionStatus prepareTransactionStatus(
			TransactionDefinition definition, Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, Object suspendedResources) {

		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
		prepareSynchronization(status, definition);
		return status;
	}

	protected DefaultTransactionStatus newTransactionStatus(
			TransactionDefinition definition, Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, Object suspendedResources) {

		boolean actualNewSynchronization = newSynchronization &&
				!TransactionSynchronizationManager.isSynchronizationActive();

        // 创建DefaultTransactionStatus对象
		return new DefaultTransactionStatus(
				transaction, newTransaction, actualNewSynchronization,
				definition.isReadOnly(), debug, suspendedResources);
	}

        8、存在原事务时的处理方案

        handleExistingTransaction方法针对不同的传播行为做不同的处理方法,比如挂起原事务开启新事务等。

	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {
		// 当传播方式为NEVER时抛出异常
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}
		// 当传播方式为NOT_SUPPORTED时挂起当前事务,然后在无事务的状态中运行
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			// 挂起事务
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			// 返回默认status
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}
		// 当传播方式为REQUIRES_NEW时挂起当前事务,然后启动新的事务
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction, creating new transaction with name [" +
						definition.getName() + "]");
			}
			// 挂起原事务
			SuspendedResourcesHolder suspendedResources = suspend(transaction);
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 启动新的事务
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException beginEx) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
			catch (Error beginErr) {
				resumeAfterBeginException(transaction, suspendedResources, beginErr);
				throw beginErr;
			}
		}
		// 当传播方式为NESTED时, 设置事务的保存点
		// 存在事务,将该事务标注保存点,形成嵌套事务。
		// 嵌套事务中的子事务出现异常不会影响到父事务保存点之前的操作。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			if (useSavepointForNestedTransaction()) {
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				// 创建保存点,回滚时只回滚到该保存点
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				// 如果不支持保存点,就启动新的事务
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}
		// 其余代码
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
	}

        到这里我们就分析完了事务的创建工作,下一步就是被代理类方法的执行,执行完之后会根据结果判断是回滚还是提交,这部分我们会在下文进行讲解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

哈希表(如何打造一个工业级的哈希表)

目录 哈希思想 哈希函数 哈希冲突 1.开放寻址法 2、链表法 解决装载因子过大的问题 选择合适的哈希冲突解决方法 哈希思想 哈希表(hashtable)是数组的一一种扩展&#xff0c;由数组演化而来&#xff0c;底层依赖数组支持按下标快速 访问元素的特性。换句话说&#xff0c;…

HTTP协议详解(二)

目录 1.HTTP 响应详解 1.1认识状态码(status code) 1.2 认识响应报头(header) 1.3 认识响应正文(body) 2.构造 HTTP 请求 2.1 通过form表单构造请求 2.2 通过ajax构造请求 2.3 使用第三方工具构造请求 开始之前我们先复习一下http协议格式 1.HTTP 响应详解 我们先抓包…

ChatGPT中文方式写作-chatgpt中文生成

ChatGPT是一种强大的自然语言处理技术&#xff0c;可以帮助人们进行各种语言任务&#xff0c;包括机器翻译、问答系统、自然语言生成等。在中文辅助写作上&#xff0c;ChatGPT也很有用武之地&#xff0c;下面我们将就如何通过ChatGPT实现中文辅助写作&#xff0c;提高文章质量和…

C语言预处理指令-宏定义、文件包含、条件编译

预处理指令简介 1.C语言在对源程序进行编译之前&#xff0c;会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令)&#xff0c;产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译 2.为了区分预处理指令和一般的C语句&#xff0c;所有预处理…

计算机科班与培训开发编程的区别在哪里?

科班、培训班、科班培训班的模式都培养了很多编程技术人员进入IT行业&#xff0c;有的成为某个技术领域的专家&#xff0c;有的成为领导层&#xff0c;有的一直在默默无闻的敲代码等待35岁的到来。不管那种方式入行&#xff0c;这些类似的情况都存在&#xff0c;并且未来还会一…

全链路监控:方案概述

问题背景 随着微服务架构的流行&#xff0c;服务按照不同的维度进行拆分&#xff0c;一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上&#xff0c;这些软件模块&#xff0c;有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台…

java版工程项目管理系统-功能清单 图文解析

ava版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…

哈希表(概念,冲突的解决,实现哈希桶)

目录 概念 冲突 如何尽量减少冲突? 负载因子 解决冲突的几种方案 冲突严重时的解决办法 哈希表的实现 基本类型哈希桶实现 泛型哈希桶实现 注意!!! 概念 构造出一种存储结构,通过某种函数使元素的存储位置(下标)与它的关键码之间能够建立一一的映射关系,那么在查找…

让ChatGpt可以看视频,看文档,帮你总结,并提供示例的github项目(附体验地址)

github地址&#xff1a;https://github.com/madawei2699/myGPTReader 演示 Stay updated with the latest news summaries daily with chatGPT. Use chatGPT to read and provide a summary of any webpage include the video(YouTube). 总之这个玩意有很多&#xff0c;可以…

【K8S系列】深入解析StatefulSet(一)

序言 那些看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持的意义。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点Kubernetes (k8s) 是一个容器编排平…

java毕业生就业信息管理系统servlet程序

1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;系统用户管理&#xff1a;不管是…

第13章_约束

第13章_约束 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司…

一次压测遇到的问题和排查过程记录

文章目录问题 & 排查1、cpu使用率过高问题描述问题排查解决方案扩展内容2、504 Gateway Time-out问题描述问题排查解决方案3、限流对压测的影响问题描述问题排查解决方案jmeter相关1、beanShell 动态生成签名2、响应断言3、导出结果树请求和响应文件问题 & 排查 1、cp…

域名批量查询功能常用查询方法教程

一些用户在抱怨&#xff0c;要找到好域名怎么就那么不容易呢&#xff0c;能不能让我批量查下不含0的数字啊&#xff0c;能不能查下不含4的数字啊&#xff0c;能不能查下AABBB这样的域名啊…… 别着急&#xff0c;这就给您支招啦&#xff1a;通过西部数码强大的批量查询功能&am…

活动报名|SOFA 五周年,Live Long and Prosper!

2018 年 4 月 19 日&#xff0c;我们在北京启程&#xff0c;伴随种下希望的种子&#xff0c;举办了 SOFAStack 社区的第一个开放日。转眼来到 2023 年&#xff0c;瑞兔送福又逢春暖花开&#xff0c;怀揣着新的愿景&#xff0c;我们将于 4 月 15 日回到北京庆祝 SOFA 的五岁生日…

服务器磁盘又双叒叕爆满了?被/proc占满?

又双叒叕服务器前言排查分析前言 继上一次文章&#xff1a; MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. 通过删除tomcat下的catalina.out文件&#xff0c;解决了磁盘爆满的问题后。今天又双叒叕出现这个问题。。…

万字长文解读「新一代CMDB落地的困境及出路」

2023年3月21日春分时节&#xff0c;优维结合在CMDB技术领域的经验沉淀与洞察能力&#xff0c;梳理金融客户在数据运营中面临的问题和挑战&#xff0c;为了帮助到广大客户建立健全有效的方法参考&#xff0c;全新策划了一档“CMDB数据运营精准化专场公开课”线上直播课程。该系列…

2023-Python实现百度翻译接口调用

目录 &#x1f449;1、目标网址 ​​​​​​​&#x1f449;2、接口分析调试 ​​​​​​​&#x1f449;3、python 代码实现 学习记录&#xff1a;百度翻译 ​​​​​​​&#x1f449;1、目标网址 百度翻译&#xff1a;百度翻译-200种语言互译、沟通全世界&#xff0…

【LeetCode】二叉树的中序遍历(递归,迭代,Morris遍历)

目录 题目要求&#xff1a;给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 方法一&#xff1a;递归 方法二&#xff1a;迭代 思路分析&#xff1a; 复杂度分析 代码展示&#xff1a; 方法三&#xff1a;Morris 遍历 思路分析&#xff1a; 复杂度分析…

Vue3学习笔记(9.1)

Vue.js style&#xff08;内联样式&#xff09; 我们可以在v-bind:style直接设置样式&#xff0c;可以简写:style <!--* Author: RealRoad1083425287qq.com* Date: 2023-04-02 19:41:53* LastEditors: Mei* LastEditTime: 2023-04-03 15:41:44* FilePath: \vscode\Vue3_li…
最新文章