Spring源码分析之Bean的实例化(createBeanInstance())

news2024/12/26 11:23:27

前言:

  通过Spring源码分析之Bean的创建过程(createBean)-CSDN博客我们可以知道如果没有动态代理以及循环依赖的前提之下的话那么一个普通的单例Bean的创建后就是实例化,属性填充,初始化这三个步骤那么这篇文章的话我们就先说一下实例化也就是doCreateBean方法里面的createBeanInstance()

createBeanInstance():

 这个方法的代码如下所示

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// 确保在此点已经解析了bean类
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		if (args == null) {
			Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
			if (instanceSupplier != null) {
				// 如果bean定义中提供了实例供应者,则从供应者获取实例
				return obtainFromSupplier(instanceSupplier, beanName, mbd);
			}
		}

		if (mbd.getFactoryMethodName() != null) {
			// 如果bean定义中指定了工厂方法,则使用工厂方法创建实例
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// 当重新创建相同的bean时的快捷方式这个就是涉及到缓存了
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				// 如果需要自动装配,则调用autowireConstructor方法
				return autowireConstructor(beanName, mbd, null, null);
			} else {
				// 否则,直接实例化bean使用的就是默认的无参构造方法
				return instantiateBean(beanName, mbd);
			}
		}

		// 确定可用于自动装配的候选构造器因为是第一次进行加载的
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			// 如果有候选构造器或需要自动装配构造器,则调用autowireConstructor方法
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// 获取首选构造器存在多个构造器的话
		//那么就会进行挑选选择哪个进行实例话选择最优的方法
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			//构造方法的自动注入
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// 默认情况下,使用无参构造器
		return instantiateBean(beanName, mbd);
	}

1.调用supplier接口:

 若 RootBeanDefinition 中设置了 Supplier 则使用 Supplier 提供的bean替代Spring要生成的bean

protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
		Object instance;
		// 从当前已经创建的Bean中获得一个其的名字
		String outerBean = this.currentlyCreatedBean.get();
		this.currentlyCreatedBean.set(beanName);
		try {
			// 从 Supplier 接口中获取 bean实例
			instance = instanceSupplier.get();
		}
		finally {
			if (outerBean != null) {
				this.currentlyCreatedBean.set(outerBean);
			}
			else {
				this.currentlyCreatedBean.remove();
			}
		}

		if (instance == null) {
			instance = new NullBean();
		}
		// 进行一次封装变成 BeanWrapper
		BeanWrapper bw = new BeanWrapperImpl(instance);
		initBeanWrapper(bw);
		return bw;
	}

2.instantiateUsingFactoryMethod():

 这个主要就是说通过工厂方法进行创建Bean对象

测试一下毕竟实践出真知:

//这个就是配置类
@Configuration
@ComponentScan("com.example") // 确保这里是你的包路径
public class AppConfig {
    @Bean
    public BeanFactory myFactory() {
        return new BeanFactory();
    }
    @Bean
    public TestBean myBean(BeanFactory beanFactory) {
        // 调用工厂对象的实例方法创建 Bean
        return beanFactory.createBean("testBean");
    }
}
//这个就是工厂类
public class BeanFactory {
    public TestBean createBean(String name){
        return new TestBean(name);
    }
}
//这个就是普通的一个类
public class TestBean {
    private  String BeanName;
    public TestBean(String  beanName){
        this.BeanName=beanName;
    }
    @Override
    public String toString() {
        return "这个的bean的名字为{" + "name='" +  BeanName + '\'' + '}';
    }

}

最后结果的展示:

3.构造函数的缓存判断:

  这个就是说涉及到一个优化的手段了,也就是说这个时候Bean的创建需要使用构造方法进行创建但是Bean的构造方法就有可能会有多个(由于方法重载的出现)那么如果进行判断的话那么这个时候Spring就会将已经判断成功的构造方法放到RootDefinition进行一个缓存的处理

	/** Package-visible field for caching the resolved constructor or factory method. */
    //缓存以及解析的构造方法以及工厂方法
	@Nullable
	Executable resolvedConstructorOrFactoryMethod;
    	/** Package-visible field for caching fully resolved constructor arguments. */
    //用于缓存以及解析的构造器的参数
	@Nullable
	Object[] resolvedConstructorArguments;
    /** Package-visible field that marks the constructor arguments as resolved. */
    //表示这个构造器的参数以及解析了
    //是否需要通过构造注入(autowireConstructor) 的方式加载
	boolean constructorArgumentsResolved = false;
// 当重新创建相同的bean时候
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				//是否已经解析了构造方法以及工厂方法
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					//是否有必要进行进行自动装配
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				// 如果需要自动装配,则调用autowireConstructor方法进行依赖注入
				return autowireConstructor(beanName, mbd, null, null);
			} else {
				// 否则,直接实例化bean使用的就是默认的无参构造方法
				return instantiateBean(beanName, mbd);
			}
		}

4.autowireConstructor():

 对于一个对象的创建我们就是分为有参构造方法以及无参构造方法,那么对于有参构造方法进行创建的时候就会使用这个方法这个里面进行仔细研究的话其实就是特别复杂但是使用一句话进行概括的话那么就是说就是根据传入的参数列表,来匹配到合适的构造函数进行bean 的创建。  

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
        //explicitArgs这个就是说通过getBean方法传入的参数
		//chosenCtors这个就是说是选择的候选的构造方法我的理解就是所有的有参数的构造放啊
		BeanWrapperImpl bw = new BeanWrapperImpl();
		this.beanFactory.initBeanWrapper(bw);
        //变量的初始化
		Constructor<?> constructorToUse = null;
		ArgumentsHolder argsHolderToUse = null;
		Object[] argsToUse = null;
       //如果getBean传入的参数不为空的话那么就直接使用这个参数
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			Object[] argsToResolve = null;
			//检查是否有以及缓存的构造函数以及构造参数
			synchronized (mbd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached constructor...
					 //如果解析的构造函数种有参数的话的话那么就直接进行使用
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						//如果没有缓存的参数的话那么就赋值已经准备的构造器的参数
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			if (argsToResolve != null) {
				//如果存在没有解析的参数那么就会进行准备解析以及准备的参数
				//这个已经准备好了
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
			}
		}
        //如果还是没有找到能够使用的构造器以及参数的话
		if (constructorToUse == null || argsToUse == null) {
			// Take specified constructors, if any.
           //获得候选的构造器
			Constructor<?>[] candidates = chosenCtors;
			if (candidates == null) {
				Class<?> beanClass = mbd.getBeanClass();
				try {
					// 获取所有声明的构造函数(包括私有构造函数)
					//这个就是很明显能够看出来就是通过反射进行的
					candidates = (mbd.isNonPublicAccessAllowed() ?
							beanClass.getDeclaredConstructors() : beanClass.getConstructors());
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Resolution of declared constructors on bean Class [" + beanClass.getName() +
							"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
				}
			}
           //如果是一个无参构造函数的话那么就会直接进行使用
			if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
				Constructor<?> uniqueCandidate = candidates[0];
				if (uniqueCandidate.getParameterCount() == 0) {
					synchronized (mbd.constructorArgumentLock) {
						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
						mbd.constructorArgumentsResolved = true;
						mbd.resolvedConstructorArguments = EMPTY_ARGS;
					}
					bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
					return bw;
				}
			}

			// Need to resolve the constructor.
			//需要解析这个构造函数
			boolean autowiring = (chosenCtors != null ||
					mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
			ConstructorArgumentValues resolvedValues = null;

			int minNrOfArgs;
			if (explicitArgs != null) {
				minNrOfArgs = explicitArgs.length;
			}
			else {
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				resolvedValues = new ConstructorArgumentValues();
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}
           //对候选构造函数进行排序,优先选择参数更多的构造函数
			AutowireUtils.sortConstructors(candidates);
			int minTypeDiffWeight = Integer.MAX_VALUE;
			Set<Constructor<?>> ambiguousConstructors = null;
			Deque<UnsatisfiedDependencyException> causes = null;

			for (Constructor<?> candidate : candidates) {
				int parameterCount = candidate.getParameterCount();
				// 如果已经找到了一个可以满足的构造函数,且参数更多,停止查找
				if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
					// Already found greedy constructor that can be satisfied ->
					// do not look any further, there are only less greedy constructors left.
					break;
				}
				if (parameterCount < minNrOfArgs) {
					continue;
				}

				ArgumentsHolder argsHolder;
				 //尝试解析其他构造方法的参数
				Class<?>[] paramTypes = candidate.getParameterTypes();
				if (resolvedValues != null) {
					try {
						String[] paramNames = null;
						if (resolvedValues.containsNamedArgument()) {
							paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
							if (paramNames == null) {
								ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
								if (pnd != null) {
									paramNames = pnd.getParameterNames(candidate);
								}
							}
						}
						argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
								getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
					}
					catch (UnsatisfiedDependencyException ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
						}
						// Swallow and try next constructor.
						if (causes == null) {
							causes = new ArrayDeque<>(1);
						}
						causes.add(ex);
						continue;
					}
				}
				else {
					// Explicit arguments given -> arguments length must match exactly.
					//如果有显式给的参数的话那么参数的数目一定式完全匹配的
					if (parameterCount != explicitArgs.length) {
						continue;
					}
					argsHolder = new ArgumentsHolder(explicitArgs);
				}
               //计算构造函数参数与依赖项之间的类型差异权重
				int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
						argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
				// Choose this constructor if it represents the closest match.
				//选择类型差异最小的构造函数
				if (typeDiffWeight < minTypeDiffWeight) {
					constructorToUse = candidate;
					argsHolderToUse = argsHolder;
					argsToUse = argsHolder.arguments;
					minTypeDiffWeight = typeDiffWeight;
					ambiguousConstructors = null;
				}
				//如果有多个构造函数的类型差异相同,标记为模糊匹配
				else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
					if (ambiguousConstructors == null) {
						ambiguousConstructors = new LinkedHashSet<>();
						ambiguousConstructors.add(constructorToUse);
					}
					ambiguousConstructors.add(candidate);
				}
			}

			if (constructorToUse == null) {
				if (causes != null) {
					UnsatisfiedDependencyException ex = causes.removeLast();
					for (Exception cause : causes) {
						this.beanFactory.onSuppressedException(cause);
					}
					throw ex;
				}
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Could not resolve matching constructor on bean class [" + mbd.getBeanClassName() + "] " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities. " +
						"You should also check the consistency of arguments when mixing indexed and named arguments, " +
						"especially in case of bean definition inheritance)");
			}
			else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Ambiguous constructor matches found on bean class [" + mbd.getBeanClassName() + "] " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
						ambiguousConstructors);
			}
            //缓存解析结果,以便后续使用
			if (explicitArgs == null && argsHolderToUse != null) {
				argsHolderToUse.storeCache(mbd, constructorToUse);
			}
		}
		Assert.state(argsToUse != null, "Unresolved constructor arguments");
		// 使用解析后的构造函数和参数创建 Bean 实例
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}
方法总结:

  4.1.解析构造函数中的参数 :如果是getBean传入的参数那么就直接进行使用如果没有的话那么就会抓直接从缓存中得到如果参数完全解析成功的话那么直接使用resolvePreparedArguments 进行解析以及准备好的参数

  4.2.获得所有的候选的构造函数 (我的理解就是说是所有的有参构造方法):这个就是看有没有传入chosenCtors如果没有的话那么就会通过反射进行获得如果构造函数只有一个 & getBean 没有传参 & 构造参数无参,则直接使用这唯一一个构造函数并返回

 4.3.解析构造函数的参数的个数:因为传入的参数不一定就是说是构造方法的参数

  4.4.寻找最匹配的构造函数:这个就是根据参数的数目以及类型进行挑选构造函数然后选择完成之后就会调用instantiate(beanName, mbd, constructorToUse, argsToUse) 方法来创建bean实例。

 4.5.构造函数的筛选: 这个就是通过构造函数参数的数目以及参数类型之间的差距(优先选择参数数目比较多的构造函数)

4.6.instantiate():进行Bean的创建 获取当前 Bean 工厂的 InstantiationStrategy 实例然后这个进行创建

5. instantiateBean()——无参数构造方法实例化Bean:

  这个方法就是说有没有方法进行重写的如果没有的话那么就是进行反射获得器的无参构造方法进行创建否则的话那么就是通过CGLB重写目标类的方法进行实例化

//主要就是这个逻辑
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);


public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// 如果 Bean 定义中没有方法覆盖(即没有方法注入
		// 则不使用 CGLIB 进行子类生成。
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				// 尝试从缓存中获取已经解析的构造函数或工厂方法。
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;

				//  如果没有缓存的构造函数就是获得这个Class
				if (constructorToUse == null) {
					Class<?> clazz = bd.getBeanClass();

					// 5. 如果目标类是一个接口,则抛出异常。
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}

					try {
						// 查找默认构造函数(无参构造函数)。
						constructorToUse = clazz.getDeclaredConstructor();
						// 7. 将找到的构造函数缓存到 Bean 定义中,以便后续使用。
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
      //通过反射调用构造函数,创建并返回 Bean 实例。
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			//  如果 Bean 定义中包含方法覆盖(即有方法注入),则必须使用 CGLIB 生成子类。
			// 通过 CGLIB 子类可以动态地重写目标类的方法,从而实现方法注入。
			return instantiateWithMethodInjection(bd, beanName, owner);
		}

6.第一次进行创建:

  如果是第一次进行创建的化那么不会存在缓存构造方法以及参数的那么就进行下面的这个逻辑.

进行简单概括来说激就是说: 如何有构造函数参数或者自动注入有候选的构造函数那么就会调用

 autowireConstructor方法也就是我们刚刚在上面说的有参构造函数的放啊

Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			// 如果有候选构造器或需要自动装配构造器,则调用autowireConstructor方法
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// 获取首选构造器存在多个构造器的话
		//那么就会进行挑选选择哪个进行实例话
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			// 如果有首选构造器,则调用autowireConstructor方法
			//这个也就是说添加了@autowire注解的构造方法
			return autowireConstructor(beanName, mbd, ctors, null);
		}
getPreferredConstructors():

 这个方法就是说寻找优先级高的构造方法然后就会进行使用就是还是会进查看是有多个构造方法还是说只有一个构造方法然后不是默认的构造方法的话那么就会使用autowireConstructor()在这个中主要就是说通过@atowire注解表明实际上使用的构造方法当需要进行自动装配的时候,在旧的版本需要在xml文件中进行属性的配置(atowire属性)

protected BeanWrapper autowireConstructor(
			String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
		// 创建一个ConstructorResolver实例,用于处理构造器的解析和自动装配
		return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
	}

总结:

AbstractAutowireCapableBeanFactory中的CreateInstance就是创建Bean实例的入口我们可以通过实现一些拓展接口来创建Bean实例如(Supplier、factory-method)如果都没有的话那么就是说通过GetBean方法当中参数以及在所有的构造方法挑选出合适的构造方法:如果是在缓存中获得已经解析过的构造方法以及工厂方法那么就是如果需要进行自动注入的话那么就是会使用autowireConstruct进行实例的创建反之就是instantiateBean进行创建(也就是说是默认的),大部分情况都是第一次进行加载那么这个时候就会进行构造方法以及参数的解析(如优先通过使用@autowire注解的构造方法进行创建 当然也可以通过一个类实现SmartInstantiationAwareBeanPostProcessor接口然后根据不同的情况进行选择一个类的不同的构造方法)然后将其进行缓存起来不然的话默认就是会选择无参构造方法进行实例化

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

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

相关文章

一次“okhttp访问间隔60秒,提示unexpected end of stream“的问题排查过程

一、现象 okhttp调用某个服务&#xff0c;如果第二次访问间隔上一次访问时间超过60s&#xff0c;返回错误&#xff1a;"unexpected end of stream"。 二、最终定位原因&#xff1a; 空闲连接如果超过60秒&#xff0c;服务端会主动关闭连接。此时客户端恰巧访问了这…

蓝桥杯准备训练(lesson5 ,c++)

单目操作符与第 2 章 C/C输⼊输出&#xff08;上&#xff09; 8. 单⽬操作符8.1 和--8.1.1 前置 和 后置8.1.2 前置-- 和 后置-- 8.2 和 - 第 2 章 C/C输⼊输出&#xff08;上&#xff09;1. getchar 和 putchar1.1 getchar()1.2 putchar() 2. scanf 和 printf2.1 printf2.1.1…

Linux系统挂载exfat格式U盘教程,触觉智能RK3562开发板演示

本文介绍Linux系统&#xff08;Ubuntu/Debian通用&#xff09;挂载exfat格式U盘的方法&#xff0c;触觉智能RK3562开发板演示&#xff0c;搭载4核A53处理器&#xff0c;主频高达2.0GHz&#xff1b;内置独立1Tops算力NPU&#xff0c;可应用于物联网网关、平板电脑、智能家居、教…

LeetCode 0935.骑士拨号器:动态规划(DP)

【LetMeFly】935.骑士拨号器&#xff1a;动态规划(DP) 力扣题目链接&#xff1a;https://leetcode.cn/problems/knight-dialer/ 象棋骑士有一个独特的移动方式&#xff0c;它可以垂直移动两个方格&#xff0c;水平移动一个方格&#xff0c;或者水平移动两个方格&#xff0c;垂…

No.4 笔记 探索网络安全:揭开Web世界的隐秘防线

在这个数字时代&#xff0c;网络安全无处不在。了解Web安全的基本知识&#xff0c;不仅能保护我们自己&#xff0c;也能帮助我们在技术上更进一步。让我们一起深入探索Web安全的世界&#xff0c;掌握那些必备的安全知识&#xff01; 1. 客户端与WEB应用安全 前端漏洞&#xff1…

PHP使用local-proxy的一种思路! | 架构师之路(19)

《架构师之路&#xff1a;架构设计中的100个知识点》 19.脚本语言使用长连接的一种思路 脚本类语言&#xff0c;例如PHP&#xff0c;不能像C/Java那样能搞服务常驻内存&#xff0c;不能搞长连接&#xff1f; 为什么脚本语言要搞长连接&#xff1f; 脚本类语言每次访问后端数据库…

【51单片机】程序实验1112.外部中断-定时器中断

主要参考学习资料&#xff1a;B站【普中官方】51单片机手把手教学视频 前置知识&#xff1a;C语言 单片机套装&#xff1a;普中STC51单片机开发板A4标准版套餐7 码字不易&#xff0c;求点赞收藏加关注(•ω•̥) 有问题欢迎评论区讨论~ 目录 程序实验11&12.外部中断-定时器…

驱动---1.DAC8552实现三角波输出

最近开始进行新项目的研发&#xff0c;考虑用DAC做一个前级输出&#xff0c;选择了DAC8552这个器件的一个模块&#xff0c;用了野火的指南者做主控&#xff0c;芯片是STM32F103VET6&#xff0c;主频是72MHz。 一、器件手册重要信息提取 1.DAC8552具有十六位的分辨率、双通道输…

虚幻引擎生存建造系统

先做一个建造预览模式&#xff0c;按下按键B后进入建造预览模式 首先创建自定义事件Preview Loop 用射线追踪摆放物体预览位置&#xff0c;并做一个预览材质 增强输入设置按键 每帧判断是否进入建造模式 预览模式制作成功&#xff01; 接着做点击左键放置物品&#xff0…

IP研究 | 大数据洞察黄油小熊的爆火之路

一只来自泰国的小熊在国内红成了顶流。 今年&#xff0c;黄油小熊以烘焙店“打工人”的超萌形象迅速走红&#xff0c;2个月内火遍中国的社交媒体&#xff0c;泰国门店挤满飘洋过海求合影的中国粉丝&#xff0c;根据数说故事全网大数据洞察&#xff0c;黄油小熊2024年度的线上声…

深度学习案例:DenseNet + SE-Net

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 回顾DenseNet算法 DenseNet&#xff08;Densely Connected Convolutional Networks&#xff09;是一种深度卷积神经网络架构&#xff0c;提出的核心思想是通过在每一层与前面所有层进行直接连接…

【java学习笔记】Set接口实现类-LinkedHashSet

一、LinkedHashSet的全面说明 &#xff08;就是把数组不同位置的链表当成一个节点然后相连&#xff09;

【大模型系列篇】LLaMA-Factory大模型微调实践 - 从零开始

前一次我们使用了NVIDIA TensorRT-LLM 大模型推理框架对智谱chatglm3-6b模型格式进行了转换和量化压缩&#xff0c;并成功部署了推理服务&#xff0c;有兴趣的同学可以翻阅《NVIDIA TensorRT-LLM 大模型推理框架实践》&#xff0c;今天我们来实践如何通过LLaMA-Factory对大模型…

【C++】LeetCode:LCR 078. 合并 K 个升序链表

题干&#xff1a; 给定一个链表数组&#xff0c;每个链表都已经按升序排列。 请将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 解法&#xff1a;优先队列 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *ne…

数据结构和算法-04二叉树-04

广度优先的实现力扣中常见的二叉树相关问题及基本解决方案 tips&#xff1a; 在解决问题时&#xff0c;先确保问题能解决&#xff0c;再去考虑效率&#xff0c;这是解题的关键&#xff0c;切不可为追求效率而变成了技巧性解答。 广度优先 广度优先(层序遍历)遍历的方式是按层次…

DMA代码部分

第一个程序的接线图 OLED ShowHexNum(2,1,(uint32_t)&ADC1->DR,8); 这样可以看AD的DR寄存器的的地址(固定的)了 可以跑一下然后和手册对比 先查ADC1的地址 再在外设的总表里面, 查一下DR相对于上面地址的偏移量 所以其地址为4001 244C 研究一下外设寄存器的地址是怎么…

spdlog高性能日志系统

spdlog高性能日志系统 spdlog 是一个快速、简单、功能丰富的 C 日志库&#xff0c;专为现代 C 开发设计。它支持多种日志后端&#xff08;如控制台、文件、syslog 等&#xff09;&#xff0c;并提供灵活的格式化和线程安全的日志输出。 1. 特点 极高的性能&#xff1a;大量的编…

FPGA在线升级 -- Multiboot

简介 本章节主要描述关于如何从Golden Image转换到Multiboot Image程序。 升级方案 Golden Image转换到Multiboot Image的方法主要又两种 1、使用ICAPE2 原语&#xff1b; 2、在XDC文件中加入升级约束命令&#xff1b; 以上两种方案都可以实现在线升级&#xff0c;第一种升级…

守护进程化

目录 一、进程组 二、会话 &#xff08;1&#xff09;什么是会话 &#xff08;2&#xff09;如何创建一个会话 三、守护进程 一、进程组 之前我们学习过进程&#xff0c;其实每一个进程除了有一个进程 ID(PID)之外 还属于一个进程组。进程组是一个或者多个进程的集合&…

QML插件扩展

https://note.youdao.com/ynoteshare/index.html?id294f86c78fb006f1b1b78cc430a20d74&typenote&_time1706510764806