Spring学习——什么是循环依赖及其解决方式

news2024/11/13 13:17:33

文章目录

  • 前言
  • 一、什么是循环依赖
  • 二、解决思路
    • 1、循环依赖分类
    • 2、对象初始化步骤及对象分类
    • 3、spring是如何解决的
    • 4、图解
    • 5、三级缓存
      • 1、区别
      • 2、ObjectFactory是什么
  • 三、源码debug
    • 1、spring创建对象过程
      • 1、dubug第一步——找到getBean
      • 2、dubug第二步——getBean与doGetBean
      • 3、dubug第三步——createBean和doCreateBean
      • 4、dubug第四步——createBeanInstance创建对象
      • 5、dubug第五步——populateBean填充属性
      • 6、dubug第六步——创建B对象
      • 7、dubug第七步——给B对象中的a属性赋值
      • 8、dubug第八步——创建A对象
      • 9、dubug第九步——给A对象中b属性赋值
      • 10、dubug第十步——循环创建B
    • 2、流程图
  • 四、问题与思考
    • 1、三个类型的map分别存储什么类型的对象
    • 2、三个map在查找对象的时候,是按照什么顺序查找的
    • 3、如果只有一个map,可以解决循环问题吗
    • 4、如果只有两个map,可以解决循环问题吗
    • 5、三级缓存是怎么解决AOP问题的


前言

当前网络上有许多文章探讨循环依赖及Spring框架如何解决这一问题,然而,他人的观点毕竟是他人的,因此我想写一篇属于自己的文章,来探讨循环依赖的问题。

一、什么是循环依赖

在Spring框架中,循环依赖指的是当两个或多个Bean之间相互依赖,形成循环引用的情况。这种循环依赖可能会导致Spring容器无法正确地实例化Bean,从而引发应用程序启动失败或运行时异常。

在这里插入图片描述
例如图中的情况:A对象中依赖于B,B对象中依赖A。
创建过程如下
在这里插入图片描述

那么在创建A对象时,需要对A进行实例化,随后对其属性B进行赋值,但是这时候还没有B对象,就需要先创建B对象。
在创建B对象时,需要对B进行实例化,随后对其属性A进行赋值,但是这时候A对象还没有创建完成。
这个时候就形成了一个闭环,这种情况就是循环依赖,如果没有正确处理这种情况,程序就会启动失败或运行时异常。

二、解决思路

从上图中可以看到,形成闭环的最后一步为容器中没有A对象蓝色线条),所以这一步如果不存在,那么就不会存在循环依赖问题。

1、循环依赖分类

在Spring框架中,循环依赖可以分为两种主要类型:

1、构造器注入循环依赖:当两个或多个Bean通过构造器注入相互依赖时,如果它们之间形成了循环引用,Spring容器在创建这些Bean时可能会陷入死循环,导致应用启动失败或者内存溢出等问题。

2、属性注入循环依赖:当两个或多个Bean通过属性注入相互依赖时,如果它们之间形成了循环引用,Spring容器会尝试解决这种循环依赖。但是,如果解决不当,可能会导致其中一个Bean的某些属性为null,或者Bean的状态不一致,从而引发运行时错误。

其中,构造器循环依赖不能解决,只能抛出异常,而属性注入循环依赖是可以解决的。

2、对象初始化步骤及对象分类

在这里插入图片描述
在spring创建对象的流程图中可以看出,对象初始化分为三步:实例化填充属性初始化

那么可以将对象分为两类:
1、完成实例化但未完成初始化
2、完成实例化也完成了初始化

发生循环依赖时,即进行到蓝色线条步骤的时候,对象A是完成实例化但未完成初始化

3、spring是如何解决的

在流程图中看出,B对象是在完成实例化之后立刻对属性A进行赋值,虽然A对象没有完成初始化,但是完成了实例化,所以此时拥有了完成实例化但未完成初始化的A对象。
此时,将这个半成品的A对象赋值给B,那么蓝色线条步骤就不会存在,循环依赖问题也就解决了。

即,实例化与初始化分开进行

如果实例化与初始化在必须在一起时,循环依赖问题就不能得到解决,例如构造器注入循环依赖

4、图解

在这里插入图片描述

上图中,在对象实例化之后就放入半成品对象A,在给a属性赋值的时候,把半成品对象A赋值给a,那么会得到一个成品的B对象,那么在回去把成品的B对象赋值给A对象,那么也就得到了一个成品的A对象

5、三级缓存

在上一步中已经解决了循环依赖问题,那么又有一个新的问题:spring分别创建了半成品的A、B,成品的A、B。如果放在一个map中,显然比较混乱,那么它们在spring中时怎么存放的。

在spring中,spring的开发者使用三个Map存放我们的Bean对象,这三个也就是我们说的三级缓存。

三个Map位于 DefaultSingletonBeanRegistry 类中
在这里插入图片描述

/** 一级缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三级缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二级缓存 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

那么这三个Map有什么区别?

1、区别

Map类型不同:一级、二级缓存为ConcurrentHashMap,三级缓存为HashMap
容量不同一级缓存为256,二级缓存为16,三级缓存为16
泛型不同:三级缓存里边放的是ObjectFactory类型,一级、二级缓存为Object
其中最重要的是泛型的不同那么ObjectFactory是什么?

2、ObjectFactory是什么

ObjectFactory:函数式接口,可以将lambda表达式作为参数放到方法的实参中。在方法执行的时候不会执行lambda表达式,只有在调用getObject的时候才会执行lambda表达式。

@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}

三、源码debug

1、spring创建对象过程

spring创建对象过程中重要的六个方法

在这里插入图片描述

1、dubug第一步——找到getBean

这个方法是在AbstractApplicationContext类中,这个方法中包含了创建对象的13个流程,我们看其中最重要的finishBeanFactoryInitialization

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			initMessageSource();

			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			onRefresh();

			// Check for listener beans and register them.
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			finishRefresh();
		}
		
		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

在beanFactory中可以找到我们的三级缓存:
在这里插入图片描述
在singletonObjects中有五个对象,是容器创建时需要的。
在这里插入图片描述
在finishBeanFactoryInitialization方法中,有一些判断和设置可以不看,着重看最后一行beanFactory.preInstantiateSingletons();

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// Initialize conversion service for this context.
	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
		beanFactory.setConversionService(
				beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
	}

	// Register a default embedded value resolver if no BeanFactoryPostProcessor
	// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
	// at this point, primarily for resolution in annotation attribute values.
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}

	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}

	// Stop using the temporary ClassLoader for type matching.
	beanFactory.setTempClassLoader(null);

	// Allow for caching all bean definition metadata, not expecting further changes.
	beanFactory.freezeConfiguration();

	// Instantiate all remaining (non-lazy-init) singletons.
	beanFactory.preInstantiateSingletons();
}

打开preInstantiateSingletons()方法:

@Override
public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(
								(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				getBean(beanName);
			}
		}
	}

	// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else {
				smartSingleton.afterSingletonsInstantiated();
			}
		}
	}
}

可以看到beanDefinitionNames中存放着我们需要创建的对象的集合:a和b。随后开始循环创建
在这里插入图片描述
首先经过for循环和一些判断之后,进入到了getBean中,也就是spring创建对象过程中重要的六个方法的第一个。
在这里插入图片描述

2、dubug第二步——getBean与doGetBean

getBean中,也就是doGetBean,是spring创建对象过程中重要的六个方法的第二个。

@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

打开doGetBean方法:

/**
 * Return an instance, which may be shared or independent, of the specified bean.
 * @param name the name of the bean to retrieve
 * @param requiredType the required type of the bean to retrieve
 * @param args arguments to use when creating a bean instance using explicit arguments
 * (only applied when creating a new instance as opposed to retrieving an existing one)
 * @param typeCheckOnly whether the instance is obtained for a type check,
 * not for actual use
 * @return an instance of the bean
 * @throws BeansException if the bean could not be created
 */
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
		throws BeansException {

	String beanName = transformedBeanName(name);
	Object bean;

	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}

	else {
		// Fail if we're already creating this bean instance:
		// We're assumably within a circular reference.
		if (isPrototypeCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}

		// Check if bean definition exists in this factory.
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
			// Not found -> check parent.
			String nameToLookup = originalBeanName(name);
			if (parentBeanFactory instanceof AbstractBeanFactory) {
				return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
						nameToLookup, requiredType, args, typeCheckOnly);
			}
			else if (args != null) {
				// Delegation to parent with explicit args.
				return (T) parentBeanFactory.getBean(nameToLookup, args);
			}
			else if (requiredType != null) {
				// No args -> delegate to standard getBean method.
				return parentBeanFactory.getBean(nameToLookup, requiredType);
			}
			else {
				return (T) parentBeanFactory.getBean(nameToLookup);
			}
		}

		if (!typeCheckOnly) {
			markBeanAsCreated(beanName);
		}

		try {
			RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			// Guarantee initialization of beans that the current bean depends on.
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (String dep : dependsOn) {
					if (isDependent(beanName, dep)) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
					}
					registerDependentBean(dep, beanName);
					try {
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
					}
				}
			}

			// Create bean instance.
			if (mbd.isSingleton()) {
				sharedInstance = getSingleton(beanName, () -> {
					try {
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						// Explicitly remove instance from singleton cache: It might have been put there
						// eagerly by the creation process, to allow for circular reference resolution.
						// Also remove any beans that received a temporary reference to the bean.
						destroySingleton(beanName);
						throw ex;
					}
				});
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
			}

			else if (mbd.isPrototype()) {
				// It's a prototype -> create a new instance.
				Object prototypeInstance = null;
				try {
					beforePrototypeCreation(beanName);
					prototypeInstance = createBean(beanName, mbd, args);
				}
				finally {
					afterPrototypeCreation(beanName);
				}
				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
			}

			else {
				String scopeName = mbd.getScope();
				if (!StringUtils.hasLength(scopeName)) {
					throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
				}
				Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
				}
				try {
					Object scopedInstance = scope.get(beanName, () -> {
						beforePrototypeCreation(beanName);
						try {
							return createBean(beanName, mbd, args);
						}
						finally {
							afterPrototypeCreation(beanName);
						}
					});
					bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(beanName,
							"Scope '" + scopeName + "' is not active for the current thread; consider " +
							"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
							ex);
				}
			}
		}
		catch (BeansException ex) {
			cleanupAfterBeanCreationFailure(beanName);
			throw ex;
		}
	}

	// Check if required type matches the type of the actual bean instance.
	if (requiredType != null && !requiredType.isInstance(bean)) {
		try {
			T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
			if (convertedBean == null) {
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
			return convertedBean;
		}
		catch (TypeMismatchException ex) {
			if (logger.isTraceEnabled()) {
				logger.trace("Failed to convert bean '" + name + "' to required type '" +
						ClassUtils.getQualifiedName(requiredType) + "'", ex);
			}
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		}
	}
	return (T) bean;
}

getSingleton中,会先检查在单例缓存中是否有手动注册的单例对象,可以看到,现在结果为空。随后开始创建对象。

随后开始走到如下位置,另一个getSingleton方法,可以看到第一个参数为beanName,第二个参数就是lambda表达式,这时lambda表达式并不会执行。
在这里插入图片描述
打开getSingleton方法,

/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			if (this.singletonsCurrentlyInDestruction) {
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
			}
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				// Has the singleton object implicitly appeared in the meantime ->
				// if yes, proceed with it since the exception indicates that state.
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					throw ex;
				}
			}
			catch (BeanCreationException ex) {
				if (recordSuppressedExceptions) {
					for (Exception suppressedException : this.suppressedExceptions) {
						ex.addRelatedCause(suppressedException);
					}
				}
				throw ex;
			}
			finally {
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = null;
				}
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

可以看到,传递的是一个lambda表达式。
在这里插入图片描述
当走到singletonObject = singletonFactory.getObject();时,才会执行lambda表达式。
也就是执行getObject也就是执行刚刚的lambda表达式,执行lambda表达式中的createBean,spring创建对象过程中重要的六个方法的第三个。
在这里插入图片描述

3、dubug第三步——createBean和doCreateBean

createBean方法中,经过一些判断,进入doCreateBean.spring创建对象过程中重要的六个方法的第四个。

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {

	if (logger.isTraceEnabled()) {
		logger.trace("Creating instance of bean '" + beanName + "'");
	}
	RootBeanDefinition mbdToUse = mbd;

	// Make sure bean class is actually resolved at this point, and
	// clone the bean definition in case of a dynamically resolved Class
	// which cannot be stored in the shared merged bean definition.
	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
	if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
		mbdToUse = new RootBeanDefinition(mbd);
		mbdToUse.setBeanClass(resolvedClass);
	}

	// Prepare method overrides.
	try {
		mbdToUse.prepareMethodOverrides();
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
				beanName, "Validation of method overrides failed", ex);
	}

	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	}
	catch (Throwable ex) {
		throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
				"BeanPostProcessor before instantiation of bean failed", ex);
	}

	try {
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		if (logger.isTraceEnabled()) {
			logger.trace("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}
	catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
		// A previously detected exception with proper bean creation context already,
		// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
		throw ex;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
	}
}
/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation, use of a
 * factory method, and autowiring a constructor.
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}

	// Allow post-processors to modify the merged bean definition.
	synchronized (mbd.postProcessingLock) {
		if (!mbd.postProcessed) {
			try {
				applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
			}
			catch (Throwable ex) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Post-processing of merged bean definition failed", ex);
			}
			mbd.postProcessed = true;
		}
	}

	// Eagerly cache singletons to be able to resolve circular references
	// even when triggered by lifecycle interfaces like BeanFactoryAware.
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	catch (Throwable ex) {
		if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
			throw (BeanCreationException) ex;
		}
		else {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
		}
	}

	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				exposedObject = earlySingletonReference;
			}
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				String[] dependentBeans = getDependentBeans(beanName);
				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
				for (String dependentBean : dependentBeans) {
					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
						actualDependentBeans.add(dependentBean);
					}
				}
				if (!actualDependentBeans.isEmpty()) {
					throw new BeanCurrentlyInCreationException(beanName,
							"Bean with name '" + beanName + "' has been injected into other beans [" +
							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
							"] in its raw version as part of a circular reference, but has eventually been " +
							"wrapped. This means that said other beans do not use the final version of the " +
							"bean. This is often the result of over-eager type matching - consider using " +
							"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}

	// Register bean as disposable.
	try {
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
	}

	return exposedObject;
}

doCreateBean方法中,createBeanInstance就是创建我们的bean实例的。在createBeanInstance方法中,使用的是反射来创建对象
在这里插入图片描述

4、dubug第四步——createBeanInstance创建对象

createBeanInstance方法中,最后一个方法为instantiateBean
在这里插入图片描述
进入instantiateBean,先获取了当前对象的构造器,然后创建对象。
在这里插入图片描述
进入instaniate,点进去instantiateClass这个方法,可以看到:
return ctor.newInstance(argsWithDefaultValues);

此时,就算是利用反射的方式,来创建出了对象了。
在这里插入图片描述
在这里插入图片描述

总体流程:

在这里插入图片描述

从下图可以看出,已经创建完成了A对象,其中属性b为空。

在这里插入图片描述

继续向下,可以看到,执行了addSingletonFactory方法。

在这里插入图片描述

点开addSingletonFactory方法,可以看到,
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
在三级缓存中放入了map,key为beanName,value是一个lambda表达式。
然后将二级缓存清空,将beanName添加到已注册过的集合中。

/**
 * Add the given singleton factory for building the specified singleton
 * if necessary.
 * <p>To be called for eager registration of singletons, e.g. to be able to
 * resolve circular references.
 * @param beanName the name of the bean
 * @param singletonFactory the factory for the singleton object
 */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

现在我们创建了一个半成品的A对象,并且在三级缓存中加了一条数据

在这里插入图片描述

5、dubug第五步——populateBean填充属性

在这里插入图片描述

执行了addSingletonFactory方法之后,就开始对属性赋值,打开populateBean方法,

/**
 * Populate the bean instance in the given BeanWrapper with the property values
 * from the bean definition.
 * @param beanName the name of the bean
 * @param mbd the bean definition for the bean
 * @param bw the BeanWrapper with bean instance
 */
@SuppressWarnings("deprecation")  // for postProcessPropertyValues
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	if (bw == null) {
		if (mbd.hasPropertyValues()) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
		}
		else {
			// Skip property population phase for null instance.
			return;
		}
	}

	// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
	// state of the bean before properties are set. This can be used, for example,
	// to support styles of field injection.
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}
	}

	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

	int resolvedAutowireMode = mbd.getResolvedAutowireMode();
	if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
		MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
		// Add property values based on autowire by name if applicable.
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
			autowireByName(beanName, mbd, bw, newPvs);
		}
		// Add property values based on autowire by type if applicable.
		if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}
		pvs = newPvs;
	}

	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
	boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

	PropertyDescriptor[] filteredPds = null;
	if (hasInstAwareBpps) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					if (filteredPds == null) {
						filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
					}
					pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						return;
					}
				}
				pvs = pvsToUse;
			}
		}
	}
	if (needsDepCheck) {
		if (filteredPds == null) {
			filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
		}
		checkDependencies(beanName, mbd, filteredPds, pvs);
	}

	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

经过一些逻辑判断之后,直到最后,执行了applyPropertyValues方法。
这里就不贴完整代码了,只看关键部分。在applyPropertyValues方法中,有getName和getValue分别获取属性的名称和值。
获取到的值的类型为RuntimeBeanReference

在这里插入图片描述
随后执行
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);进行类型转换
刚刚的属性值为RuntimeBeanReference类型,随后返回resolveReference(argName, ref);

在这里插入图片描述

打开resolveReference方法

/**
 * Resolve a reference to another bean in the factory.
 */
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
	try {
		Object bean;
		Class<?> beanType = ref.getBeanType();
		if (ref.isToParent()) {
			BeanFactory parent = this.beanFactory.getParentBeanFactory();
			if (parent == null) {
				throw new BeanCreationException(
						this.beanDefinition.getResourceDescription(), this.beanName,
						"Cannot resolve reference to bean " + ref +
								" in parent factory: no parent factory available");
			}
			if (beanType != null) {
				bean = parent.getBean(beanType);
			}
			else {
				bean = parent.getBean(String.valueOf(doEvaluate(ref.getBeanName())));
			}
		}
		else {
			String resolvedName;
			if (beanType != null) {
				NamedBeanHolder<?> namedBean = this.beanFactory.resolveNamedBean(beanType);
				bean = namedBean.getBeanInstance();
				resolvedName = namedBean.getBeanName();
			}
			else {
				resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
				bean = this.beanFactory.getBean(resolvedName);
			}
			this.beanFactory.registerDependentBean(resolvedName, this.beanName);
		}
		if (bean instanceof NullBean) {
			bean = null;
		}
		return bean;
	}
	catch (BeansException ex) {
		throw new BeanCreationException(
				this.beanDefinition.getResourceDescription(), this.beanName,
				"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
	}
}

随后执行至bean = this.beanFactory.getBean(resolvedName);开始建造b对象

在这里插入图片描述

6、dubug第六步——创建B对象

从上得知,进入getBean方法后和创建A的流程一致,只不过doGetBean中的getSingleton方法实现不同

/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							singletonObject = singletonFactory.getObject();
							this.earlySingletonObjects.put(beanName, singletonObject);
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}

这里分别从一、二、三级缓存中获取b对象,如果没有就返回空,随后创建,如果有就返回

此时创建完成一个半成品的B对象,随后将半成品的B对象放入三级缓存

在这里插入图片描述

在这里插入图片描述

7、dubug第七步——给B对象中的a属性赋值

dubug第五步流程一致,执行至bean = this.beanFactory.getBean(resolvedName);开始建造a对象

在这里插入图片描述

8、dubug第八步——创建A对象

创建A对象执行至getSingleton方法,从一、二、三级缓存中获取A对象

在这里插入图片描述

可知一级、二级缓存中没有A对象,那么从三级缓存中取,执行至singletonFactory.getObject()时,执行上文中存入三级缓存的lambda表达式,即dubug第四步addSingletonFactory方法中存入的 () -> getEarlyBeanReference(beanName, mbd, bean)

在这里插入图片描述

看一下getEarlyBeanReference方法

/**
 * Obtain a reference for early access to the specified bean,
 * typically for the purpose of resolving a circular reference.
 * @param beanName the name of the bean (for error handling purposes)
 * @param mbd the merged bean definition for the bean
 * @param bean the raw bean instance
 * @return the object to expose as bean reference
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

exposedObject暴露对象,方法中最后返回的也是exposedObject,那就就是在exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);中对exposedObject进行了修改,那么看一下getEarlyBeanReference方法。

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
	return wrapIfNecessary(bean, beanName, cacheKey);
}

继续向下执行

/**
 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 * @param bean the raw bean instance
 * @param beanName the name of the bean
 * @param cacheKey the cache key for metadata access
 * @return a proxy wrapping the bean, or the raw bean instance as-is
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));在这里创建一个代理对象。

也就是getEarlyBeanReference方法中如果进入if条件就会执行createProxy

但是继续向下dubug,并没有进入if条件,直接返回了exposedObject

在这里插入图片描述

随后取出A对象放入二级缓存,删除三级缓存中的a

在这里插入图片描述

在这里插入图片描述
随后返回至applyPropertyValues方法给B对象的a属性赋值,也就是dubug第七步的目的。

在这里插入图片描述

执行完applyPropertyValues方法中的bw.setPropertyValues(new MutablePropertyValues(deepCopy));后,就完成了B对象的a属性赋值。可以看到B中此时有A,但是A中还没有B。

在这里插入图片描述

此时赋值完成,B为成品对象,返回至getSingleton方法,执行addSingleton方法

在这里插入图片描述

将B对象放入一级缓存。

/**
 * Add the given singleton object to the singleton cache of this factory.
 * <p>To be called for eager registration of singletons.
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

在这里插入图片描述

9、dubug第九步——给A对象中b属性赋值

随后逐步返回至创建A对象时调用的applyPropertyValues方法,执行dubug第五步
依旧是bw.setPropertyValues(new MutablePropertyValues(deepCopy));
执行完成之后,就可以看到已经完成了对A对象中b属性赋值

在这里插入图片描述

之后再次执行创建A对象时调用的addSingleton方法,此时一级缓存中多了两个我们创建的对象。

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

10、dubug第十步——循环创建B

由上文可知,我们创建了A和B对象,但是我们进入到getBean方法是从preInstantiateSingletons中循环创建A、B进入的创建A对象,此时我们应该进入下一个循环创建B对象了。

在这里插入图片描述

随后在创建B对象的getSingleton方法中在一级缓存能取到B对象,所以直接返回。

在这里插入图片描述

至此,完成A、B对象的创建

2、流程图

这里整理了一个大致流程

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

四、问题与思考

1、三个类型的map分别存储什么类型的对象

一级缓存:成品对象
二级缓存:半成品对象
三级缓存:lambda表达式

2、三个map在查找对象的时候,是按照什么顺序查找的

先从一级缓存中获取对象,如果没有再从二级缓存中获取,二级缓存中没有再从三级缓存中获取。

3、如果只有一个map,可以解决循环问题吗

可以,只要能存储一个标志位区分开成品和半成品就行,但是太麻烦了。

4、如果只有两个map,可以解决循环问题吗

可以,如果对象的创建过程中不包含 aop(没有代理对象的时候) ,那么一级二级缓存就可以解决循环依赖问题,但是如果包含 aop 的操作,那么没有三级缓存的话,循环依赖问题是解决不了的

5、三级缓存是怎么解决AOP问题的

首先一个容器中只能包含一个同名的对象
对象创建的过程中有可能需要生成代理对象,那么如果程序创建了代理对象,那么调用的时候用的是原始对象还是代理对象呢?要使用代理对象,程序怎么知道该用代理对象呢?
程序是没有办法知道的,所以当出现代理对象的时候,就要用代理对象替换掉原始对象

那么代理对象的创建时在初始化过程扩展阶段,即debug第八步Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));,而属性赋值在生成代理对象前。
在这里插入图片描述
怎么完成替换呢? 所以需要在前置过程的时候判断需要生成代理对象。即:

在这里插入图片描述

这段代码是在lambda表达式(debug第四步的代码addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));)中调用的,那么为什么要用lambda表达式机制中完成呢

因为对象在什么时候被暴露出去还是被应用是不能提前确定的,只有在被调用的时候才可以判断是原始对象还是代理对象,使用lambda表达式就类似于回调机制,不暴露的时候不执行,被调用的时候才执行,来判断返回的是代理对象还是原始对象。(该lambda表达式是是从三级缓存中取出key为a时调用的,这时候判断是需要队里对象还是原始对象)
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

基于AT89C51单片机的智能交通灯设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/89035863?spm1001.2014.3001.5503 1绪 论 1.1课题研究背景 交通是城市经济活动的命脉&#xff0c;对城市经济发展、人民生活水平的提高起着十分重要的作用。城市交…

用 C++ 编码架构图的最佳用例

统一建模语言&#xff08;UML&#xff09;&#xff0c;作为一种实际应用的语言标准&#xff0c;借助一系列架构图呈现建模软件系统。UML 的出现鼓励了自动化软件工具的开发&#xff0c;有助于自动代码生成。UML 图面向对象系统和软件工具&#xff0c;将静态结构和动态行为以可视…

如何统计代码量

工具&#xff1a; cloc 下载地址&#xff1a; Releases AlDanial/cloc GitHub 使用方法&#xff1a;

2024.3.26

实现闹钟 weiget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTimer> #include<QTime> #include<QTimerEvent> #include<QString> #include<QtTextToSpeech> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } Q…

力扣● 503.下一个更大元素II ● 42. 接雨水

503.下一个更大元素II 与496.下一个更大元素 I的不同是要循环地搜索元素的下一个更大的数。那么主要是对于遍历结束后&#xff0c;单调栈里面剩下的那些元素。 如果直接把两个数组拼接在一起&#xff0c;然后使用单调栈求下一个最大值就可以。 代码实现的话&#xff0c;不用直…

ubuntu22.04配置Azure Kinect DK深度相机

一.安装SDK 今天我来配置一下微软公司的Azure Kinect DK深度相机,以前在ubuntu18.04上配置过,因为官方说唯一支持linux版本是18.04,所以在18.04中配置还算顺利 but这不代表不可以在更高版本的ubuntu中使用,只不过需要自己去多配置一些东西 apt 源安装 更新源: c…

上海企业必应bing国内广告推广如何开户?

随着数字化营销时代的深入发展&#xff0c;搜索引擎广告已成为众多企业提升品牌知名度和促进产品销售的重要手段之一。在国内市场&#xff0c;微软必应&#xff08;Bing&#xff09;搜索广告以其精准定位与高价值用户群赢得了众多企业的青睐。对于位于上海地区的企业来说&#…

蓝桥杯:Python基础学习一

目录 一、遍历列表 1.使用for 循环和 enumerate()函数实现 2.案例代码 二、对列表进行统计和计算 1.统计数值列表的元素和 2.案例代码 三、对列表进行排序 1.使用列表对象的sort()方法 2.使用内置的 sorted()函数实现 四、列表推导式 1.从列表中选择符合条件的元素组…

flask_restful结合蓝图使用

在蓝图中&#xff0c;如果使用 Flask_RESTful &#xff0c; 创建 Api 对象的时候&#xff0c;传入蓝图对象即可&#xff0c;不再是传入 app 对象 /user/__init__.py from flask.blueprints import Blueprintuser_bp Blueprint(user,__name__)from user import views /user…

泛型编程的启蒙之旅

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 讲模板之前呢&#xff0c;我们先来谈谈泛型编程&#xff1a; 泛型编程&#xff1a;编写与类…

Linux——命名管道

Linux——命名管道 命名管道命名管道和匿名管道的区别 创建命名管道利用命名管道实现简单通信 我们之前学习了匿名管道&#xff0c;这种管道有一个缺点就是只有两个有血缘关系的进程才能够使用匿名管道&#xff0c;这个非常不方便。所以我们又在匿名管道的基础之上引入了命名管…

Linux实现m4a格式转换为wav格式

需要在linux上安装ffmpeg 参考博客 Linux上安装ffmpeg修改环境变量【这一点很重要&#xff0c;自己因为没有添加环境变量&#xff0c;捣鼓了很长时间】 将ffmpeg的绝对路径添加到 PATH 环境变量中&#xff0c;以让系统能找到ffmpeg的安装路径。 # /home//project/ffmpeg-6.1-a…

QT作业day3

1、使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是…

vue3+threejs新手从零开发卡牌游戏(八):关联卡组和手牌区、添加初始化卡组和初始化手牌逻辑

首先我们优化下之前的代码&#xff0c;先加载游戏资源&#xff0c;然后再初始化场景&#xff0c;由于目前只有一个font字体需要加载&#xff0c;所以我们将之前game/deck/p1.vue中的font相关代码迁移到game/index.vue下&#xff0c;同时使用async和await处理异步加载&#xff0…

干货分享之反射笔记

入门级笔记-反射 一、利用反射破泛型集合二、Student类三、获取构造器的演示和使用1.getConstructors只能获取当前运行时类的被public修饰的构造器2.getDeclaredConstructors:获取运行时类的全部修饰符的构造器3.获取指定的构造器3.1得到空构造器3.2得到两个参数的有参构造器&a…

基于java+springboot+vue实现的超市管理系统(文末源码+Lw+ppt)23-354

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对超市管理的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”…

启扬RK3568核心板助力智慧步道轻装健身,打造全民健康生活新方式

随着物联网、AI智能等新技术的快速发展&#xff0c;智慧步道成为全国各地公园建设和全民健身公共服务设施改造的新主题。智慧步道基于物联网、人脸识别、大数据分析等技术&#xff0c;对人们的运动进行监测和数据采集&#xff0c;显示运动数据&#xff0c;包括里程统计、热量消…

【王道训练营】第6题 输入一个整型数,判断是否是对称数,如果是,输出yes,否则输出no

文章目录 我的代码改正代码其他代码 我的代码 没有完成 #include<stdio.h> int main(){int a;int b;int c0;//位数int d0;//比较几次scanf("%d",&a);while(b!0){bb/10;c;}dc/2;//比较几次int ffor(int i0 ;i<d;i){int ec;//位数fa - a / (((e-i-1)*10…

2024年软件测试,“我“从初级到高级进阶,不再走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 现在2024年&#…

Python算法100例-4.2 列出真分数序列

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.确定程序框架5.完整的程序6.拓展训练 1&#xff0e;问题描述 按递增顺序依次列出所有分母为40、分子小于40的最简分数。 2&#xff0e;问题分析 分子和分母只有公因数1的分数&…