Spring 之 @Component 和 @Configuration 两者区别以及源码分析

news2025/2/3 7:13:27

之前一直搞不清 @Component 和 @Configuration 这两个注解到底有啥区别,一直认为被这两修饰的类可以被 Spring 实例化嘛,不,还是见识太短,直到今天才发现这两玩意有这么大区别。很幸运能够及时发现,后面可以少走点坑,下面就直接通过最简单的案例来说明它两的区别,颠覆你的认知。

1、案例演示

定义一个 Apple 实体类,通过 @Bean 的方式交给 Spring 管理,如下:

public class Apple {

}

在定义一个 AppleFactory 工厂类也可以获取到 Apple 类实例,如下:


public class AppleFactory {

	private Apple apple;

	public Apple getApple() {
		return apple;
	}

	public void setApple(Apple apple) {
		this.apple = apple;
	}
}

在定义一个 AppleAutoConfiguration 类,此时先用 @Configuration 注解修饰该类,如下:


@Configuration
public class AppleAutoConfiguration {

	@Bean
	public Apple apple() {
		return new Apple();
	}

	@Bean
	public AppleFactory appleFactory() {
		AppleFactory appleFactory = new AppleFactory();

		appleFactory.setApple(apple());
		return appleFactory;
	}
}

先来分析下 AppleAutoConfiguration 类中的方法,apple() 方法中直接通过 new 关键字创建了一个 Apple 对象,这个没啥问题

继续看到 appleFactory() 方法内部,又调用了 apple() 方法,此时仔细想想,Spring 是不是调用了两次 apple() ,第一次是 Spring 扫描到 @Bean 注解调用一次,第二次是在 Spring 扫描到 @Bean 注解调用 appleFactory() 方法,appleFactory() 方法中又调用一次 apple() 方法,调两次 new 创建对象,必然会出现两个不一样的 Apple 对象,这样必然违背了 Spring 单例设计思想。

接下来测试下结果,看下是不是和我们想象的结果一样呢?测试如下:


@ComponentScan
public class ComponentConfigurationTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentConfigurationTest.class);


		AppleFactory appleFactory = context.getBean(AppleFactory.class);
		Apple apple = appleFactory.getApple();
		System.out.println("从 AppleFactory 获取的 apple = " + apple);


		Apple bean = context.getBean(Apple.class);
		System.out.println("从 Spring 容器中获取的 apple = " + bean);
	}
}

最终输出结果如下:

AppleFactory 获取的 apple = com.gwm.configurationanno.Apple@2f465398Spring 容器中获取的 apple = com.gwm.configurationanno.Apple@2f465398

发现这结果和我们刚刚的猜想不一样啊,其实大家隐约应该猜到,是因为这里使用的是 @Configuration 注解,所以这里最终你调用多少次最终都是同一个对象,原理稍后分析。

那么接下来肯定是要把 @Configuration 注解替换成 @Component 试试,修改之后的代码如下:


@Component
public class AppleAutoConfiguration {

	@Bean
	public Apple apple() {
		return new Apple();
	}

	@Bean
	public AppleFactory appleFactory() {
		AppleFactory appleFactory = new AppleFactory();

		appleFactory.setApple(apple());
		return appleFactory;
	}
}

测试结果如下:

AppleFactory 获取的 apple = com.gwm.configurationanno.Apple@2f465398Spring 容器中获取的 apple = com.gwm.configurationanno.Apple@2f465353

最终发现这个结果和我们在前面想象的结果一模一样,果然就创建了两个 Apple 对象,违背了 Spring 单例设计思想,这个区别非常非常的重要,因为在 SpringBoot 中为什么配置类都是使用的 @Configuration 注解,并不是直接使用 @Component 注解,这个原因想必大家应该也知道了。

上面这个例子在 SpringBoot 中有非常多的应用,在来看下另一个例子,如下:

定义一个 Orange 实体类,通过 FactoryBean 接口将 Orange 类交给 Spring 管理,如下:

public class Orange {

}

再定义个 OrangeFactoryBean 类实现 FactoryBean 接口,调用 getObject() 方法可以创建 Orange 对象,如下:

public class OrangeFactoryBean implements FactoryBean<Orange> {
	@Override
	public Orange getObject() throws Exception {
		return new Orange();
	}

	@Override
	public Class<?> getObjectType() {
		return Orange.class;
	}
}

大家都知道实现了 FactoryBean 接口的类最终会去调用 getObject() 方法然后创建对象。然后再在 AppleAutoConfiguration 类中调用 getObject() 方法,这里直接使用 @Component 注解演示,因为 @Configuration 注解肯定是没问题的,如下:


@Component
public class AppleAutoConfiguration {

	@Bean
	public Apple apple() {
		return new Apple();
	}

	@Bean
	public AppleFactory appleFactory() throws Exception {
		AppleFactory appleFactory = new AppleFactory();

		appleFactory.setApple(apple());
		
		OrangeFactoryBean orangeFactoryBean = orangeFactoryBean();
		System.out.println("appleFactory orangeFactoryBean = " + orangeFactoryBean);
		Orange orange = orangeFactoryBean.getObject();
		System.out.println("appleFactory orange="+orange);
		return appleFactory;
	}

	@Bean
	public OrangeFactoryBean orangeFactoryBean() {
		return new OrangeFactoryBean();
	}
}

这里通过 @Bean 注入 OrangeFactoryBean 实例,因为 AppleAutoConfiguration 类被 @Component 修饰,所以这里 Spring 和 手动调用两次创建的 OrangeFactoryBean 不一样,导致 getObject() 其实也是不一样的。

测试代码如下:


@ComponentScan
public class ComponentConfigurationTest {
	public static void main(String[] args) throws Exception {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentConfigurationTest.class);


		AppleFactory appleFactory = context.getBean(AppleFactory.class);
		Apple apple = appleFactory.getApple();
		System.out.println("从 AppleFactory 获取的 apple = " + apple);


		Apple bean = context.getBean(Apple.class);
		System.out.println("从 Spring 容器中获取的 apple = " + bean);


		Orange bean1 = context.getBean(Orange.class);
		System.out.println("从 Spring 容器中获取的 orange = "+bean1);

		Object bean2 = context.getBean("orangeFactoryBean");
		System.out.println("=>从 Spring 容器中获取的 orange = " + bean2);

		OrangeFactoryBean bean3 = context.getBean(OrangeFactoryBean.class);
		System.out.println("bean3 = " + bean3);
	}
}

输出结果如下:


appleFactory orangeFactoryBean = com.gwm.configurationanno.OrangeFactoryBean@c0c2f8d
appleFactory orange=com.gwm.configurationanno.Orange@305b7c14AppleFactory 获取的 apple = com.gwm.configurationanno.Apple@484970b0Spring 容器中获取的 apple = com.gwm.configurationanno.Apple@4470f8a6Spring 容器中获取的 orange = com.gwm.configurationanno.Orange@7c83dc97
=>Spring 容器中获取的 orange = com.gwm.configurationanno.Orange@7c83dc97
Spring 容器中的 orangeFactoryBean = com.gwm.configurationanno.OrangeFactoryBean@7748410a

通过以上两个案例可以清楚的知道 @Configuration 和 @Component 注解的区别,那么这个底层是怎么实现的呢?

2、源码解析

2.1、@Configuration 简单思路分析

这里我们可以大致的思考下,产生多个实例无非就是没有从 Spring 缓存中取值嘛,如果都是从 Spring 缓存中取值,那么必然就不会出现那么多对象。

对于 apple() 方法因为被 @Bean 修饰,所以在 Spring 实例化过程中会被调用 ,然后创建完实例将其放到 Spring 单例缓冲池中,那么下次就可以直接从缓存中获取到 Apple 实例(@Bean 注入 bean 流程看另一篇文章)。

对于 appleFactory() 方法中去调用 apple() 方法,apple() 方法又会 new 一个新的 Apple 实例,那么怎么样避免它重复创建对象呢?是不是可以通过代理改变 apple() 方法内部的逻辑,改成让它直接从 Spring 缓冲池中获取 Apple 的实例,这样就避免了重复创建对象。

如果要把 apple() 方法改成代理方法,是不是需要将所在的类 AppleAutoConfiguration 变成代理对象即可,Spring 就是这样干的,加上 @Configuration 注解标识之后,Spring 就会通过 cglib 代理创建 AppleAutoConfiguration 实例,所以你在 Spring 容器中获取 AppleAutoConfiguration 类是一个代理类,并不是真正的实体类。

跟着上述思路再去看源码,就简单多了。

2.2、@Configuration 源码分析之普通类型方法调用

首先看 ConfigurationClassPostProcessor 类解析 @Configuration 注解的地方,会先做个 full 标记,源码如下:

在这里插入图片描述
在这里插入图片描述

标记做好了之后,后面就可以通过判断是否有这个标记,然后要不要用代理创建实例,进入到 ConfigurationClassPostProcessor 类的 postProcessBeanFactory() 方法,源码如下:

在这里插入图片描述

这里会去判断当前 beanClass 是否有 full 标记,有的话就加入到 configBeanDefs 容器中,准备要用代理方式生成实例 bean。

在这里插入图片描述

然后开始遍历 configBeanDefs 容器,通过 ConfigurationClassEnhancer 对象挨个创建代理类,这里增强的是 Class 字节码文件,其实工作中这种方法也是可以借鉴的。

在这里插入图片描述

其中增强逻辑都放在了拦截器中(BeanMethodInterceptorBeanFactoryAwareMethodInterceptor) 源码如下:

从这两个拦截器侧面说明 @Configuration 和 @Bean 才是老搭档,干活不累,紧密相连,相辅相成,所以你在使用 @Bean 的时候,最好在外层类上标注 @Configuration,不要使用 @Component 注解。

在这里插入图片描述在这里插入图片描述

也就是说当你触发了目标方法的调用时,就会回调到这两个拦截器链,但是具体执行哪个拦截器是需要条件的,BeanMethodInterceptor 拦截器的条件需要,如下所示:

在这里插入图片描述在这里插入图片描述

BeanFactoryAwareMethodInterceptor 拦截器需要条件,源码如下:

在这里插入图片描述

我们这里满足 BeanMethodInterceptor 拦截器的执行条件,所以 Spring 在调用 apple() 方法的时候,会触发 BeanMethodInterceptor 拦截器增强逻辑的执行,增强逻辑如下:

在这里插入图片描述

这里有一个判断 isCurrentlyInvokedFactoryMethod() 非常关键,因为这个开关控制着你是否会多次创建实例,进入该方法内部,源码如下:

在这里插入图片描述在这里插入图片描述在这里插入图片描述

可以发现这里有一个 ThreadLocal 类型的容器,那么这个容器什么时候会有值呢?这个就要需要你对 @Bean 的实例化流程非常了解了,这里简单摘取核心部分,源码如下:

在这里插入图片描述在这里插入图片描述在这里插入图片描述

在 @Bean 实例化流程的时候就用到了这个 currentlyInvokedFactoryMethod 容器,先把值放进去,然后反射调用完方法之后又删除。其实这只是一个标记作用。

Spring 在调用 @Bean 修饰的 apple() 方法时,currentlyInvokedFactoryMethod 容器中放的就是 apple,然后通过 set() 反射调用 apple() 方法,注意此时的 AppleAutoConfiguration 是一个代理对象,所以调用 apple() 方法就会触发走切面逻辑,因为是 @Bean 修饰的,所以走的是 BeanMethodInterceptor 这个类的增强逻辑,源码如下:

在这里插入图片描述

注意此时的判断逻辑 isCurrentlyInvokedFactoryMethod() 是有值的哦,存的就是 apple,所以这里就直接走 if 逻辑,直接调用目标方法逻辑,直接 new Apple() 对象,然后返回到 set() 反射调用处,在删除 currentlyInvokedFactoryMethod 容器中的值 apple,然后就是 Spring 实例化 @Bean 的后续流程,最终会将这个 new 出来的实例放到 Spring 一级缓存中,源码如下:

在这里插入图片描述

那么重点来了,Spring 在执行 @Bean 修饰的 appleFactory() 方法时, isCurrentlyInvokedFactoryMethod 容器中那么就是存的 appleFactory,然后通过反射 set() 方法去调用 appleFactory() 方法,然后再 appleFactory() 方法中执行逻辑时发现又调用了 apple() 方法,那么又会触发进入 apple() 方法的增强逻辑 BeanMethodInterceptor,源码如下:

在这里插入图片描述

注意此时的 isCurrentlyInvokedFactoryMethod() 判断逻辑,当前入参 beanMethod 是 apple,但是 isCurrentlyInvokedFactoryMethod 容器中刚刚存放的是外面方法 appleFactory() 的值,所以这里 isCurrentlyInvokedFactoryMethod() 方法判断条件不成立,走 resolveBeanReference() 逻辑,源码如下:

在这里插入图片描述

这里面这段逻辑会触发 getBean() 流程,此时 getBean() 流程去获取 Apple 类实例,肯定是从单例缓冲池中获取得,因为之前在执行 @Bean 修饰的 apple() 方法就已将实例存入到了 Spring 的一级缓存中,所以在 appleFactory() 方法中,不管你调用多少次,都不会重复创建 Apple 类实例,因为最终都是通过切面逻辑去调用 getBean() 从缓存中获取得,必然是同一个实例。

2.3、@Configuration 源码分析之 FactoryBean 类型方法调用

Apple 是一个普通类,上面已经解析完,现在来解析一下实现 FactoryBean 接口的 OrangeFactoryBean 特殊一点类型的看 Spring 又是如何处理的。


public class Orange {

}

public class OrangeFactoryBean implements FactoryBean<Orange> {
	@Override
	public Orange getObject() throws Exception {
		return new Orange();
	}

	@Override
	public Class<?> getObjectType() {
		return Orange.class;
	}
}


@Configuration
public class AppleAutoConfiguration {

	@Bean
	public AppleFactory appleFactory() throws Exception {
		OrangeFactoryBean orangeFactoryBean = orangeFactoryBean();
		System.out.println("appleFactory orangeFactoryBean = " + orangeFactoryBean);
		Orange orange = orangeFactoryBean.getObject();
		System.out.println("appleFactory orange="+orange);

		return appleFactory;
	}

	@Bean
	public OrangeFactoryBean orangeFactoryBean() {
		return new OrangeFactoryBean();
	}
}

其实只需要看切面逻辑就可以,AppleAutoConfiguration 类的实例是一个代理对象,在调用 appleFactory() 方法里面调用 orangeFactoryBean() 方法时会触发进入切面逻辑(BeanMethodInterceptor),因为 OrangeFactoryBean 这个类有点特殊,实现了 FactoryBean 接口,所以在切面逻辑(BeanMethodInterceptor)实现会有一点不一样,源码如下:

在这里插入图片描述

从上面源码分析,当在 appleFactory() 方法中调用 orangeFactoryBean() 方法时会触发进入 BeanMethodInterceptor 切面逻辑,然后在切面中会去判断是否是 FactoryBean 接口类型,恰好 OrangeFactoryBean 就是 FactoryBean 类型,所以会直接调用 getBean() 流程,此时注意,beanName 是包含了 & 符号,表示是需要实例化 OrangeFactoryBean 类,这个特别注意。因为不带 & 符号的话会调用 getObject() 方法创建 Orange 实例。注意这里是包含了 & 符号,是要去创建 OrangeFactoryBean 实例。

当调用 getBean() 去创建 OrangeFactoryBean 实例时,因为 AppleAutoConfiguration 类是一个代理类,所以在调用 AppleAutoConfiguration 类中调用 orangeFactoryBean() 方法创建 OrangeFactoryBean 实例时会又会触发切面逻辑,又会走上面的逻辑,但是注意注意注意注意此时的 beanName 是不带 & 符号的哦,此时的 beanName 就是方法名称,所以此时就会进入 else 逻辑进入 enhanceFactoryBean() 方法中,源码如下:

在这里插入图片描述

从源码中可以看到又是通过 cglib 创建了一个 OrangeFactoryBean 的代理对象,注意这里的拦截器逻辑,只有当你调用了 OrangeFactoryBean 类中的 getObject() 方法才会做特殊增强,去调用 getBean() 逻辑,同时 注意 beanName 是不带 & 符号的,也就是去创建 Orange 类实例,除了 getObject() 方法之外的所有方法不做任何处理,直接进回调即可。至此 OrangeFactoryBean 类实例已经创建好了,在 Spring 容器中是一个代理类。

然后再看到我们自己的代码如下:


@Configuration
public class AppleAutoConfiguration {

	@Bean
	public AppleFactory appleFactory() throws Exception {
		OrangeFactoryBean orangeFactoryBean = orangeFactoryBean();
		System.out.println("appleFactory orangeFactoryBean = " + orangeFactoryBean);
		Orange orange = orangeFactoryBean.getObject();
		System.out.println("appleFactory orange="+orange);

		return appleFactory;
	}

	@Bean
	public OrangeFactoryBean orangeFactoryBean() {
		return new OrangeFactoryBean();
	}
}

刚才已执行到第一行代码,执行完后,获取到一个 OrangeFactoryBean 代理对象,然后开始执行第二行代码,注意这里隐式调用了 toString() 方法,会触发 OrangeFactoryBean 代理类的切面逻辑,而 OrangeFactoryBean 代理类只对 getObject() 方法有特殊处理,其他的方法都不做处理,就是直接回调而已,所以 toString() 的切面逻辑不用太在乎。

接下来执行第三行代码,调用 getObject() 方法,getObject() 方法是 OrangeFactoryBean 代理类非常关心的方法,在源码中写死要对 getObject() 方法进行处理。最终会调用到 getBean(orange) 流程,实例化 Orange bean,最终将 Orange 实例放入到 Spring 一级缓存。

所以最终在 appleFactory() 方法中执行 Orange orange = orangeFactoryBean.getObject() 代码和在测试类中执行 getBean(Orange.class) 或者 getBean(“orangeFactoryBean”) 代码都是从同一个地方获取到的值(Spring 中的一级缓存中),虽然多个地方调用,表面上给人的感觉是调用了多次,会出现多个实例,但是 Spring 中用代理的方式从底层帮我们解决了这个问题。但是前提是要使用 @Configuration 注解才会生效。

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

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

相关文章

操作系统知识点

操作系统的目标&#xff1a; 方便&#xff1a;使计算机系统易用 有效&#xff1a;以更有效的方式使用计算机系统资源 扩展&#xff1a;方便用户有效开发、测试和引进新功能 操作系统的 作用&#xff1a; 1. 有效的管理资源 2.通过命令接口、编程接口等为用户提供各种…

【自然语言处理】【ChatGPT系列】Chain of Thought:从大模型中引导出推理能力

Chain-of-Thought Prompting&#xff1a;从大模型中引导出推理能力《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》论文地址&#xff1a;https://arxiv.org/pdf/2201.11903.pdf 相关博客 【自然语言处理】【ChatGPT系列】Chain of Thought&#xf…

什么是加权轮询?云解析DNS是否支持加权轮询?-中科三方

什么是加权轮询&#xff1f; 所谓的加权轮询算法&#xff0c;其实就是Weighted Round Robin&#xff0c;简称wrr。在我们配置Nginx的upstream的时候&#xff0c;带权重的轮询&#xff0c;其实就是wrr。 upstream backend { ip_hash; server 192.168.1.232 weight4; server 19…

无疫苗未吃药,48小时内阳康全纪实个案

为了不误导不同体质的人&#xff0c;特意强调这是个案&#xff0c;阳康方案仅供参考。小编体质偏寒&#xff0c;长期熬夜&#xff0c;黑白颠倒&#xff0c;有习惯性头痛症。经验总结&#xff1a;1.受到风寒是病发的直接导火索&#xff0c;就算携带病毒&#xff0c;本体没有受到…

Android实现红绿灯检测(含Android源码 可实时运行)

Android实现红绿灯检测(含Android源码 可实时运行) 目录 Android实现红绿灯检测(含Android源码 可实时运行) 1. 前言 2. 红绿灯检测数据集说明 3. 基于YOLOv5的红绿灯检测模型训练 4.红绿灯检测模型Android部署 &#xff08;1&#xff09; 将Pytorch模型转换ONNX模型 &…

吉林优美姿:抖音怎么增加销量?

为了更好的在做抖音好物联盟&#xff0c;那么一些抖音达人也会想方设法的去报名申请&#xff0c;那么大家是否真的清楚这个报名要怎么做呢&#xff1f;具体有什么要求呢&#xff1f;跟着吉林优美姿小编来一起看看吧&#xff01; 注册要求&#xff1a;凡注册抖音 APP并开通产品分…

大数据培训Impala之存储和压缩

注&#xff1a;impala不支持ORC格式 1.创建parquet格式的表并插入数据进行查询 [hadoop104:21000] > create table student2(id int, name string) > row format delimited > fields terminated by ‘\t’ > stored as PARQUET; [hadoop104:21000] > insert …

工控CTF之协议分析9——其他协议

协议分析 流量分析 主要以工控流量和恶意流量为主&#xff0c;难度较低的题目主要考察Wireshark使用和找规律&#xff0c;难度较高的题目主要考察协议定义和特征 简单只能简单得干篇一律&#xff0c;难可以难得五花八门 常见的工控协议有&#xff1a;Modbus、MMS、IEC60870、…

【计算机视觉】回顾2022年计算机视觉领域最激动人心的进展

目录&#xff1a;回顾2022年计算机视觉一、前言二、计算机视觉趋势2.1 Transformer统治计算机视觉2.2 以数据为中心的计算机视觉获得牵引力2.3 AI 生成的艺术作品2.4 多模态人工智能成熟三、计算机视觉的新应用3.1 运动领域3.2 环境保护3.3 自动驾驶3.4 健康与医药四、总结一、…

数据类型隐式转换导致的阻塞

背景 不合适的数据类型隐式转换会导致性能下降和并发下降&#xff0c;而且大多数技术人员对隐式转换的概念是陌生的&#xff0c;因此在生产环境中非常常见&#xff0c;通过本文做一个系统的梳理。 现象 收到SQL专家云阻塞告警邮件&#xff0c;登录SQL专家云&#xff0c;进入实…

推荐系统学习笔记-冷启动

简介 推荐系统的主要目标是将大量的标的物推荐给可能喜欢的海量用户, 这里涉及到标的物和用户两类对象。任何互联网推荐产品, 标的物和用户都是不断增长变化的&#xff0c;所以一定会频繁面对新标的物和新用户, 推荐系统冷启动问题指的就是对于新注册的用户或者新入库的标的物…

CDGA|2022年内有超20家银行因数据治理模块受罚,原因都在这里

今年&#xff0c;银保监会对银行数据治理的监管趋严&#xff0c;对银行机构在监管数据质量和数据报送中存在的违法违规行为&#xff0c;不断加大处罚与整治力度。 近日&#xff0c;北京农商银行收到的一张630万元罚单显示&#xff0c;该行主要存在的违法违规事实具体为&#xf…

NVMe解读

看NVMe协议&#xff08;1.0e&#xff09;过程中&#xff0c;参考了SSDFans的很多文章内容&#xff0c; 目录 1. 综述 3 1.1 名词解释 3 1.1.1 Namespace 3 1.1.2 Fused Operations 4 1.1.3 指令执行顺序 4 1.1.4 写单元的原子性 4 1.1.5 元数据 4 1.1.6 仲裁机制 4 1…

图数据库知识点3:图数据库解决了什么问题?

在前面的两个知识点中我们先后介绍了&#xff1a; 知识点1&#xff1a;图数据库与关系型数据库的区别 知识点2&#xff1a;图思维方式 现在,我们可以更进一步来通过具体的例子来了解图数据库、图计算到底解决了什么问题。我们先来剖析下面这个问题&#xff1a; 图数据库查询…

【Vue实用功能】elementUI 自定义表单模板组件

elementUI 实现一个自定义的表单模板组件 注&#xff1a;该功能基于elementUI 背景&#xff1a;在项目开发中&#xff0c;我们会遇到这种需求&#xff0c;在管理后台添加自定义表单&#xff0c;在指定的页面使用定义好的表单 直接上代码&#xff1a; <template><di…

DBCO-PEG-NHS, 可溶于水,有多种分子量供选择

凯新生物DBCO-PEG-NHS衍生物可以点击化学反应不需要任何金属催化剂。反应促进1,3-偶极环加成反应&#xff0c;环辛炔和叠氮化合物&#xff0c;也被称为铜自由点击反应&#xff0c;是一种生物正交反应使溶液中的两个分子的共轭。DBCO PEG衍生物具有缓冲溶液中快速的动力学和稳定…

C语言基础【程序=算法+数据结构】——(数据结构——基础篇)

欢迎小伙伴的点评✨✨ 本篇章系列是对C语言的深度思考和总结、关于C语言内容会持续更新。 文章目录✨前言✨数据的表现形式✨数据类型一、基本类型1.1、整型类型1.1.1、基本整型(int)1.1.2、短整型(short int)1.1.3、长整型(long int)1.1.4、*双长整型(long long int)1.1.5、字…

1556:Dis——Tarjan求LCA、倍增求LCA

【题目描述】 给出 n 个点的一棵树&#xff0c;多次询问两点之间的最短距离。 注意&#xff1a;边是双向的。 【输入】 第一行为两个整数 n 和 m。n 表示点数&#xff0c;m 表示询问次数&#xff1b; 下来 n−1 行&#xff0c;每行三个整数 x,y,k&#xff0c;表示点 x 和点 …

英文计算机论文写作,需要注意哪些细节? - 易智编译EaseEditing

&#xff08;1&#xff09;尽量用动词少用名词化 那些大篇名词的文章真的很难读懂&#xff0c;而有强大动词的句子更容易理解。 我们注意到客户在论文中经常使用的名词有“agreement”, “disagreement”, “investigation”, “analysis”, “examination”, “comparison”…

Qt 中的多项目管理

背景&#xff1a; 在Visual Studio中使用 “Solution” 来组织多个 Projects。 在Qt中&#xff0c;使用 "Subdirs Project"来组成多个projects &#xff0c;实现VS中的Solution功能。 项目类型 首先是先新建一个子目录项目&#xff0c;再根据需要在子目录项目中添…