spring注解驱动系列--声明式事务

一、环境搭建

一、导入依赖

     <!-- 数据源、数据库驱动、spring-jdbc模块-->

          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>

二、编写配置

1、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据

2、@EnableTransactionManagement 开启基于注解的事务管理功能;

3、配置事务管理器来控制事务;

@Bean

public PlatformTransactionManager transactionManager()

4、给方法上标注 @Transactional 表示当前方法是一个事务方法;

@EnableTransactionManagement
@ComponentScan("com.test")
@Configuration
public class TxConfig {
	
	//数据源
	@Bean
	public DataSource dataSource() throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser("root");
		dataSource.setPassword("123456");
		dataSource.setDriverClass("com.mysql.jdbc.Driver");
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
		return dataSource;
	}
	
	//
	@Bean
	public JdbcTemplate jdbcTemplate() throws Exception{
		//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
		return jdbcTemplate;
	}
	
	//注册事务管理器在容器中
	@Bean
	public PlatformTransactionManager transactionManager() throws Exception{
		return new DataSourceTransactionManager(dataSource());
	}
	

}

二、原理 

一、@EnableTransactionManagement

@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 Integer.MAX_VALUE;
}

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

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

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

TransactionManagementConfigurationSelector

注解中AdviceMode mode() default AdviceMode.PROXY;默认是PROXY类型,

所以,这个类会导入两个组件

AutoProxyRegistrar
ProxyTransactionManagementConfiguration
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);
	}

}

一、AutoProxyRegistrar组件

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

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

 1)importingClassMetadata:用于获取导入类的元数据,例如注解信息、类名等。 

 2)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();
		// 获取配置类上的所有注解类型。然后遍历这些注解类型,尝试获取注解上的mode和proxyTargetClass属性值。
        for (String annType : annTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");

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

如果proxyTargetClass属性为true,则调用AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry)方法,
强制自动代理创建器使用类代理模式。然后,方法返回,完成注册过程。
**/
			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;
					}
				}
			}
		}

        /**
        如果在配置类上没有找到具有mode和proxyTargetClass属性的注解,
        或者出现了其他异常情况,会打印一条警告日志,
        提示可能没有按预期进行自动代理创建器的注册和配置。
        **/
		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));
		}
	}

}

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

 InfrastructureAdvisorAutoProxyCreator:

AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)这个方法里面,其实是将InfrastructureAdvisorAutoProxyCreator生产Bean的定义信息注册到容器中。但其实还会存在一个覆盖的行为。

主要就是:利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;

@Nullable
	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())) {
				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;
	}

 1、

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

2、

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

3、

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

4、

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

5、

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

6、

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

7、

为 RootBeanDefinition 设置属性值:

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

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

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

二、ProxyTransactionManagementConfiguration组件

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

@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;
	}

}
 1、transactionAdvisor():

1、

用于创建 BeanFactoryTransactionAttributeSourceAdvisor 对象,作为事务的Advisor(通知器),Advisor定义了在哪些切点上应用事务逻辑。

2、

该方法接收两个参数:transactionAttributeSource(事务属性源)和 transactionInterceptor(事务拦截器)。它将这两个对象设置到创建的BeanFactoryTransactionAttributeSourceAdvisor 中,并根据需要设置Advisor的顺序

2、transactionAttributeSource() 

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

3、transactionInterceptor() 

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

 三、执行流程

ProxyTransactionManagementConfiguration给容器中注册事务增强器

        1:事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解

        2、事务拦截器:TransactionInterceptor;保存了事务属性信息,事务管理器,他是一个 MethodInterceptor。在目标方法执行的时候

                执行拦截器链

                事务拦截器

                        1)、先获取事务相关的属性

                        2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;

                        3)、执行目标方法

                                如果异常,获取到事务管理器,利用事务管理回滚操作;

                                如果正常,利用事务管理器,提交事务

1、ProxyTransactionManagementConfiguration给容器中注册事务增强器
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
   BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    //事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解  
   advisor.setTransactionAttributeSource(transactionAttributeSource());
    // 事务拦截器
   advisor.setAdvice(transactionInterceptor());
   if (this.enableTx != null) {
      advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
   }
   return advisor;
}
2、transactionAttributeSource()
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// 事务增强器要用事务注解的信息
   return new AnnotationTransactionAttributeSource();
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
   this.publicMethodsOnly = publicMethodsOnly;
   if (jta12Present || ejb3Present) {
      this.annotationParsers = new LinkedHashSet<>(4);
      this.annotationParsers.add(new SpringTransactionAnnotationParser());
      if (jta12Present) {
         this.annotationParsers.add(new JtaTransactionAnnotationParser());
      }
      if (ejb3Present) {
         this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
      }
   }
   else {
// mysql 使用这个事务解析器
      this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
   }
}

SpringTransactionAnnotationParser:主要解析各个事务属性

 

3、transactionInterceptor()
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
   TransactionInterceptor interceptor = new TransactionInterceptor();
       // 拦截器也保留了一份事务属性
   interceptor.setTransactionAttributeSource(transactionAttributeSource());
   if (this.txManager != null) {
      interceptor.setTransactionManager(this.txManager);
   }
   return interceptor;
}

TransactionInterceptor:本质也是MethodInterceptor,和aop源码里的增强拦截器本质一样。

 

 // transactionInterceptor里面执行invoke方法
@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...
   return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

 

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

		// If the transaction attribute is null, the method is non-transactional.
        // 获取事务类型,是jta还是ejb的
		TransactionAttributeSource tas = getTransactionAttributeSource();
        // 获取事务属性
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        // 获取事务管理器
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

// 如果有事务,就开启事务
		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
                // 执行方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
                // 出现异常,回滚事务
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
                //  清除事务信息{从当前线程中移除数据库连接、重置数据库连接、如果是新连接,则释放连接}
				cleanupTransactionInfo(txInfo);
			}
            // 如果正常,将提交事务,返回对象
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
					try {
						return invocation.proceedWithInvocation();
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});

				// Check result state: It might indicate a Throwable to rethrow.
				if (throwableHolder.throwable != null) {
					throw throwableHolder.throwable;
				}
				return result;
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}
		}
	}

 determineTransactionManager:获取事务管理器

protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
   // Do not attempt to lookup tx manager if no tx attributes are set
   if (txAttr == null || this.beanFactory == null) {
      return getTransactionManager();
   }

   String qualifier = txAttr.getQualifier();
// 如果事务注解@Transactional(transactionManager = "") 有值就通过名字获取
   if (StringUtils.hasText(qualifier)) {
      return determineQualifiedTransactionManager(this.beanFactory, qualifier);
   }
   else if (StringUtils.hasText(this.transactionManagerBeanName)) {
      return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
   }
   else {
        // 没有就获取默认的
      PlatformTransactionManager defaultTransactionManager = getTransactionManager();
      if (defaultTransactionManager == null) {
         defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
         if (defaultTransactionManager == null) {
            defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
            this.transactionManagerCache.putIfAbsent(
                  DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
         }
      }
      return defaultTransactionManager;
   }
}
private PlatformTransactionManager determineQualifiedTransactionManager(BeanFactory beanFactory, String qualifier) {
   PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);
   if (txManager == null) {
      txManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
            beanFactory, PlatformTransactionManager.class, qualifier);
      this.transactionManagerCache.putIfAbsent(qualifier, txManager);
   }
   return txManager;
}

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

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

相关文章

Dockerfile详解构建镜像

Dockerfile构建企业级镜像 在服务器上可以通过源码或rpm方式部署Nginx服务&#xff0c;但不利于大规模的部署。为提高效率&#xff0c;可以通过Dockerfile的方式将Nginx服务封装到镜像中&#xff0c;然后Docker基于镜像快速启动容器&#xff0c;实现服务的快速部署。 Dockerf…

统一网关 Gateway(黑马程序员)

网关的技术实现 在 SpringCloud 中网关的实现包括两种&#xff1a; gatewayzuul Zuul 是基于 Servlet 的实现&#xff0c;属于阻塞式编程。而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux &#xff0c; 属于响应式编程的实现&#xff0c;具备更好的性能。 网关的作…

火柴排队(c++实现)

题目 涵涵有两盒火柴&#xff0c;每盒装有 n 根火柴&#xff0c;每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列&#xff0c;同一列火柴的高度互不相同&#xff0c;两列火柴之间的距离定义为&#xff1a; 其中 ai 表示第一列火柴中第 i 个火柴的高度&#xf…

【OneAPI】贴纸生成API

OneAPI新接口发布&#xff1a;贴纸生成 生成一个10241024像素的贴纸。 API地址&#xff1a;POST https://oneapi.coderbox.cn/openapi/api/stickers 请求参数&#xff08;body&#xff09; 参数名类型必填含义说明prompt提示词是提示词示例&#xff1a;一只可爱的小狗 响应…

网络网络层之(1)IPv4地址

网络网络层之(1)IPv4地址 Author: Once Day Date: 2024年4月1日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

嵌入式系统初学者指南

什么是嵌入式系统&#xff1f; 嵌入式系统是一种独立的、基于微处理器的计算机系统。您可以将其视为大型系统的一部分的微型计算机。如今&#xff0c;从洗碗机到波音 747&#xff0c;几乎所有“电子”产品内部都有嵌入式系统。但是&#xff0c;嵌入式系统与笔记本电脑或手机不…

mysql dublewrite 双写缓存机制

mysql dublewrite 双写缓存机制&#xff0c;像不像主板双bois系统&#xff0c; 在MySQL的InnoDB存储引擎中&#xff0c;当进行数据写操作时&#xff0c;会先将数据写入到内存中的缓冲池&#xff08;Buffer Pool&#xff09;&#xff0c;然后异步刷新到磁盘上的数据文件。为了提…

基于巴法云物联网云平台构建可视化控制网页(以控制LED为例)

0 前言 如今大大小小的物联网云平台非常多&#xff0c;但大部分要收取费用&#xff0c;免费的物联网云平台功能则有很多限制使用起来非常不方便。以百度云物联网云平台为例&#xff0c;它的物可视不支持发布主题&#xff0c;等于可视化界面只能作为数据监控而不具备双向通信的…

打造个人高效图床系统:威联通NAS+兰空+PicGo全方位整合教程

1.图床选择 最近因为家里人有使用图床的需求&#xff0c;又担心第三方图床跑路导致数据丢失&#xff0c;恰好家里有个威联通NAS&#xff0c;还有公网IP和域名&#xff0c;既然如此&#xff0c;那就动手自建一个图床吧&#xff0c;毕竟开源的图床应用还是有很多的。 一上来就在…

掌握数据相关性新利器:基于R、Python的Copula变量相关性分析及AI大模型应用探索

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

写JDBC遇到的问题

执行会出现以下错误信息 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ? and loginPwd ? at line 1 at com.mysql.cj.jdbc.exceptions…

spark3.x新特性

Adaptive Query Execution自适应查询(SparkSQL) 由于缺乏或者不准确的数据统计信息&#xff08;元数据&#xff09;和对成本的错误估算&#xff08;执行计划调度&#xff09;导致生成的初始执行计划不理想 在Spark3.x版本提供Adaptive Query Execution自适应查询技术 通过在”…

数据结构顺序表的初始化,头插,尾插,头删,尾删,指定位置删除,指定位置插入,查找,销毁(详解)

目录 前言顺序表的介绍静态顺序表动态顺序表一.顺序表的初始化二.销毁扩容顺序表打印顺序表三.头插四.尾插五.头删六.尾删七.指定位置之前&#xff08;包括指定位置&#xff09;的插入八.指定位置数据的删除九.查找全部的代码实现总结 前言 数据结构是什么&#xff1f; 数据结…

碘浊度法与红外相机联用测定食品中维生素C

&#x1f31e;欢迎来到看论文的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年4月6日&…

1.微服务

一、微服务是什么 微服务是一种架构风格&#xff0c;即&#xff0c;一个应用应该是一组小型服务&#xff0c;每个服务器只负责一种服务&#xff0c;服务之间可以通过 HTTP 的方式进行互通。每一个功能元素最终都是一个可独立替换和独立升级的软件单元。 可以说&#xff0c;微…

网络编程套接字应用分享【Linux C/C++ 】【UDP应用 | TCP应用 | TCP线程池小项目】

目录 前提知识 1. 理解源ip&#xff0c;目的ip和Macip 2. 端口号 3. 初识TCP&#xff0c;UDP协议 4. 网络字节序 5. socket 编程 sockaddr类型 一&#xff0c;基于udp协议编程 1. socket——创建套接字 2. bind——将套接字强绑定 3. recvfrom——接受数据 4. s…

c++11的重要特性2

可变参数模板在3中。 目录 ​编辑 1、统一的列表初始化&#xff1a; std::initializer_list&#xff1a; std::initializer_list是什么类型&#xff1a; std::initializer_list使用场景&#xff1a; 让模拟实现的vector也支持{}初始化和赋值 2、声明 auto decltype nul…

「每日跟读」英语常用句型公式 第4篇

「每日跟读」英语常用句型公式 第4篇 1. I’ve decided to ____ 我决定要____了 I’ve decided to take a vacation (我决定要去度假) I’ve decided to change my lifestyle (我决定要改变我的生活方式) I’ve decided to adopt a dog (我决定要收养一条狗了) I’ve dec…

【深度学习环境配置】一文弄懂cuda,cudnn,NVIDIA Driver version,cudatoolkit的关系

【深度学习环境配置】一文弄懂cuda&#xff0c;cuDNN&#xff0c;NVIDIA Driver version&#xff0c;cudatoolkit的关系 NVIDIA Driver version&#xff08;NVIDIA驱动程序&#xff09;CUDAcuDNNcudatoolkit深度学习环境配置顺序 今天突然发现配置的环境有些问题&#xff0c;意…

使用阿里云试用Elasticsearch学习:2.6 深入搜索——控制相关度

处理结构化数据&#xff08;比如&#xff1a;时间、数字、字符串、枚举&#xff09;的数据库&#xff0c;只需检查文档&#xff08;或关系数据库里的行&#xff09;是否与查询匹配。 布尔的是/非匹配是全文搜索的基础&#xff0c;但不止如此&#xff0c;我们还要知道每个文档与…