SpringBoot事务管理-5个面试核心类源码刨析

news2024/11/24 3:01:38

“简单的事重复做,你就是专家;重复的事用心做,你就是赢家。”

在开始讲解SpringBoot事务之前,我们先来整体回顾下事务的概念及特性,便于我们了解SpringBoot是如何解决事务相关问题的,另外这部分也是面试必考内容。

需要学习交流的可入群,大厂10年+大佬持续分享优质技术内容!带你深入技术腹地,学习硬核技术!

1、事务的概念

从专业技术角度讲,事务管理是对于一系列数据库操作进行管理,一个事务包含一个或多个SQL语句,而所有的SQL就组成了一个事务。那在逻辑层面,这些SQL执行要么全部成功,要么全部失败。比如,购物商城用户下单到付款完成可以看作是一个事务。作为一个事务来讲,这个下单过程要么全部成功,要么全部失败。如果部分失败,你可以想象一下会有什么问题?如果不加事务,又会造成哪些影响呢?

2、关于事务的特性

原子性(A), 主要针对的是事务的整体性,不能对事务进行分割,要么全部成功,要么全部失败。比如部分SQL成功,部分SQL失败这时不允许的;

一致性(C),主要针对的是数据层面,在事务执行前后需要保证数据一致性。比如,A给B转账1000元,那A账户需要扣掉1000元,而B账户需要增加1000元,不能出现A扣减成功而B则没有增加成功;

隔离性(I),主要解决的是多用户并发访问的问题,也就是说,一个用户的事务不能被其他用户的事务所终断,多个事务之间是互相隔离的。比如A账户给B账户赚钱,C账户给D账户赚钱,这两个事务是互不影响的;

持久性(D),主要针对的是事务提交的一种数据状态,一旦一个事务被提交,那么它的数据库中数据的改变就是永久性的,即使数据库发生故障也不会受影响。

以上,我们都是偏概念性的内容,那接下来讲解SpringBoot事务管理源码的部分是事务最核心部分,无论工作还是面试,都属于高频知识点。大家一定要认真学习~~

针对以上事务的特点,那么SpringBoot(或Spring)做了哪些工作呢?(在看下面的代码刨析前,大家可以自己设想一下,如果让你设计一个事务管理器,你会如何设计呢?)

3、SpringBoot事务管理源码分析

3.1 事务管理AOP机制

我们知道Spring有AOP机制,那Spring的事务管理就是基于AOP原理实现的,主要思想就是对业务调用方法前后进行拦截,在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务,如下图所示。

3.2 事务生命周期

事务生命周期6大核心步骤:获取事务(doGetTransaction )、开启事务(doBegin )、暂停事务(doSuspend )、恢复事务(doResume )、提交事务(doCommit )和回滚事务(doRollback )。

以下内容用DataSourceTransactionManager源码来说明这6部分内容的实现(其他几种实现方式类似)。

3.3 事务核心源码分析

在SpringBoot中,最核心类有:TransactionInterceptor(事务拦截器)、TransactionManager(事务管理器标签接口)、PlatformTransactionManager(具体事务管理器实现接口)、 TransactionInfo(事务管理信息对象存储) 和 TransactionStatus(当前事务管理状态记录)这5个核心类。

接下来,我们进行逐一分析。

1) 事务拦截器 TransactionInterceptor(线程安全)

该类的主要作用:拦截事务方法、在事务方法前进行解析事务存储对象、在方法执行后做提交回滚等处理。

我们知道,事务管理是基于AOP实现的,而AOP是需要实现MethodIntercptor(方法拦截器)接口的。那么事务管理对应的就是TransactionInterceptor也如此,在实际方法调用前会调用这个intercepter做额外的前置事务处理,我们可以大概看下TransactionInterceptor源代码。

/**
 *
 * <p>TransactionInterceptors are thread-safe.(线程安全)
 *
 */
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

	// 省略...

    /**
    * AOP核心方法
    */

	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
        // 这个方法主要完成的内容:
        // 1) 设置 TransactionAttribute 事务属性(后面会讲)
        // 2) 设置 TransactionManager   事务管理器(后面会讲)
        // 3) 设置 TransactionStatus   事务状态管理 (后面会讲)
        // 4) 解析包含事务的业务方法的请求参数、返回类型、隔离级别、异常处理机制等
        // 5) 设置 TransactionInfo 事务信息管理对象(后面会讲)
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
			@Override
			@Nullable
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
			@Override
			public Object getTarget() {
				return invocation.getThis();
			}
			@Override
			public Object[] getArguments() {
				return invocation.getArguments();
			}
		});
	}


	// 序列化相关内容(省略...)

}

简单看下invokeWithinTransaction方法, 这个方法是TransactionInterceptor 对应派生类TransactionAspectSupport中的方法。

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// 获取TransactionAttribute和TransactionManager。前面会提前做一些事务判断逻辑:比如是否为一个事务方法。
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		final TransactionManager tm = determineTransactionManager(txAttr);

	     // ...
         // transactionSupportCache为ConcurrentMap,是线程安全的,主要用于存储当前声明式事务缓存。便于后面流程使用。
		 ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
				Class<?> reactiveType =
						(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
				ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
				if (adapter == null) {
					throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
							method.getReturnType());
				}
				return new ReactiveTransactionSupport(adapter);
			});

			// ...
		}

        // 事务管理器实现处理 
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
				// Set rollback-only in case of Vavr failure matching our rollback rules...
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null && txAttr != null) {
					retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
				}
			}

			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
		// 该部分内容主要针对的是事务执行异常后,进行的一系列异常流程处理。可自行查看
		Object result;
		final ThrowableHolder throwableHolder = new ThrowableHolder();
		//... 
			
		}
	}

从源码中,我们大致了解到Spring采用的是声明式事务(即通过AOP的形式告诉执行器这个是事务),而且主要拦截在TransactionAspectSupport这个派生类中,这个类与Spring底层事务API进行了集成管理。

2) 事务管理器TransactionManager

该类的主要作用:事务管理标记接口,告诉代码解析器这个是一个事务方法,而不是普通的方法。

我们从TransactionManager源代码可以看到,这个类仅仅是一个空的接口定义,是不是很奇怪?这个属于Spring事务管理器实现类的标记接口, 从注解来看就是要告诉代码解析器这个是一个事务方法,而不是普通的方法。比如我们常见的Serializable 接口为,如果一个类实现了这个接口,说明它可以被序列化。因此,我们实际上通过Serializable这个接口,给该类标记了“可被序列化”的元数据,打上了“可被序列化”的标签。这也是标记/标签接口名字的由来。

关于标记接口,我在这里多解释下这种设计思路。

Marker Interface标记接口有时也叫标签接口(Tag interface),即接口不包含任何方法。

标记接口是计算机科学中的一种设计思路。编程语言本身不支持为类维护元数据。而标记接口则弥补了这个功能上的缺失:一个类实现某个没有任何方法的标记接口,实际上标记接口从某种意义上说就成为了这个类的元数据之一。运行时,通过编程语言的反射机制,我们就可以在代码里拿到这种元数据。

其中,针对这个接口的实现事务管理器如下,

很多人可能会问,之前的HibernateTransactionManager、JpaTransactionManager等管理器怎么没有了呢?这个主要是因为这些比较传统的持久层框架在很多主流开发中慢慢逐渐被淘汰了或者使用不是那么高频,除非是早些年的老项目才会存在这种API。所以对这种不常用的API在Spring5.x中进行了淘汰(如Spring3.0+版本只支持Hibernate 3.2+版本)。

我这里主要使用的是Spring5.x版本,以下是目前Spring5.x中保留的事务管理器。

1)DataSourceTransactionManager

数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;

2)JtaTransactionManager

提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;

3)WebSphereUowTransactionManager

Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;

4)WebLogicJtaTransactionManager

Spring提供的对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。

3)PlatformTransactionManager

该类的主要作用:对事务具体实现类的统一管理,包括事务的提交、回滚等操作。

PlatformTransactionManager 实现了TransactionManager,定义了3个基本接口方法。(获取当前事务状态/提交 /回滚). Spring进行了统一的抽象,形成了PlatformTransactionManager事务管理器接口,事务的提交、回滚等操作全部交给它来实现。

public interface PlatformTransactionManager extends TransactionManager {

	// 获取事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

    // 提交事务
	void commit(TransactionStatus status) throws TransactionException;
 
	// 回滚事务
	void rollback(TransactionStatus status) throws TransactionException;

}

而AbstractPlatformTransactionManage 实现了PlatformTransactionManager接口,是一个抽象的基本类,实现了Spring标准的事务管理流程,以DataSourceTransactionManager为例,大致流程如下:

① 确定是否已经有存在的事务

// 是否存在事务
protected boolean isExistingTransaction(Object transaction) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject)transaction;
        return txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive();
}

② 运用合适的事务隔离策略

protected void doBegin(Object transaction, TransactionDefinition definition) {
     // 省略...
     // 设置隔离级别
     Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
     txObject.setPreviousIsolationLevel(previousIsolationLevel);
     txObject.setReadOnly(definition.isReadOnly());
}

③ 暂停或者恢复事务

// 暂停事务
protected Object doSuspend(Object transaction) {
	DataSourceTransactionObject txObject = (DataSourceTransactionObject)transaction;
	txObject.setConnectionHolder((ConnectionHolder)null);
	return TransactionSynchronizationManager.unbindResource(this.obtainDataSource());
}
// 恢复事务
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
	TransactionSynchronizationManager.bindResource(this.obtainDataSource(), suspendedResources);
}

④ 提交事务时检查是否有只能回滚标记

// 提交事务
protected void doCommit(DefaultTransactionStatus status) {
	DataSourceTransactionObject txObject = (DataSourceTransactionObject)status.getTransaction();
	Connection con = txObject.getConnectionHolder().getConnection();
	if (status.isDebug()) {
		this.logger.debug("Committing JDBC transaction on Connection [" + con + "]");
	}

	try {
		con.commit();
	} catch (SQLException var5) {
		throw this.translateException("JDBC commit", var5);
	}
}

// 回滚事务
protected void doRollback(DefaultTransactionStatus status) {
	DataSourceTransactionObject txObject = (DataSourceTransactionObject)status.getTransaction();
	Connection con = txObject.getConnectionHolder().getConnection();
	if (status.isDebug()) {
		this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
	}

	try {
		con.rollback();
	} catch (SQLException var5) {
		throw this.translateException("JDBC rollback", var5);
	}
}

以上就是整个事务的执行过程,下面来看事务时如何进行信息管理的。

4) 事务信息 TransactionInfo

该类的主要作用:持有事务管理器,事务配置的属性,连接点,以及事务状态信息等。

TransactionInfo 主要是持有事务的状态,以及上一个TransactionInfo 的一个引用,并与当前线程进行绑定。主要是为了保证当前请求持有的是自己的事务对象,根据自己的事务状态决定事务的提交与否。TransactionInfo 是抽象类TransactionAspectSupport的一个内部类。

protected static final class TransactionInfo {

        // 对应事务管理器的实现,比如DataSourceTransactionManager、JdbcTransactionManager等
		@Nullable
		private final PlatformTransactionManager transactionManager;
       
        // 存储事务所需属性,比如回滚信息、隔离级别、超时设置、是否只读等
		@Nullable
		private final TransactionAttribute transactionAttribute;

        // 记录每次触发与数据库操作相关的位置,方便后续做提交操作
		private final String joinpointIdentification;

		// 事务状态存储对象,如是否有保存点、是否回滚、是否是新的事务、事务是否完成等
        @Nullable
		private TransactionStatus transactionStatus;

        // 对旧事物的信息存储,比如事务嵌套事务这种场景
		@Nullable
		private TransactionInfo oldTransactionInfo;

       // 省略...
}

说明:如果想要使用事务的回滚相关操作,继承了AbstractTransactionStatus类的子类必须提供实现,否则在使用事务的回滚点相关操作的时候会抛出异常。

该类完成的主要功能包含:

①  获取当前事务 -> getTransaction()

② 是否有激活的事务 -> hasTransaction()

③ 该事务是否是新事务 -> isNewTransaction()

④  事务是否只读 -> isReadOnly()

⑤  返回已为此事务挂起的资源的持有者(如果有)  -> getSuspendedResources()

5)事务状态TransactionStatus

该类的主要作用:描述当前事务的状态,比如:是否有事务,是否是新事物,事务是否只读和回滚点相关操作等。这些相关的属性在后面会影响事务的提交。

通过源码分析,我们发现TransactionStatus接口的实现类抽象类AbstractTransactionStatus类主要完成以下功能,主要是针对回滚点相关的一些操作。

① 创建回滚点 -> createAndHoldSavepoint()

② 设置回滚点 -> setSavepoint(@Nullable Object savepoint)

③ 获取回滚点 -> getSavepoint()

④ 判断是否有回滚点 -> getSavepoint()

⑤ 释放回滚点 -> releaseHeldSavepoint()

⑥ 回滚到回滚点 -> rollbackToHeldSavepoint()

注意:在AbstractTransactionStatus中创建回滚点是需要子类去实现getSavepointManager()方法,默认该方法会抛出异常NestedTransactionNotSupportedException("This transaction does not support savepoints")

4、SpringBoot事务实战演示

在SpringBoot中,使用事务需要做两步操作:

1)在启动类中使用注解 @EnableTransactionManagement 开启事务支持

2)在访问数据库的Service方法上添加注解 @Transactional 即可.(注意,方法必须为public)

下面通过SpringBoot +MyBatis实现对数据库商品表的更新操作,在service层的方法中构建异常,查看事务是否生效;我们依然基于前面的项目: SpringBootCase. 我们接着前面创建的TProductController类和TProductService类进行事务代码演示。

具体步骤如下:

1) 开启事务

package com.xintu.demo;

import com.xintu.demo.config.XinTuConfigInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@EnableTransactionManagement //开启事务
@RestController
@SpringBootApplication
public class SpringbootApplication {

	@Autowired
	private XinTuConfigInfo configInfo; //测试@ConfigurationProperties

	@Value("${test.site}")
	private String site;

	@Value("${test.user}")
	private String user;
	public static void main(String[] args) {
		SpringApplication.run(SpringbootApplication.class, args);
	}

	@GetMapping("/hello")
	public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
		return String.format("欢迎 %s 来到<a href=\"http://www.35xintu.com\">35新途</a>!", name);
	}

	@GetMapping("/value")
	public String testValue() { //测试 @Value 注解
		return String.format("欢迎 %s 来到<a href=\"http://www.35xintu.com\">%s</a>!" , user,site);
	}

	@GetMapping("/config")
	public String config() { //测试 @ConfigurationProperties 注解
		return String.format("欢迎 %s 来到<a href=\"http://www.35xintu.com\">%s</a>!" , configInfo.getUser(),configInfo.getSite());
	}

}

2)在TProductService类中添加事务方法

package com.xintu.demo.service;

import com.xintu.demo.entity.TProduct;
import com.xintu.demo.entity.TProductExample;
import com.xintu.demo.mapper.TProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author XinTu
 * @classname TProductService
 * @description TODO
 * @date 2023年04月29日 21:19
 */
@Service
public class TProductService {
    @Autowired
    private TProductMapper mapper;

    /**
     * 查询测试
     *
     * @return
     */
    public List<TProduct> queryList() {
        TProductExample example = new TProductExample();
        TProductExample.Criteria criteria = example.createCriteria();
        criteria.andCategoryIdEqualTo(1);
        return mapper.selectByExample(example);
    }

    @Transactional
    public Boolean update(TProduct record) {
        TProductExample example = new TProductExample();
        example.createCriteria().andCategoryIdEqualTo(1);
        Boolean flag = mapper.updateByExample(record, example) > 0 ? Boolean.TRUE : Boolean.FALSE;
        /** 构造一个除数为0的常见异常,测试事务是否起作用(操作数据库在这个异常之前) **/
        int a = 1/0;
        return flag;
    }
}

这里我们故意构造了一个异常逻辑,来验证在异常情况下事务事务会生效。

3)TProductController实现

package com.xintu.demo.controller;

import com.xintu.demo.entity.TProduct;
import com.xintu.demo.mapper.TProductMapper;
import com.xintu.demo.service.TProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author XinTu
 * @classname TProductController
 * @description TODO
 * @date 2023年04月29日 21:12
 */


@RestController
public class TProductController {

    @Autowired
    private TProductService service;

    /**
     * 查询测试
     * @return
     */
    @GetMapping(value = "/queryList")
    public List<TProduct> queryList() {
        return service.queryList();
    }


    @GetMapping(value = "/updateProduct")
    public Boolean updateProduct(){
        TProduct record = new TProduct();
        record.setId(13);
        record.setTitle("测试SpringBoot事务");
        return service.update(record);
    }

}

4) 启动SpringbootApplication,通过浏览器访问进行测试.

① 事务生效场景验证

浏览器执行,http://localhost:8888/springbootcase/updateProduct

控制台

数据库表数据依然没变。

通过以上结果,说明事务起作用了。

② 事务不生效场景验证

注释掉 TProductService 上的@Transactional进行测试。

后台依然报错,但数据库的数据以及被更新了。

以上就是整个SpringBoot事务管理核心内容,最后给大家留一个面试高频题:如果需要做分布式事务,该如何处理呢?

这个问题我会在后面的SpringBoot扩展篇中进行分析,大家可以先提前思考。

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

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

相关文章

Windows安装配置Tomcat服务器教程 - 外网远程访问

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 转载自cpolar文章&#xff1a;外网访问本地Tomcat服务器【cpolar内网穿透】…

Cadence (1) 手动制作SMD封装

前提&#xff1a;软件版本 焊盘设计 &#xff1a;Pad Designer16.6PCB设计 &#xff1a;PCB Editor16.6PCB参考&#xff1a;LP Viewer 10.2 文章目录 SMD封装制作(R0603)封装信息SMD焊盘制作新建工程添加焊盘库路径到PCB EditorPCB Editor设计预处理放置焊盘放置丝印放置1脚标识…

【10.HTML入门知识-CSS元素定位】

1 标准流&#xff08;Normal Flow&#xff09; 默认情况下&#xff0c;元素都是按照normal flow&#xff08;标准流、常规流、正常流、文档流【document flow】&#xff09;进行排布  从左到右、从上到下按顺序摆放好  默认情况下&#xff0c;互相之间不存在层叠现象 1.1…

【13.HTML-动画】

1 CSS属性 - transform 1.1 位移 - translate translate的百分比可以完成一个元素的水平和垂直居中&#xff1a; 1.2 缩放 - scale 1.3 旋转 - rotate 1.4 transform-origin 形变的原点 1.5 倾斜 - skew 1.6 transform设置多个值 2 transition动画 2.1 认识transition动画 2…

Java反射(原理剖析与使用)

一、反射机制是什么 1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得到class对象之后&#xff0c;再通过class对象进行反编译&#xff0c;从而获取对象的各种信息。 2、Java属于先编译再运行的语言&a…

2023年第二十届五一数学建模竞赛C题:“双碳”目标下低碳建筑研究-思路详解与代码答案

该题对于模型的考察难度较低&#xff0c;难度在于数据的搜集以及选取与处理。 这里推荐数据查询的网站&#xff1a;中国碳核算数据库&#xff08;CEADs&#xff09; https://www.ceads.net.cn/ 国家数据 国家数据​data.stats.gov.cn/easyquery.htm?cnC01 以及各省市《统…

第四届“长城杯”信息安全铁人三项赛决赛RE-obfuscating

这里主要是加了混淆 这里要用到IDA的一个插件D810和去混淆脚本deflat.py。值得注意的是deflat.py无法在主逻辑去混淆&#xff0c;这里可以参考这篇文章的脚本利用angr符号执行去除控制流平坦化 - 0x401RevTrain-Tools (bluesadi.github.io)。在使用deflat.py和这文章中的脚本轮…

【AI折腾录】stable web ui基础【sd安装、lora vae embedding hyperwork等基础概念】

目录 一 sd安装二 目标三 sd基础3.1 模型3.2 vae&#xff08;Variational autoencoder&#xff0c;变分自编码器&#xff09;3.3 embedding3.3.1 安装方式3.3.2 使用方式 3.4 Lora3.4.1 lora组成3.4.2 使用&#xff1a;3.4.3 效果3.4.4 测试不同CFG效果 3.5 hypernetworks 超网…

LeetCode_BFS_DFS_中等_1376.通知所有员工所需的时间

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 公司里有 n 名员工&#xff0c;每个员工的 ID 都是独一无二的&#xff0c;编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。 在 manager 数组中&#xff0c;每个员工都有一个直属负责人&#xff0c…

UE5实现距离测量功能

文章目录 1.实现目标2.实现过程2.1 Widget2.2 蓝图实现3.参考资料1.实现目标 UE5在Runtime环境下测量两个空间点位之间的绝对距离,并支持多段线的距离测量,GIF动图如下所示: 2.实现过程 实现原理比较简单,首先是基于PDI绘制线,有关绘制点和绘制线的可以看本专栏之前的文章…

css弹性布局

目录 1、实现弹性布局的前提&#xff1a;给父元素设置display:flex; 2、flex-direction&#xff1a;确定主轴方向 3、flex-wrap&#xff1a;是否换行 4、justify-content&#xff1a;主轴对齐方式 5、align-items&#xff1a;交叉轴对齐方式 6、align-content&#xff1a…

AWSFireLens轻松实现容器日志处理

applog应用程序和fluent-bit共享磁盘&#xff0c;日志内容是json格式数据&#xff0c;输出到S3也是JSON格式 applog应用部分在applog目录&#xff1a; Dockerfile文件内容 FROM alpine RUN mkdir -p /data/logs/ COPY testlog.sh /bin/ RUN chmod 777 /bin/testlog.sh ENTRYP…

人工智能技术在建筑能源管理中的应用场景

人工智能技术在建筑能源管理中的应用场景&#xff08;龙惟定&#xff09;&#xff0c;2021 摘 要 本文简要介绍了建筑能源管理(building energy management, BEM) 的概念。并从5个方面阐述了 BEM 对人工智能(AI) 技术的需求&#xff0c;即楼宇控制需要由从顶到底的基于物理模…

03-Vue技术栈之生命周期

目录 1、什么是生命周期2、分析生命周期2.1 生命周期钩子函数2.2 生命周期钩子函数的作用2.3 生命周期钩子函数图例2.4 生命周期钩子函数的应用 3、生命周期总结 1、什么是生命周期 又名&#xff1a;生命周期回调函数、生命周期函数、生命周期钩子。是什么&#xff1a;Vue在关…

ChatGPT实现编程语言转换

编程语言转换 对于程序员来说&#xff0c;往往有一类工作&#xff0c;是需要将一部分业务逻辑实现从服务端转移到客户端&#xff0c;或者从客户端转移到服务端。这类工作&#xff0c;通常需要将一种编程语言的代码转换成另一种编程语言的代码&#xff0c;这就需要承担这项工作…

Java多线程深入探讨

1. 线程与进程2. 创建和管理线程2.1. 继承Thread类2.2. 实现Runnable接口2.3 利用Callable、FutureTask接口实现。2.4 Thread的常用方法 3. 线程同步3.1. synchronized关键字3.1.1同步代码块&#xff1a;3.1.2 同步方法&#xff1a; 3.2. Lock接口 4. 线程间通信5. 线程池5.1 使…

vue - pc端实现对div的拖动功能

实现对div的拖动功能&#xff0c;需要先要知道以下的一些原生事件和方法&#xff1b; 1&#xff0c;事件: 方法描述onmousedown鼠标按钮被按下onmousemove鼠标被移动onmouseup鼠标按键被松开 2&#xff0c;方法: 方法描述event.clientX返回当事件被触发时鼠标指针相对于浏览…

【BIM+GIS】Supermap加载实景三维倾斜摄影模型

OSGB是常见的倾斜模型格式,本文讲述如何在Supermap中加载实景三维倾斜摄影模型OSGB。 文章目录 一、生成配置文件二、加载倾斜模型1. 新建场景2. 添加模型3. 高程调整一、生成配置文件 点击【三维数据】→【数据管理】→【生成配置文件】。 参数设置如下: 源路径:选择倾斜模…

【12.HTML入门知识-flexbox布局】

flexbox布局 1 认识flexbox2 flex布局的重要概念3 flex布局的模型4 flex container/items相关的属性4.1 flex-direction:主轴的方向决定布局方向4.2 flex-wrap 单行或者多行排列4.3 flex-flow 简写4.4 justify-content 主轴对齐方式4.5 align-item 交叉轴单行对齐方式4.6 align…

【数据结构】JDK HashMap源码解析

目录 &#x1f31f;HashMap源码解析 &#x1f308;类的属性 &#x1f308;构造方法 &#x1f308;put方法 &#x1f31f;对比常用Map的子类实现: &#x1f31f;HashMap源码解析 JDK8之前&#xff0c;HashMap就是数组链表&#xff1b; JDK8之后&#xff0c;变成了数组链表红…