【Spring源码核心篇-01】精通Spring的bean的生命周期

news2024/11/24 2:10:20

Spring源码核心篇整体栏目


内容链接地址
【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012

spring的bean的生命周期

  • 一,spring中bean的生命周期
    • 1,生成BeanDefinition
      • 1.1,初始化context和BeanFactory
      • 1.2,核心doScan方法
      • 1.3,获取BeanDefinition
      • 1.4,BeanDefinition属性赋值
      • 1.5,Bedefinition存入BeanDefinitionMap
    • 2,Bean工厂加载bean
      • 2.1,实例化全部的单例bean
      • 2.2,doGetBean
      • 2.3,createBean
        • 2.3.1,bean的前置处理器(实例化前)
        • 2.3.2,实例化bean
        • 2.3.3,bean的后置处理器(实例化后)
        • 2.3.4,属性填充
        • 2.3.5,初始化
      • 2.4,销毁

如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/143441012

一,spring中bean的生命周期

在本人前面的文章中,也写过一些关于spring源码的一些核心方法,主要是对spring中refresh中的九个方法进行的一些源码剖析,接下来的spring系列主要为核心篇,就是疏通spring的主干和脉络,通过spring的主干来了解spring的IOC和aop。源码依旧可以选择5.2的版本: https://github.com/spring-projects/spring-framework/tree/5.2.x

本文主要的内容就是了解spring的IOC中,bean的什么周期以及流程是怎么样的,结合bean的生命周期,了解一些spring中的一些扩展点机制,如bean的前置处理器,后置处理器,bean实例化前能做的事情,bean实例化后做的事情,bean初始化前做的事情,bean初始化后做的事情等

本人通过读取一下源码锁总结的流程图如下:

在这里插入图片描述

1,生成BeanDefinition

首先第一步是生成一个Bean定义,如何理解这个Bean定义,就是类似于我要做一个家具,在做之前需要找一个加工厂帮忙做好,但是加工厂并不知道要做成什么样子,因此就需要我们定义好一份图纸交给工厂,那么这个图纸就是所谓的BeanDefinition,然后这个工厂就是对应的spring容器

1.1,初始化context和BeanFactory

在spring中,主要有注解的方式和xml启动上下文,这里直接选用注解的方式启动上下文

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();

这个对象的继承关系如下,继承了 GenericApplicationContext 类,实现了 AnnotationConfigRegistry 接口

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {}

在初始化这个 AnnotationConfigApplicationContext 对象之后,可以发现这个对象是 BeanFactory 的一个实现类,也就是说在初始化这个context上下文之前,还会先初始化一个 BeanFactory 的工厂,并且BeanFactory工厂有的功能context上下文都有,context就是一个BeanFactory的一个具体实现

在这里插入图片描述

接下来以下面这个包路径的源码进行分析,如分析一段service包下面的所有注解是如何注册到spring中生产bean对象,AnnotationConfigApplicationContext 其他的几个构造方法可以先不看

public AnnotationConfigApplicationContext(String... basePackages) {
	this();		//注册一个读取器和扫描器,也可以先不看
	scan(basePackages);	//扫描包路径
	refresh();			//spring中最核心的方法,refresh方法
}

1.2,核心doScan方法

然后直接分析这个scan方法,一直往下点,可以看到一个 doScan 方法,就是扫描的核心方法,然后会循环的寻找包路径集合,最后回去找每一个路径下面的有 @Component 注解的bean,然后将bean生成一个 BeanDefinition

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	//创建bean定义的holder对象用于保存扫描后生成的bean定义对象
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	//循环我们的包路径集合
	for (String basePackage : basePackages) {
		//找到候选的Components
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			//拿到元数据解析器,可以获取类中所有信息
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			//设置我们的beanName
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			//这是默认配置 autowire-candidate
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			//获取@Lazy @DependsOn等注解的数据设置到BeanDefinition中
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			//把我们解析出来的组件bean定义注册到我们的IOC容器中(容器中没有才注册)
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

1.3,获取BeanDefinition

可以直接查看这个 findCandidateComponents 方法时如何获取到Bean定义的,内部主要会通过一个 scanCandidateComponents 方法来具体的扫描这些带有@Component注解的主键,如下图所示

如一个com.zhs.service的包下面有一个OrderService的类:

  • 这个方法首先会获取到这个包路径为 com.zhs.service
  • 然后获取到该包下面的全部带有 .class 类文件,将这个OrderService类封装成一个Resource资源
  • 遍历每个资源,判断是否在includeFilters中或者excludeFilters中,Component在容器启动已经加入到includeFilters中,所以有这个注解的会生成一个BeanDefinition被选中加入到集合中

在这里插入图片描述

这里还有一个细节,就是在下面这行代码中,设置到这个Bean的ClassName还是这个类的名字,而不是一个对象,在后面实例化之后那么设置的就是一个Object的对象了,并且这个 beanClass 原本就是一个Object的对象,因为此时在容器启动时,目前还没有真正的bean对象被实例化

@Nullable
private volatile Object beanClass;	//原因是目前只扫描到了,但是并没有加载

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);

public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
	this.metadata = metadataReader.getAnnotationMetadata();
	setBeanClassName(this.metadata.getClassName());
}

1.4,BeanDefinition属性赋值

在上面虽然获取到了BeanDefinition,但是这个bean定义里面只有名字,还需要给BeanDefinition设置一些基础属性,如是否懒加载,是否单例bean

在拿到 BeanDefinition 的set集合之后,会遍历这个set集合,为里面每一个bean定义进行属性赋值,首先是获取到每个bean定义的元数据解析器,可以获取到每个bean定义的所有属性,然后设置bean定义作用域

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());

然后就是判断类是否懒加载,是否加了 @Dependson注解等,Dependson注解主要用于依赖关系,比如A上面加了一个依赖B的注解,那么在加载A实例前需要先加载B实例,这种方式造成的循环依赖解决不了,只能手动的避免

if (candidate instanceof AnnotatedBeanDefinition) {					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

在这里插入图片描述

1.5,Bedefinition存入BeanDefinitionMap

最后会先判断当前bean定义是否已经存在于容器中,如果不存在的话,把我们解析出来的组件bean定义注册到spring的IOC容器中

if (checkCandidate(beanName, candidate)) {
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
	definitionHolder =
			AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	beanDefinitions.add(definitionHolder);
	registerBeanDefinition(definitionHolder, this.registry);
}

最后直接进入到这个 registerBeanDefinition 方法中,可以发现是直接通过一个工具类进行了注册

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);

最后将这个BeanDefinition注册到BeanDefinitionMap

在这里插入图片描述

2,Bean工厂加载bean

上面说了beanDefinition是一张图纸,spring是一个加工厂,那么接下来就需要通过工厂将图纸生成对应的家具,就是需要通过spring将beanDefinitionMap中的全部beanDefinition生成对应的bean

2.1,实例化全部的单例bean

在通过scan扫描方法将全部的beanDefinition拿到之后,接下来的事情就是遍历全部的BeanDefinition,然后将BeanDefinition交给spring的bean工厂,直接来直接分析 refresh 中的初始化单例bean的方法

finishBeanFactoryInitialization(beanFactory);

在这个方法中,会有一个专门实例化单例bean的方法 preInstantiateSingletons

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    //实例化剩余的单实例bean
	beanFactory.preInstantiateSingletons();
}

在这个实例化单例bean的方法中,会遍历全部扫描拿到的单例beanDefinition,做以下流程:

  • 先判断这个beanDefinition是否为抽象的、单例的或者是否为懒加载的
  • 再次判断这个beanDefinition是否为工厂bean,是的话则需要特殊处理转化成对应的bean
  • 最后调用这个getBean方法去创建对应的实例,有这个实例则直接获取,无则创建

在这里插入图片描述

2.2,doGetBean

接下来就是进入bean工厂创建bean的方法,在进入上面的getBean方法之后,里面可以看到这个真正去创建bean的方法 doGetBean 方法

@Override
public Object getBean(String name) throws BeansException {
	//真正的获取bean的逻辑
	return doGetBean(name, null, null, false);
}

如下图所示,其部分流程如下:

  • 首先第一步就是获取到这个bean的别名,如果有别名则获取到别名,没有则用当前bean的名字
  • 随后去单例池的缓存中获取对象,默认绝大多数的对象都是单例对象

在这里插入图片描述

首先刚进来这个单例池里面肯定是没有对象的,因为还未初始化放到单例池里面,因此看后面的else逻辑。下面这段代码的逻辑也比较简单,就是判断这个BeanDefinition是否存在于单前的BeanFactory中,如果不存在,那么就去父工厂的BeanFactory中去获取

BeanFactory parentBeanFactory = getParentBeanFactory();
//若存在父工厂,切当前的bean工厂不存在当前的bean定义,那么bean定义是存在于父beanFacotry中
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
	//获取bean的原始名称
	String nameToLookup = originalBeanName(name);
	//若为 AbstractBeanFactory 类型,委托父类处理
	if (parentBeanFactory instanceof AbstractBeanFactory) {
		return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
				nameToLookup, requiredType, args, typeCheckOnly);
	}
	else if (args != null) {
		//  委托给构造函数 getBean() 处理
		return (T) parentBeanFactory.getBean(nameToLookup, args);
	}
	else {
		// 没有 args,委托给标准的 getBean() 处理
		return parentBeanFactory.getBean(nameToLookup, requiredType);
	}
}

如果没有父BeanFactory的话,那么就会继续往下走,其流程如下

  • 首先会拿到合并的RootBeanDefinition,这个才是最终交给bean工厂加载的BeanDefinition
  • 随后就是会对这个合并的RootBeanDefinition进行检查,判断是否抽象的等
  • 随后会校验这个 dependsOn 注解,就是前面所讲到的依赖于谁的注解,如A依赖于B,那么在创建A之前则需要将B先创建出来,这个注解方式避免不了循环依赖,只能手动解决
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//检查当前创建的bean定义是不是抽象的bean定义
checkMergedBeanDefinition(mbd, beanName, args);
//依赖bean的名称
String[] dependsOn = mbd.getDependsOn();
	if (dependsOn != null) {
	// <1> 若给定的依赖 bean 已经注册为依赖给定的 bean
	// 即循环依赖的情况,抛出 BeanCreationException 异常
	for (String dep : dependsOn) {
		//beanName是当前正在创建的bean,dep是正在创建的bean的依赖的bean的名称
		if (isDependent(beanName, dep)) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
		}
		//保存的是依赖 beanName 之间的映射关系:依赖 beanName - > beanName 的集合
		registerDependentBean(dep, beanName);
		try {
			//获取depentceOn的bean
			getBean(dep);
		}
		catch (NoSuchBeanDefinitionException ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
		}
	}
}

2.3,createBean

在对上面的那些东西进行校验完成之后,接下来就是真正的进入创建单例bean的方法,在单例方法中,首先会判断是否为单例bean,然后最终调用这个 createBean 方法进行bean的创建

//创建单例bean
if (mbd.isSingleton()) {
	//把beanName 和一个singletonFactory 并且传入一个回调对象用于回调
	sharedInstance = getSingleton(beanName, () -> {
		try {
			//进入创建bean的逻辑
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
			destroySingleton(beanName);
			throw ex;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
2.3.1,bean的前置处理器(实例化前)

接下来直接进入这个创建bean的主角 createBean 方法,也就是bean生命周期最重要的一部分,就是在这个方法里面,在这个方法里面,首先会在真正的doCreateBean创建bean之前,创建一个bean的前置处理器,aop的实现就是在这里去进行扫描所有的@AespectJ注解,然后扫描类内部的@Before,@after等注解,最后进行一个动态代理,这一步就是实例化前的操作

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
	throws BeanCreationException {
     try {
         //bean定义的前置处理器
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	}   
}

在这个 resolveBeforeInstantiation中,可以在里面看到一下这段代码,将这个对应的切面解析并保存到缓存中

//bean的前置处理器
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
//bean的后置处理器
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

在这里插入图片描述

2.3.2,实例化bean

在上面的getBean方法中继续往下走,可以发现一个真正的去创建实例bean的doCreateBean方法

try {
	/**
	 * 该步骤是我们真正的创建我们的bean的实例对象的过程
	 */
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

方法如下图,首先会将这个Bean进行一个 BeanWrapper的包装,然后调用 createBeanInstance 进行bean的实例化,在进行实例化时,会对实例化的方式进行推断,如是否用构造方法、工厂方法等

instanceWrapper = createBeanInstance(beanName, mbd, args);

在这里插入图片描述

2.3.3,bean的后置处理器(实例化后)

在实例化之后,这里又会进行一个后置处理器的调用,这里会将一些 @AutoWired @Value 的注解进行预解析

synchronized (mbd.postProcessingLock) {
	if (!mbd.postProcessed) {
		try {
			//进行后置处理 @AutoWired @Value的注解的预解析
			applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Post-processing of merged bean definition failed", ex);
		}
		mbd.postProcessed = true;
	}
}
2.3.4,属性填充

接下来就是进入属性填充的阶段,里面通过调用这个 populateBean 方法进行属性填充

//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
populateBean(beanName, mbd, instanceWrapper);

在属性填充的方法中,会通过这个InstantiationAwareBeanPostProcessor类判断是否有一个 postProcessAfterInstantiation 的后置处理器,和实例化前的前置处理器功能类似,这种接口都是spring留给我们开发人员去自定义扩展的一些接口,改功能为实例化后,属性注入前的一个扩展点

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
	//获取容器中的所有的BeanPostProcessor
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
		//判断我们的后置处理器是不是InstantiationAwareBeanPostProcessor
		if (bp instanceof InstantiationAwareBeanPostProcessor) {
			//进行强制转化
			InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
			//若存在后置处理器给我们属性赋值了,那么返回false 可以来修改我们的开关变量,就不会走下面的逻辑了
			if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
				// 返回值为是否继续填充 bean
				// postProcessAfterInstantiation:如果应该在 bean上面设置属性则返回 true,否则返回 false
				// 一般情况下,应该是返回true 。
				// 返回 false 的话,将会阻止在此 Bean 实例上调用任何后续的 InstantiationAwareBeanPostProcessor 实
				continueWithPropertyPopulation = false;
				break;
			}
		}
	}
}

下面这段代码就是依赖注入的功能,判断注入的类型是通过type类型还是通过name的类型输入属性

if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
	//把PropertyValues封装成为MutablePropertyValues
	MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
	//根据bean的属性名称注入
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
		autowireByName(beanName, mbd, bw, newPvs);
	}
	//根据bean的类型进行注入
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
		autowireByType(beanName, mbd, bw, newPvs);
	}
	//把处理过的 属性覆盖原来的
	pvs = newPvs;
}

代码往下执行又可以看到一个InstantiationAwareBeanPostProcessor对象的 ,给开发人员一些扩展接口,用于给开发者在@Autoware等属性中提前进行初始赋值

在这里插入图片描述

2.3.5,初始化

最后一个步骤就是进行初始化,上面已经进行了对象的实例化,属性填充,那么最后一步就剩下初始化了。

//进行对象初始化操作(在这里可能生成代理对象)
exposedObject = initializeBean(beanName, exposedObject, mbd);

直接查看这个 initializeBean 方法,可以发现初始化方法中主要有四个步骤:

  • 一个是判断是否实现Aware接口的回调,比如是否实现 BeanNameAware、BeanFactoryAware 接口,还有一些环境的aware,以及Resource资源的aware,事件发布器的aware等
  • 初始化前会调用bean的后置处理器,用于加载一些 @PostConstruct 等注解
  • 然后就是进行调用初始化的方法,
  • 最后执行一个调用bean的后置处理器的一个方法,比如处理所有aop是否需要执行动态代理等

在这里插入图片描述

2.4,销毁

依旧是在这个doCreateBean方法的最后面,有一个销毁的方法 registerDisposableBeanIfNecessary

registerDisposableBeanIfNecessary(beanName, bean, mbd);

由于这一块用的相对较少,因此不做过多的分析

在这里插入图片描述

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

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

相关文章

IAR出现,Error [e12].Unable to open file "xxxx:_app.xcl"怎么办?

编译时出现&#xff0c;一般是拷贝过来出现这个问题。解决方法: 1&#xff0c;点击到最左边“code -Debug”上 2&#xff0c;点Project>>>options for node &#xff02;code&#xff02; 3&#xff0c;选项卡“linker”>>&#xff02;linker configuration fil…

C#与C++交互开发系列(十一):委托和函数指针传递

前言 在C#与C的互操作中&#xff0c;委托&#xff08;delegate&#xff09;和函数指针的传递是一个复杂但非常强大的功能。这可以实现从C回调C#方法&#xff0c;或者在C#中调用C函数指针的能力。无论是跨语言调用回调函数&#xff0c;还是在多线程、异步任务中使用委托&#x…

SpringBoot国际化:创建多语言支持的Web应用

SpringBoot国际化&#xff1a;创建多语言支持的Web应用 介绍 SpringBoot作为一个强大的框架&#xff0c;提供了便捷的国际化支持&#xff0c;使开发者能够轻松创建多语言支持的Web应用。通过使用SpringBoot的MessageSource&#xff0c;开发者可以在应用中实现动态的语言切换。…

如何快速搭建一个3D虚拟展厅?

随着元宇宙概念的兴起&#xff0c;一个全新的虚拟、立体数字空间正逐步成为我们生活的一部分。在这个空间里&#xff0c;用户可以沉浸其中&#xff0c;进行丰富的交互操作&#xff0c;体验前所未有的无限可能。而如何快速搭建一个属于自己的元宇宙3D虚拟展厅&#xff0c;正成为…

blender 小车建模 建模 学习笔记

一、学习blender视频教程链接 案例4&#xff1a;狂奔的小车_建模_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt4y1E7qn?p14&spm_id_from333.788.videopod.episodes&vd_sourced0ea58f1127eed138a4ba5421c577eb1 二、开始建模 &#xff08;1&#xff09;创…

强大的文本编辑器Notepad++8.4.6 最新版

Notepad最新版是一款多功能的代码编辑工具。Notepad官方版支持27种编程语言&#xff0c;涵盖C、C 、Java 、C#,、XML、 HTML,、PHP、python等等&#xff0c;能够帮助程序员提高编辑效率。Notepad软件支持python与sql代码高亮功能&#xff0c;并且免费开源&#xff0c;能够完美地…

彻底理解链表(LinkedList)结构

目录 比较操作结构封装单向链表实现面试题 循环链表实现 双向链表实现 链表&#xff08;Linked List&#xff09;是一种线性数据结构&#xff0c;由一组节点&#xff08;Node&#xff09;组成&#xff0c;每个节点包含两个部分&#xff1a;数据域&#xff08;存储数据&#xff…

小小猫棒onu替换家用光猫,薅运营商带宽羊毛,突破1000M

小小猫棒onu 一、总体步骤 1 记录原来光猫信息 主要包括SN&#xff0c;ploam密码&#xff0c;loid、loid密码、 mac、上网的vlan id等 一般gpon采用SN、ploam密码、SNploam密码三种中的一种认证方式 一般Epon采用loid&#xff08;逻辑id&#xff09;、mac、loid mac三种中…

【经典论文阅读11】ESMM模型——基于贝叶斯公式的CVR预估

传统的CVR模型&#xff08;也就是直接对conversion rate建模的模型&#xff09;在实际应用中面临两个问题&#xff08;样本选择偏差与数据稀疏性问题&#xff09;。为了解决这两个问题&#xff0c;本文提出ESMM模型。该模型巧妙地利用用户行为序列去建模这个问题&#xff0c;从…

二十八、Python基础语法(面向对象-下)

一、self 从函数的语法上来看, self 是形参 , 是一个普通的参数,那么在调用的时候,就需要传递实参值。从调用上看, 我们没有给 self 这个形参传递实参值, 但是 Python 解释器会自动的将调用这个方法的对象&#xff0c;作为实参值传递给 self。 class Dog:def eat(self):print…

江协科技STM32学习- P27 实验-串口发送/串口接收

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

基于大语言模型(LLM)自主Agent 智能体综述

近年来,LLM(Large Language Model)取得了显著成功,并显示出了达到人类智能的巨大潜力。基于这种能力,使用LLM作为中央控制器来构建自助Agent,以获得类人决策能力。 Autonomous agents 又被称为智能体、Agent。指能够通过感知周围环境、进行规划以及执行动作来完成既定任务。…

自由学习记录(15)

Java注解 else if的省略问题&#xff08;可能看花&#xff09; else if也是取最近的if连通&#xff0c;看上去加了{}就可以正常执行了&#xff0c;缩进要命&#xff0c;不提示真容易看错&#xff0c; 组合数公式和数组参数 在 C 中&#xff0c;数组作为函数参数时&#xff0c;…

直流稳压电源设计

一、设计任务和基本要求&#xff1a; &#xff08;1&#xff09;设计集成直流稳压电源电路。 &#xff08;2&#xff09;输出直流电压5V。 &#xff08;3&#xff09;画出逻辑电路图&#xff0c;对电路进行仿真验证&#xff0c;写出总结报告。 图 1 系统组成框图 二、进度安排…

使用LangChain控制大模型的输出——解析器Parser

LangChain框架中有两个好用的工具&#xff1a; 提示词模板(PromptTemplate)用于指定LLM的输入&#xff0c;解析器(Parser)来正确解释LLM给出的输出 即&#xff1a; 提示词模板(PromptTemplate)&#xff1a;用于格式化地接受输入string变量&#xff0c;作为完整的提示词。 如 给…

如何在Linux系统中使用Apache HTTP Server

如何在Linux系统中使用Apache HTTP Server Apache简介 安装Apache 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 启动Apache服务 验证Apache是否正在运行 访问Apache默认页面 配置Apache虚拟主机 创建虚拟主机配置文件 示例虚拟主机配置 创建网站根目录 准备静态网站内…

基于Spring Boot的员工与部门信息管理系统:增删改查全攻略

介绍项目的搭建过程&#xff0c;包括依赖管理、数据库设计、实体类的创建、控制器的编写以及前端的简单实现。希望通过本项目的学习&#xff0c;能够加深大家对Spring Boot及相关技术的理解&#xff0c;为后续的开发奠定基础。 文章目录 前言 环境搭建 开发规范 查询部门 删除部…

深度学习优化器【学习率调整和梯度修正,Optimizer】

文章目录 一、学习率调整1 余弦退火&#xff08;1&#xff09;Warm up&#xff08;2&#xff09;Cosine Anneal 2 AdaGrad3 RMSprop算法4 AdaDelta算法 二、梯度估计修正1 动量法2 Nesterov加速梯度3 Adam修正的原因 4 AdamW 三、总结参考资料 在当今快速发展的人工智能领域&am…

在米尔电子MPSOC实现12G SDI视频采集H.265压缩SGMII万兆以太网推流

1. 引言 随着网络视频平台的发展&#xff0c;用户对于4K高清画质的需求日益增长。然而&#xff0c;许多用户发现&#xff0c;即使购买了视频平台的会员&#xff0c;观看4K内容时画质却不如预期&#xff0c;有时甚至还会出现模糊、卡顿的情况。这种现象背后涉及到视频编码、网络…

项目复盘:TapTap聚光灯Gamejam

前言 三周的开发终于落下帷幕&#xff0c;其实我个人还是对结果很满意的&#xff0c;虽然作品不及他人的万分之一&#xff0c;不过主要目的还是以熟悉Godot引擎本身为主。这次我做的事情其实主要还是把在Unity那边学到的东西Copy到Godot来&#xff0c;仅此而已&#xff0c;几乎…