Spring事务之@EnableTransactionManagement

news2024/12/23 10:29:11

前言

@EnableTransactionManagement 是Spring框架提供的一个注解,用于启用基于注解的事务管理。通过使用这个注解,你可以开启Spring对事务的自动管理功能,使得你可以在方法上使用 @Transactional 注解来声明事务。

@EnableTransactionManagement 主要有以下两个属性:

  1. mode(默认为 AdviceMode.PROXY):指定事务代理的模式。可以选择的值有 AdviceMode.PROXYAdviceMode.ASPECTJ。默认是 AdviceMode.PROXY,表示使用基于代理的方式实现事务管理。如果选择 AdviceMode.ASPECTJ,表示使用基于AspectJ的方式实现事务管理。

  2. proxyTargetClass(默认为 false):用于确定代理是否应该使用目标类的类代理,而不是目标类实现的接口。如果设置为 true,代理将使用目标类的类代理(CGLIB代理),如果设置为 false,代理将使用目标类实现的接口代理(JDK动态代理)。

当你在配置类上使用 @EnableTransactionManagement 注解时,Spring框架会自动扫描带有 @Transactional 注解的方法,并在这些方法周围织入事务管理的逻辑。如果一个带有 @Transactional 注解的方法执行过程中出现了异常,Spring会自动回滚事务。这个注解的作用是简化事务管理的配置和使用,提高开发效率。

示例:

@Configuration
@EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false)
public class AppConfig {
    // ...
}

在这个示例中,@EnableTransactionManagement 启用了基于代理的事务管理,使用了默认的代理模式和接口代理。你可以根据需要调整这两个属性的值。

源码分析

首先查看下@EnableTransactionManagement注解,使用@import注解导入了一个类:TransactionManagementConfigurationSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
		boolean proxyTargetClass() default false;
		AdviceMode mode() default AdviceMode.PROXY;
		int order() default Ordered.LOWEST_PRECEDENCE;
}

TransactionManagementConfigurationSelector

默认情况下mode = AdviceMode.PROXY,所以当配置类解析的时候会调用selectImports(AnnotationMetadata importingClassMetadata)方法。

在这里插入图片描述

也就是AdviceModeImportSelector类的selectImports(AnnotationMetadata importingClassMetadata)方法将被调用,继而调用TransactionManagementConfigurationSelector的selectImports(AdviceMode adviceMode)方法,此处涉及到ImportSelector的使用。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		// @EnableTransactionaManagement注解默认就是Proxy
		switch (adviceMode) {
			case PROXY:
				// 然后就要把这个数组里的类进行加载,到此为止@EnableTransactionManagement注解的事情也就完成了
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

selectImports(AdviceMode adviceMode)返回的String数组,也就是会把这个数组里的类转化为BeanDefinition并注册到容器中。

ConfigurationClassParser类才是负责解析@Configuration注解的配置类。

ConfigurationClassBeanDefinitionReader类是ConfigurationClassParser的一个辅助类,用于将解析后的配置类信息转化为BeanDefinition对象并注册到Spring容器中。

ConfigurationClassParser类的主要职责是扫描和解析带有@Configuration注解的配置类,它会解析类中的各种注解、方法和字段,以确定要创建和配置的bean。它处理@Configuration注解、@Bean注解、@ComponentScan注解、@Import注解等,并根据这些注解生成相应的BeanDefinition对象。

一旦ConfigurationClassParser解析完成配置类,它会将解析得到的BeanDefinition对象传递给ConfigurationClassBeanDefinitionReader,后者负责将这些BeanDefinition对象注册到Spring容器中。

因此,ConfigurationClassParserConfigurationClassBeanDefinitionReader两者共同工作,一个负责解析和处理@Configuration注解的配置类,另一个负责将解析结果转化为BeanDefinition并注册到容器中。

AutoProxyRegistrar

AutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar接口,它的作用是在Spring容器中注册自动代理创建器(AutoProxyCreator)

具体来说,registerBeanDefinitions()方法是ImportBeanDefinitionRegistrar接口中的方法,它会在配置类(或使用)的初始化过程中被调用。该方法接收两个参数:

  • importingClassMetadata:用于获取导入类的元数据,例如注解信息、类名等。
  • registry:用于注册BeanDefinition的BeanDefinitionRegistry对象。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	private final Log logger = LogFactory.getLog(getClass());

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
		for (String annType : annTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
					Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				if (mode == AdviceMode.PROXY) {
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		if (!candidateFound && logger.isInfoEnabled()) {
			String name = getClass().getSimpleName();
			logger.info(String.format("%s was imported but no annotations were found " +
					"having both 'mode' and 'proxyTargetClass' attributes of type " +
					"AdviceMode and boolean respectively. This means that auto proxy " +
					"creator registration and configuration may not have occurred as " +
					"intended, and components may not be proxied as expected. Check to " +
					"ensure that %s has been @Import'ed on the same class where these " +
					"annotations are declared; otherwise remove the import of %s " +
					"altogether.", name, name, name));
		}
	}

}

registerBeanDefinitions()方法中,首先通过importingClassMetadata获取配置类上的所有注解类型。然后遍历这些注解类型,尝试获取注解上的modeproxyTargetClass属性值。

如果找到了具有modeproxyTargetClass属性的注解,并且它们的类型分别是AdviceModeboolean,则表示找到了候选的自动代理创建器。根据mode属性的值,如果是AdviceMode.PROXY,则调用AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)方法注册自动代理创建器。

如果proxyTargetClass属性为true,则调用AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry)方法,强制自动代理创建器使用类代理模式。然后,方法返回,完成注册过程。

如果在配置类上没有找到具有modeproxyTargetClass属性的注解,或者出现了其他异常情况,会打印一条警告日志,提示可能没有按预期进行自动代理创建器的注册和配置。

总之,AutoProxyRegistrar类的registerBeanDefinitions()方法通过检查配置类上的注解,决定是否注册自动代理创建器,并根据注解的属性值进行相应的配置。这样可以实现自动代理的功能,用于对标注了特定注解的组件进行代理。

InfrastructureAdvisorAutoProxyCreator

首先AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);其实是将InfrastructureAdvisorAutoProxyCreator生产Bean的定义信息注册到容器中。但其实还会存在一个覆盖的行为,如下方法代码所示:

private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				// currentPriority 当前注入进来的class处于什么位置
				//APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); 0
				//APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); 1
				//APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); 2
				// 也就是说底下的可以覆盖上面的
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

简要的分析一下registerOrEscalateApcAsRequired的实现。该方法用于在BeanDefinitionRegistry中注册自动代理创建器(AutoProxyCreator),并根据优先级进行必要的升级。

方法的主要逻辑如下:

  1. 首先,确保传入的 BeanDefinitionRegistry 参数不为null,否则抛出异常。

  2. 检查是否已经在 BeanDefinitionRegistry 中包含了名为 AUTO_PROXY_CREATOR_BEAN_NAME 的BeanDefinition,其中 AUTO_PROXY_CREATOR_BEAN_NAME 是自动代理创建器的名称。

  3. 如果已经存在自动代理创建器的BeanDefinition,则进一步判断当前要注册的自动代理创建器的类是否与已存在的BeanDefinition的类相同。如果不同,说明有更高优先级的自动代理创建器已经注册,需要进行升级。

  4. 通过比较自动代理创建器类的优先级,确定当前要注册的自动代理创建器的类是否具有更高的优先级。优先级通过 findPriorityForClass 方法进行判断。如果当前要注册的自动代理创建器的类优先级更高,则更新已存在的BeanDefinition的类为当前要注册的类。

  5. 如果不存在自动代理创建器的BeanDefinition,则创建一个 RootBeanDefinition 对象,该对象的类为当前要注册的自动代理创建器的类。

  6. 设置 RootBeanDefinition 的源对象为传入的 source 参数,这个参数表示注册的来源。

  7. RootBeanDefinition 设置属性值,将其顺序(order)设置为 Ordered.HIGHEST_PRECEDENCE,以确保它在其他Bean之前被创建。

  8. RootBeanDefinition 的角色(role)设置为 BeanDefinition.ROLE_INFRASTRUCTURE,表示它是一个基础架构角色的Bean。

  9. RootBeanDefinition 注册到 BeanDefinitionRegistry 中,使用 AUTO_PROXY_CREATOR_BEAN_NAME 作为Bean的名称。

  10. 最后,返回注册的 RootBeanDefinition 对象。

总之,这段代码描述了在BeanDefinitionRegistry中注册自动代理创建器的逻辑,并根据优先级进行必要的升级。它确保只注册具有最高优先级的自动代理创建器,并将其设置为基础架构角色的Bean。

然而在实际的使用过程中,都可能会被AnnotationAwareAspectJAutoProxyCreator替换,在同时开始了AOP和事务的情况下。具体可以看博主的另外一篇文章:SpringAOP源码解析之基础设施注册

ProxyTransactionManagementConfiguration

这个类 ProxyTransactionManagementConfiguration 是一个用于配置事务管理的Spring配置类。它继承自 AbstractTransactionManagementConfiguration,并使用了 @Configuration 注解进行标记,表示这是一个配置类。

该类中定义了以下几个方法:

  1. transactionAdvisor(): 这个方法用于创建 BeanFactoryTransactionAttributeSourceAdvisor 对象,作为事务的Advisor(通知器)。Advisor定义了在哪些切点上应用事务逻辑。该方法接收两个参数:transactionAttributeSource(事务属性源)和 transactionInterceptor(事务拦截器)。它将这两个对象设置到创建的 BeanFactoryTransactionAttributeSourceAdvisor 中,并根据需要设置Advisor的顺序。

  2. transactionAttributeSource(): 这个方法用于创建 AnnotationTransactionAttributeSource 对象,作为管理事务属性的事务属性源。AnnotationTransactionAttributeSource 是一个基于注解的事务属性源,用于从标注了事务注解的方法获取事务属性信息。

  3. transactionInterceptor(): 这个方法用于创建 TransactionInterceptor 对象,作为事务的拦截器。TransactionInterceptor 是一个AOP拦截器,用于在方法调用前后应用事务逻辑。它接收一个 transactionAttributeSource 参数,用于获取事务属性信息。如果配置了 txManager(事务管理器),则将其设置到创建的 TransactionInterceptor 中。

这些方法通过使用 @Bean 注解将它们声明为Bean,并使用 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 注解指定它们的角色为基础设施角色。这意味着它们是框架内部使用的组件,用于支持事务管理功能。

在配置中,transactionAdvisor() 方法创建了一个事务Advisor,将事务属性源和事务拦截器组合在一起,用于对切点进行增强,从而实现事务管理。transactionAttributeSource() 方法创建了一个注解事务属性源,用于管理事务的属性信息。transactionInterceptor() 方法创建了一个事务拦截器,用于在方法执行前后应用事务逻辑。

通过使用这些方法创建相应的组件,并将它们注册为Spring容器中的Bean,可以实现对事务的管理和拦截。

这个类中的方法用于配置事务管理的相关组件,包括事务Advisor、事务属性源和事务拦截器。它们是支持Spring事务管理的关键组件,用于实现声明式事务的功能。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	// 对切点进行增强,自然需要Advisor
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
			TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
		// 实例化advisor对象
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		// 引入第二个bean
		advisor.setTransactionAttributeSource(transactionAttributeSource);
		// 引入第三个bean
		advisor.setAdvice(transactionInterceptor);
		if (this.enableTx != null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}

	// 这个是管理事务属性的
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}

	// Advisor需要的拦截器
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource);
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}

}

InfrastructureAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator

InfrastructureAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator是Spring Framework中的两个关键类,用于实现AOP(面向切面编程)功能。

  1. InfrastructureAdvisorAutoProxyCreator是Spring框架的一个基础设施类,用于自动创建代理对象来实现AOP。它是一个后置处理器,用于在Spring容器初始化过程中检测并自动创建适当的代理对象。它主要被用于支持基于XML配置的AOP功能。

  2. AnnotationAwareAspectJAutoProxyCreator是Spring框架中的一个自动代理创建器,用于支持基于注解的AOP功能。它继承自InfrastructureAdvisorAutoProxyCreator,并在其基础上增加了对注解的解析和处理。它会扫描Spring容器中的bean,检测其中使用了AOP相关注解(例如@Aspect、@Before、@After等),然后自动创建相应的代理对象。

通过使用这两个类,Spring框架可以根据配置和注解信息,自动创建代理对象来实现AOP功能。AOP允许开发者通过定义切面(Aspect)和通知(Advice)来将横切关注点(Cross-cutting Concerns)应用到应用程序的不同部分,例如日志记录、性能监测、事务管理等。

在Spring框架中,默认情况下会注册一个InfrastructureAdvisorAutoProxyCreator(也称为AnnotationAwareAspectJAutoProxyCreator的父类)。这个类是Spring的一个基础设施类,用于支持的AOP功能。

当你使用@EnableAspectJAutoProxy注解时,Spring会自动将InfrastructureAdvisorAutoProxyCreator替换为更高级的AnnotationAwareAspectJAutoProxyCreator。AnnotationAwareAspectJAutoProxyCreator继承自InfrastructureAdvisorAutoProxyCreator,并在其基础上增加了对注解的解析和处理,以支持基于注解的AOP功能。

通过使用@EnableAspectJAutoProxy注解,你可以启用基于注解的AOP功能,并使用注解配置切面和通知,而不需要显式地在XML中配置AOP。也就是说当你使用@EnableAspectJAutoProxy注解时,Spring会自动升级为AnnotationAwareAspectJAutoProxyCreator,从而启用基于注解的AOP功能。

总结

本文描述的是@EnableTransactionManagement注解在初始化的时候做了哪些事情,其实和AOP还是有很大的关联。我们后续将针对事物的拦截、以及事物失效规则进行源码分析。

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

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

相关文章

Windows环境下ADB调试——安装adb

一、下载 Windows版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-windows.zipMac版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-darwin.zipLinux版本&#xff1a;https://dl.google.com/android/reposit…

CPD:使用restAPI和cpd-cli命令创建DMC实例

环境 Red Hat Enterprise Linux release 8.6 (Ootpa)OCP 4.12.22IBM CP4D 4.8.0Data Management Console 3.1.12 (DMC for CPD 4.8.0) 注&#xff1a;使用了fyre VM。 创建DMC实例 准备 首先export环境变量&#xff1a; . ./stg_env.sh把 cpd-cli 放到PATH里。编辑 ~/.ba…

【KCC@南京】KCC南京数字经济-开源行

一场数字经济与开源的视听盛宴&#xff0c;即将于11月26日&#xff0c;在南京举办。本次参与活动的有&#xff1a; 庄表伟&#xff08;开源社理事执行长、天工开物开源基金会执行副秘书长&#xff09;、林旅强Richard&#xff08;开源社联合创始人、前华为开源专家&#xff09;…

ElasticSearch简单操作

目录 1.单机部署 1.1 解压软件 1.2 创建软链接 1.3 修改配置文件 1.4 配置环境变量 1.5 后台启动 2.配置分词器 2.1 安装IK分词器 2.2 ES 扩展词汇 3.常用操作 3.1 索引 3.1.1 创建索引 3.1.2 查看所有索引 3.1.3 查看单个索引 3.1.4 删除索引 3.2.文档 3.2.1…

排序算法之-快速

算法原理 丛待排序的数列中选择一个基准值&#xff0c;通过遍历数列&#xff0c;将数列分成两个子数列&#xff1a;小于基准值数列、大于基准值数列&#xff0c;准确来说还有个子数列&#xff1a;等于基准值即&#xff1a; 算法图解 选出基准元素pivot&#xff08;可以选择…

【藏经阁一起读】(76)__《“DNS+”发展白皮书》

【藏经阁一起读】&#xff08;76&#xff09;__《“DNS”发展白皮书》 作者&#xff1a; 梁卓 宋林健 陈剑 刘志辉 刘保君 郭丰 马晨迪 马永 孙俊哲 沈建伟 嵇叶楠 孙宛月 张建光 李贤达 张晓军 赵华 发布时间&#xff1a;2023-10-31 章节数&#xff1a;6 一、基础知识 1.1、…

《深入浅出进阶篇》洛谷P4147 玉蟾宫——悬线法dp

上链接&#xff1a;P4147 玉蟾宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4147 上题干&#xff1a; 有一个NxM的矩阵&#xff0c;每个格子里写着R或者F。R代表障碍格子&#xff0c;F代表无障碍格子请找出其中的一个子矩阵&#xff0c…

做一个Sprngboot文件上传-阿里云

概述 这个模块是用来上传头像以及文章封面的&#xff0c;图片的值是一个地址字符串&#xff0c;一般存放在本地或阿里云服务中 1、本地文件上传 我们将文件保存在一个本地的文件夹下&#xff0c;由于可能两个人上传不同图片但是却同名的图片&#xff0c;那么就会一个人的图片就…

JavaEE——网络原理(网络层 IP协议与数据链路层)

文章目录 一、详细解释 IP协议二、解释 TCP 和 IP 之间的联系和区别。三、IP协议——地址管理四、数据链路层 一、详细解释 IP协议 注&#xff1a;在这里我向大家描述的 IP协议是 IPv4。 如上图所示&#xff0c;这就是 IP 协议头的格式&#xff0c;下面我会分别解释他们其中每…

【C++笔记】AVL树的模拟实现

【C笔记】AVL树的模拟实现 一、AVL树的概念二、AVL树的模拟实现2.1、定义节点2.2、插入2.3、旋转2.3.1、左单旋2.3.2、右单旋2.3.3、左右双旋2.3.4、右左双旋2.3.5、插入接口的整体代码实现 三、验证AVL树3.1、验证 一、AVL树的概念 二叉搜索树虽然在一般情况下可以提高查找的…

如何使用`open-uri`模块

首先&#xff0c;我们需要使用open-uri模块来打开网页&#xff0c;并使用Nokogiri模块来解析网页内容。然后&#xff0c;我们可以使用Nokogiri的css方法来选择我们想要的元素&#xff0c;例如标题&#xff0c;作者&#xff0c;内容等。最后&#xff0c;我们可以使用open-uri模块…

多语言TTS:Multilingual speech synthesis

文章目录 [Learning to Speak Fluently in a Foreign Language:Multilingual Speech Synthesis and Cross-Language Voice Cloning](https://arxiv.org/abs/1907.04448)[2019interspeech][google][Improving Cross-lingual Speech Synthesis with Triplet Training Scheme](htt…

3DMAX建模基础教程:捕捉功能

在3DMAX中&#xff0c;捕捉功能是一项极其重要的技术&#xff0c;它能帮助我们在创建三维模型时更加精确和高效。本教程将详细介绍3DMAX中的捕捉功能及其应用。 1. 捕捉简介 3DMAX中的捕捉功能是指将物体固定在三维空间中的特定位置&#xff0c;以便进行精确的建模操作。这种…

0基础学习VR全景平台篇第120篇:极坐标处理接缝 - PS教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 紧跟上节课&#xff0c;我们已经学会了怎么利用PS蒙版工具来对航拍全景图补天。但是在后续工作学习中&#xff0c;我们会遇到天空这部分存在部分接缝的问题&#xff0c;如图&…

算法通关村第八关-黄金挑战

大家好我是苏麟 ...... 路径总和2 描述 : 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 题目 : LeetCode 113.路径总和2 113. 路径总和 II 分析 : 这…

为什么UI自动化难做?—— 关于Selenium UI自动化的思考

在快速迭代的产品、团队中&#xff0c;UI自动化通常是一件看似美好&#xff0c;实际“鸡肋”&#xff08;甚至绝大部分连鸡肋都算不上&#xff09;的工具。原因不外乎以下几点&#xff1a; 1 效果有限 通常只是听说过&#xff0c;就想去搞UI自动化的团队&#xff0c;心里都认…

数据跨领域应用实例—车辆通行大数据应用场景(二)

2023年10月25日&#xff0c;国家数据局正式揭牌。标志着我国数据基础制度正在不断完善&#xff0c;数据资源使用水平稳步提升&#xff0c;数据要素市场将进入发展快车道。当前&#xff0c;数字经济已成为我国经济高质量发展的新动能&#xff0c;国家数据局的成立&#xff0c;在…

Python高级语法----Python类型注解与类型检查

文章目录 一、类型注解基础二、使用 `mypy` 进行类型检查三、类型注解的最佳实践结论在当今的软件开发实践中,类型注解和类型检查在提高代码的可读性和健壮性方面发挥着至关重要的作用。尤其在 Python 这种动态类型语言中,通过类型注解和类型检查工具,如 mypy,可以显著提升…

python爬虫代理ip关于设置proxies的问题

目录 前言 一、什么是代理IP? 二、为什么需要设置代理IP? 三、如何设置代理IP? 四、完整代码 总结 前言 在进行Python爬虫开发时&#xff0c;经常会遇到被封IP或者频繁访问同一网站被限制访问等问题&#xff0c;这时&#xff0c;使用代理IP就可以避免这些问题&#x…

微软允许OEM对Win10不提供关闭Secure Boot

用户可能将无法在Windows 10电脑上安装其它操作系统了&#xff0c;微软不再要求OEM在UEFI 中提供的“关闭 Secure Boot”的选项。 微软最早是在Designed for Windows 8认证时要求OEM的产品必须支持UEFI Secure Boot。Secure Boot 被设计用来防止恶意程序悄悄潜入到引导进程。问…