• 【Spring-5】AbstractBeanFactory#doGetBean创建实例完整流程


    本文详细分析spring创建对象的完整流程。
    入口:AbstractBeanFactory#doGetBean

    从缓存中取实例

    		// 名称转换,如果传入的是别名会转换为bean的真正名称;如果是针对FactoryBean的,会去掉&
    		String beanName = transformedBeanName(name);
    		Object beanInstance;
    
    		// 从缓存中取实例
    		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 + "'");
    				}
    			}
    			// 对FactoryBean类行的实例进行特殊处理
    			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    缓存逻辑

    	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    		// spring创建的缓存都是保存在singletonObjects这个map中的。
    		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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    双检查加锁,缓存中没有的话,从暴露的引用中找,能找到就返回早期的引用;找不到就返回null。这里设计循环引用的逻辑,单独分析。。。

    注意:
    这里为什么会有三个缓存呢?
    第一个缓存singletonObjects,用来保存已经实例化好,并且初始化好的的对象的
    第二个缓存earlySingletonObjects,是用来保存已经实例化好的对象,但是没有初始化,所以他叫早期的单例对象,为的是解决循环依赖
    第三个缓存singletonFactories,是创建对象的接口,可以理解为懒创建,等需要的时候再创建。

    对FactoryBean类型的实例特殊处理

    AbstractBeanFactory#getObjectForBeanInstance

    protected Object getObjectForBeanInstance(
    			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    
    		// name如果是&开头,说明要的是FactoryBean的实例
    		if (BeanFactoryUtils.isFactoryDereference(name)) {
    			// 如果是nullBean直接返回
    			if (beanInstance instanceof NullBean) {
    				return beanInstance;
    			}
    			// 如果实例不是FactoryBean类型的,抛异常
    			if (!(beanInstance instanceof FactoryBean)) {
    				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
    			}
    			// 如果是第一个创建,这个这个bd的属性是true
    			if (mbd != null) {
    				mbd.isFactoryBean = true;
    			}
    			// 返回实例,说明工厂中保存的是factoryBean的实例,如果用户要FactoryBean的实例,name一定是&开头,直接返回实例,如果不是&开头,就是下面的逻辑了。
    			return beanInstance;
    		}
    
    		// 如果是普通的类型,直接返回实例。
    		if (!(beanInstance instanceof FactoryBean)) {
    			return beanInstance;
    		}
    		// 下面的逻辑是获得factoryBean中的普通类型的实例
    		Object object = null;
    		// 如果第一次创建,bd属性设置为true
    		if (mbd != null) {
    			mbd.isFactoryBean = true;
    		}
    		else {
    			// 如果不是第一次创建,直接中缓存中取。并返回
    			// private final Map factoryBeanObjectCache = new ConcurrentHashMap<>(16);
    			// factoryBeanObjectCache key是beanName;value是实例。
    			object = getCachedObjectForFactoryBean(beanName);
    		}
    		// 如果是第一次创建
    		if (object == null) {
    			// 将实例转换成factoryBean类型,
    			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    			// Caches object obtained from FactoryBean if it is a singleton.
    			if (mbd == null && containsBeanDefinition(beanName)) {
    				mbd = getMergedLocalBeanDefinition(beanName);
    			}
    			boolean synthetic = (mbd != null && mbd.isSynthetic());
    			// 从factoryBean中获取实例
    			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    		}
    		return object;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    bean工厂中保存的是factoryBean的实例,如果name是以&开头,直接返回实例,如果不带,获取的可能是普通实例,对实例进行判断,如果实例不是FactoryBean类型的,直接返回实例。否则就特殊判断。

    	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    		// 如果是单例的,并且这个factoryBean已经创建完成了
    		if (factory.isSingleton() && containsSingleton(beanName)) {			
    			synchronized (getSingletonMutex()) {
    				// 从缓存中取,如果不为null,直接返回
    				Object object = this.factoryBeanObjectCache.get(beanName);
    				if (object == null) {
    					// 调用factoryBean的getObejct()方法的到实例
    					object = doGetObjectFromFactoryBean(factory, beanName);
    					// 有调用一次???
    					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
    					if (alreadyThere != null) {
    						object = alreadyThere;
    					}
    					else {
    						if (shouldPostProcess) {
    						// 
    							if (isSingletonCurrentlyInCreation(beanName)) {
    								// Temporarily return non-post-processed object, not storing it yet..
    								return object;
    							}
    							// 把这个beanName放入正在创建bean的集合汇总
    							beforeSingletonCreation(beanName);
    							try {
    								// 应用bean处理器
    								object = postProcessObjectFromFactoryBean(object, beanName);
    							}
    							catch (Throwable ex) {
    								throw new BeanCreationException(beanName,
    										"Post-processing of FactoryBean's singleton object failed", ex);
    							}
    							finally {
    								// 正在创建的bean中移除
    								afterSingletonCreation(beanName);
    							}
    						}
    						// 放入缓存中
    						if (containsSingleton(beanName)) {
    							this.factoryBeanObjectCache.put(beanName, object);
    						}
    					}
    				}
    				return object;
    			}
    		}
    		// 如果FactoryBean创建的实例是多例的,直接从FactoryBean的getObject()方法中取。
    		else {
    			Object object = doGetObjectFromFactoryBean(factory, beanName);
    			if (shouldPostProcess) {
    				try {
    					// bean的处理器对bean实例进行处理
    					object = postProcessObjectFromFactoryBean(object, beanName);
    				}
    				catch (Throwable ex) {
    					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
    				}
    			}
    			return object;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    调用factoryBean的getObejct()方法,并将实例放入缓存。如果需要,应用bean处理器。

    小结:
    直接从已经创建的bean中找,找到了进入getObjectForBeanInstance, 这个方法主要是操作FactoryBean.容器中保存的是FactoryBean的名称和实例,如果传入的名称开头是&,要找的是FactoryBean类型,直接返回实例;如果不是&开头,要找的是getObejct()方法中的示例。返回该方法的结果。这块有个单例和多例的判断,单例的会把结果放到缓存中,下次调用直接从缓存中取,多例的会直接调用getObject方法。


    前面是已经创建完了,直接从工厂取的逻辑,下面分析的是第一次创建实例的逻辑:

    创建前的工作

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    检查父容器

    			// 如果正在创建,则直接抛出异常,(应该是多线程下创建同一个对象)
    			if (isPrototypeCurrentlyInCreation(beanName)) {
    				throw new BeanCurrentlyInCreationException(beanName);
    			}
    
    			// 检查是否有父工厂
    			BeanFactory parentBeanFactory = getParentBeanFactory();
    			// 如果有父工厂,并且自己的工厂中没有注册bd信息。则从父工厂中实例化出来。
    			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);
    				}
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    如果有父容器,并且本容器并没有这个beanName,就让父容器尝试取实力化这个bean


    创建依赖的实例

    				if (requiredType != null) {
    					beanCreation.tag("beanType", requiredType::toString);
    				}
    				// 合并bd信息
    				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    				checkMergedBeanDefinition(mbd, beanName, args);
    
    				// 从bd信息中找出依赖的bean的名称。这个的依赖应该是注解标注的@DependOn
    				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 {
    						// 先实例化依赖的bean,依赖的bean全部实例化完成后,再实例化自己。
    							getBean(dep);
    						}
    						catch (NoSuchBeanDefinitionException ex) {
    							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
    						}
    					}
    				}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    如果这个bean有依赖,要先实例化依赖的,之后再实例化本bean.


    创建单例的实例

    // 如果是单例的
    if (mbd.isSingleton()) {
    	// 进入getSingleton方法,创建实例。
    	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;
    		}
    	});
    	// getObjectForBeanInstance这个方法在取实例的实例分析过了,就是对factoryBean有特殊判断,如果是普通实例,直接返回,如果是FactoryBean类型的会调用getObejct()方法。
    	beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    DefaultSingletonBeanRegistry#getSingleton

    // beanName:bean的名称
    //  singletonFactory:是一个接口,传进来来创建bean的过程方法。
    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 + "'");
    			}
    			// 放入singletonsCurrentlyInCreation的set中,表示该bean正在创建。
    			beforeSingletonCreation(beanName);
    			boolean newSingleton = false;
    			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    			if (recordSuppressedExceptions) {
    				this.suppressedExceptions = new LinkedHashSet<>();
    			}
    			try {
    				// 调用getObject方法,上一层已经将createBean传入了,返回的结果是createBean的结果,将创建的过程交给调用方。
    				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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    getSingleton方法中,主要是在实例化bean前后做逻辑判断,比如实例化前,放入到正在实例化的集合。实例化后,放入到缓存。


    创建实例

    AbstractAutowireCapableBeanFactory#createBean

    		。。。。
    
    		try {
    			// 调用bean实例化的处理器。下面单独分析下
    			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    			// 如果bean!= null 说明我们自己实例化的对象,不用spring实例化了,直接返回实例。
    			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;
    		}
    		
    		。。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    先调用处理器方法,之后进行实例化逻辑。

    调用postProcessBeforeInstantiation方法

    AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation

    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    	Object bean = null;
    	// 如果这个bean需要实力化前解析,则进入逻辑
    	if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
    		// 如果bean工厂中有实例化处理器
    		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    			Class<?> targetType = determineTargetType(beanName, mbd);
    			if (targetType != null) {
    				// 调用实例化处理器的方法
    				bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    				// 如果不是null,说明是自定义的实例创建
    				if (bean != null) {
    					// 调用初始化后处理方法。
    					bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    				}
    			}
    		}
    		mbd.beforeInstantiationResolved = (bean != null);
    	}
    	return bean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    调用bean处理器的postProcessBeforeInstantiation方法,如果返回实例了,说明在处理器方法中创建好了,不用spring给创建了。创建好的直接应用处理器的实例化方法。


    创建实例逻辑

    AbstractAutowireCapableBeanFactory#doCreateBean

    // 先分析下方法参数
    // beanName:bean名称
    // mbd:bd信息
    // args:构造器的参数
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    		throws BeanCreationException {
    
    	// 实例化bean
    	BeanWrapper instanceWrapper = null;
    	if (mbd.isSingleton()) {
    		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    	}
    	// createBeanInstance是真正的创建bean实例的方法
    	// 实例对象被包装在了BeanWrapper中
    	if (instanceWrapper == null) {
    		instanceWrapper = createBeanInstance(beanName, mbd, args);
    	}
    	Object bean = instanceWrapper.getWrappedInstance();
    	Class<?> beanType = instanceWrapper.getWrappedClass();
    	if (beanType != NullBean.class) {
    		mbd.resolvedTargetType = beanType;
    	}
    
    	// 这个调用MergedBeanDefinitionPostProcessor处理器的postProcessMergedBeanDefinition方法
    	// processor.postProcessMergedBeanDefinition(mbd, beanType, beanName)
    	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;
    		}
    	}
    
    	// 这里与循环依赖相关,会单独分析。先略过吧
    	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));
    	}
    
    	// 下面是初始化的操作了
    	Object exposedObject = bean;
    	try {
    		// 属性注入
    		populateBean(beanName, mbd, instanceWrapper);
    		// 初始化,主要是bean的生命周期相关的操作了,会单独分析
    		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);
    		}
    	}
    	。。。。
    
    	return exposedObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    有三个重要的逻辑,会分别详细分析各模块的逻辑。
    实例化对象。
    createBeanInstance(beanName, mbd, args);
    【Spring-5.1】AbstractAutowireCapableBeanFactory#createBeanInstance实例化对象
    计算属性,单独分析
    populateBean(beanName, mbd, instanceWrapper);
    初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);

  • 相关阅读:
    台式机卡死救机心情点滴记录
    JS+jQuery常用方法笔记
    包含BN层的神经网络的学习率优化
    linux文件存储之inode,硬链接,软链接详解
    qt5.15源码编译
    C语言 每日一题 PTA 10.27 day5
    nodejs国内镜像及切换版本工具nvm
    Ajax--Ajax加强--XMLHttpRequest的基本使用
    小黑子—MyBatis:第三章
    无人机航迹规划:五种最新智能优化算法(COA、SWO、KOA、GRO、LO)求解无人机路径规划MATLAB
  • 原文地址:https://blog.csdn.net/qq_34501351/article/details/118051593