Spring Bean创建流程
Spring Bean 创建流程图
大家总是会错误的理解Bean的“实例化”和“初始化”过程,总会以为初始化就是对象执行构造函数生成对象实例的过程,其实不然,在初始化阶段实际对象已经实例化出来了,初始化阶段进行的是依赖的注入和执行一些用户自定义的初始化逻辑
Bean 实例化
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// 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);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// 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) {...}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}}}
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();}
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);}}}...}}
函数会先获取所有需要构建的Bean名称,通过bean的RootBeanDefinition判断该bean是否为可构建的类型,很明显可构建的Bean不能是抽象类,不能是接口,也不能是懒加载的bean。之后会判断对象是不是FactoryBean,FactoryBean是一种特殊的Bean,需要特殊处理。
FactoryBean简介
在我们平时的开发中经常会使用第三方库相关类,为了避免接口和实现类的耦合,我们通常会使用工厂模式。提供一个工厂类来实例化具体接口实现类。主体对象只需要注入工厂类,具体接口实现类有变更时,我们只需变更工厂类,而主体类无需做任何改动。针对上面的场景,spring为我们提供了更方便的工具FactoryBean,当某些第三方库不能直接注册到spring容器时,就可以实现org.springframework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑
获取 Bean 的方法是 getBean,其来自 DefaultListableBeanFactory 的父类 AbstractAutowireCapableBeanFactory 的父类 AbstractBeanFactory
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}@Overridepublic <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {return doGetBean(name, requiredType, null, false);}@Overridepublic Object getBean(String name, Object... args) throws BeansException {return doGetBean(name, null, args, false);}...
}
getBean 有多个重载方法,可分为通过 Bean 名称或通过 Class 获取 Bean 对象,这些重载方法底层都是调用 doGetBean 方法
【从缓存拿或者创建】doGetBean
// 为了简洁美观,这里将源码中的部分非核心内容进行了删减
protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {// 特殊处理FactoryBean的名称// 解决别名的问题final String beanName = transformedBeanName(name);Object bean;// 单例 Bean 只会被创建一次,后续再获取 Bean,直接从单例缓存中获取(共有三级缓存)Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// 如果存在缓存的 Bean// 获取 Bean,正常 Bean 直接返回,如果是 FactoryBean 则返回 FactoryBean.getObject()bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// 检验逻辑if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(...);}// 父子容器的处理BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 当父容器不为空并且获取的 Bean 不在子容器定义时,从父容器递归获取String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);} else if (args != null) {// 递归到父容器中寻找return (T) parentBeanFactory.getBean(nameToLookup, args);} else if (requiredType != null) {return parentBeanFactory.getBean(nameToLookup, requiredType);} else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {// 将 Bean 标记为已经创建(或将要创建),即将 beanName 加入 alreadyCreated 集合markBeanAsCreated(beanName);}try {// 获取合并后的父子 Bean,对应 <bean id="p" abstract="true"> 和 <bean id="c" parent="p" 的用法// 将子标签 bean 的各个属性合并到父标签,生成 RootBeanDefinitionfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// 获取目标 bean 所依赖的其它 bean 名称// @DependsOn注解发挥作用的地方,如果我们的Bean在构建前必须要保证某个Bean已经构建好,那么我们就可以使用这个注解String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(...);}// 若存在依赖则需要递归实例化依赖的 beanregisterDependentBean(dep, beanName);try {getBean(dep);} // catch...}}if (mbd.isSingleton()) {// 创建单例 BeansharedInstance = getSingleton(beanName, () -> {try {// 创建 Bean 的核心方法return createBean(beanName, mbd, args);} // catch...});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {// 创建原型 BeanObject prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else {// 创建其他作用域的 BeanString scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException(...);}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} // catch...}} // catch...}// 将 Bean 的类型转换为 getBean 时指定的 requireType 类型if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(...);}return convertedBean;} // catch...}return (T) 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) {// this.singletonsCurrentlyInDestruction是个boolean类型,记录的是当前这个单例bean是否正在被销毁,// 如果是true,代表单例bean已经执行了自身的destroy销毁方法,或者有异常的时候执行了destroySingleton方法等情况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 + "'");}// 前置检查,通过构建中bean集合(singletonsCurrentlyInCreation)来检测该bean是否进行了重复构建,// 可以防止构造器注入可能导致的循环引用问题beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// 调用函数式接口ObjectFactory的具体实现逻辑,即上文中getSingleton方法的第二个参数,主要实现就是createBean方法singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// ...}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 后置处理,将当前bean从构建中bean集合(singletonsCurrentlyInCreation)中移除。afterSingletonCreation(beanName);}if (newSingleton) {// 把结果存在singletonObjects中,并删除一些用于处理循环引用的中间状态addSingleton(beanName, singletonObject);}}return singletonObject;}}
简单概述一下整个过程:
第一步【转换名称】:解析转换传入的获取 Bean 的名字,可以分为三种情况:
传入的直接是 ID,则无须转换
传入的是 Bean 的别名,转换成 Bean 的 ID 返回
传入的是 FactoryBean 的 & 写法,,转换成 Bean 的 ID 返回
第二步【从缓存中获取】:单例作用域的 Bean 只会创建一次,之后会存储在 Spring 的缓存中(共计三层)。如果缓存中存在目标 Bean,则获取 Bean,正常 Bean 直接返回,如果是 FactoryBean 则 返回 FactoryBean.getObject()。获取到 Bean 之后跳到第八步【类型转换】。
第三步【父子容器的处理】:如果没有从上面的缓存中拿到数据,说明这次要重新创建 Bean。首先进行父子容器的处理,当父容器不为空并且获取的 Bean 不在当前容器定义时,从父容器递归获取。
第四步【标记 Bean 为被创建】:将 Bean 标记为已经创建(或将要创建),即将 beanName 加入 alreadyCreated 集合。
第五步【合并父子 Bean】:获取合并后的父子 Bean,对应 <bean id="xx" abstract="true"> 和 <bean id="c" parent="xx" 的用法,这个步骤会将子标签 bean 的各个属性合并到父标签,生成 RootBeanDefinition。
第六步【处理依赖 Bean】:获取当前 Bean 是否配置依赖的其他的 Bean 的名称,若存在依赖则需要递归实例化依赖的其他 Bean。这个用法现在已经很少了,大家可以忽略。
第七步【根据作用域创建 Bean】:根据作用域可分为三类:单例(singleton)、原型(prototype)和其他,对应的 if 的三个分支,最后都会调用 createBean 创建 Bean。
第八步【类型转换】:如果获取 Bean 的时候传入了期望转换的类型 Class,那么这一步就会进行类型转换,之后就将创建的 Bean 返回给调用者了。
【创建Bean的入口】createBean
这个 createBean 方法是定义在 AbstractBeanFactory 中的抽象方法,最终的实现是交给了 AbstractAutowireCapableBeanFactory 类实现,所以接下来我们看看 AbstractAutowireCapableBeanFactory 这个类。
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {RootBeanDefinition mbdToUse = mbd;// 根据设置的 class 属性或 className 来解析得到 Class 引用赋值给 RootBeanDefinitionif (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// 对 override 属性进行标记和验证,本质上是处理 lookup-method 和 replaced-method 标签try {mbdToUse.prepareMethodOverrides();} // catch...try {// InstantiationAwareBeanPostProcessor接口继承自BeanPostProcessor接口// 执行 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),如果有 Bean 返回,则不执行接下来创建 Bean 的操作,直接返回该 Bean// 实际上这里的代码是不走的Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}} // catch...try {// 最后会执行到这里,这是创建 Bean 的核心方法Object beanInstance = doCreateBean(beanName, mbdToUse, args);return beanInstance;} // catch...
}
该方法整体流程如下:
- 如果是单例,尝试从缓存中获取 Bean 的包装器 BeanWrapper,并清除缓存
- 如果不存在对应的 Wrapper,则说明 Bean 未被实例化,创建 Bean 实例
- 执行 MergedBeanDefinitionPostProcessor 后置处理器
- 检查是否需要提前曝光,避免循环依赖
- 属性填充,将所有属性填充至 Bean 的实例中
- 执行一系列的初始化方法(回调钩子接口)
- 再次检查是否存在循环依赖
- 注册 DisposableBean
【初始化和实例化方法】doCreateBean
BeanWrapper相当于一个代理器,Spring委托BeanWrapper完成Bean属性的填充工作。BeanWrapper继承了PropertyAccessor和PropertyEditorRegistry。BeanWrapperImpl是BeanWrapper接口的默认实现,它会缓存Bean的内省结果而提高效率。BeanWrapper对bean实例的操作很方便,可以免去直接使用java反射API带来的繁琐。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {// 实例化bean,并封装进BeanWrapper中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;}
【创建Bean的核心逻辑】createBeaninstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {Class<?> beanClass = resolveBeanClass(mbd, beanName);if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(...);}// 如果有用于创建 Bean 实例的回调方法Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 如果工厂方法不为空,则使用工厂方法进行实例化if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// 利用构造函数进行实例化,解析并确定目标构造函数// 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,避免再次创建相同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) {// 依据构造函数注入return autowireConstructor(beanName, mbd, null, null);}else {// 使用默认构造函数构造return instantiateBean(beanName, mbd);}}// 根据参数确定构造函数Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// 使用默认的构造函数return instantiateBean(beanName, mbd);
}
上面方法的目的就是选出一个策略来实例化一个对象,那有什么策略呢? 这就看程序员是怎么配置的了,程序员可以配置工厂方法,指定构造方法,或者是程序员没有做出任何干涉,让Spring按自己的方式去实例化。
上面代码的主要逻辑如下:
- 如果bean定义中存在 InstanceSupplier ,会使用这个回调接口创建对象(应该是3.X以后新加的,3.X的源码中没有)
- 根据配置的factoryMethodName或factory-method创建bean(通过工厂方法创建对象)
- 解析构造函数并进行实例化
因为一个类可能有多个构造函数,所以需要根据配置文件中配置的参数或传入的参数确定最终调用的构造函数,因为判断过程会比较消耗性能,所以Spring会将解析、确定好的构造函数缓存到BeanDefinition中的resolvedConstructorOrFactoryMethod字段中。在下次创建相同bean的时候,会直接从RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod缓存的值获取,避免再次解析。
determineConstructorsFromBeanPostProcessors
determineConstructorsFromBeanPostProcessors 方法会帮我们选择合适的构造器
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)throws BeansException {if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);if (ctors != null) {return ctors;}}}return null;}
在选定好了构造参数后,我们接下来看对象的实例化过程,Spring的实例化可以分为 【带参实例化】 及 【无参实例化】 。我们先来看下带参实例化方法autowireConstructor:
有参实例化
org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {BeanWrapperImpl bw = new BeanWrapperImpl();this.beanFactory.initBeanWrapper(bw);Constructor<?> constructorToUse = null;ArgumentsHolder argsHolderToUse = null;Object[] argsToUse = null;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 = 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)");}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");bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));return bw;}
上面代码非常长,总体的功能逻辑如下:
- 确定实例化时构造器传入的参数:
- 如果调用getBean方式时传入的参数不为空,则可以直接使用传入的参数。
- 否则尝试从缓存中获取参数。
- 否则解析xml中配置的构造器参数。
- 确定使用的构造函数: 根据第一步中确定下来的参数,接下来的任务就是根据参数的个数、类型来确定最终调用的构造函数。首先是根据参数个数匹配,把所有构造函数根据参数个数升序排序,筛选出参数个数匹配的构造函数。因为配置文件中可以通过参数位置索引,也可以通过参数名称来设定参数值,所以还需要解析参数的名称。最后根据解析好的参数名称、参数类型、实际参数就可以确定构造函数,并且将参数转换成对应的类型。
- 构造函数不确定性的验证。 因为有一些构造函数的参数类型为父子关系,所以Spring会做一次验证。
- 如果条件符合(传入参数为空),将解析好的构造函数、参数放入缓存。
- 根据实例化策略通过构造函数、参数实例化bean。
实例化策略
private Object instantiate(String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) {try {InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();if (System.getSecurityManager() != null) {return AccessController.doPrivileged((PrivilegedAction<Object>) () ->strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),this.beanFactory.getAccessControlContext());}else {return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);}}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean instantiation via constructor failed", ex);}}
对象的实例化主要有两种策略:SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。其中 SimpleInstantiationStrategy 通过反射方式创建对象,而CglibSubclassingInstantiationStrategy 通过Cglib来创建对象
无参实例化
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {try {Object beanInstance;if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),getAccessControlContext());}else {beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);}BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}}
可以看到无参实例化的方法和带有参数创建bean一样,都是使用InstantiationStrategy来创建bean,但是因为没有参数,所以也就没有必要执行繁琐的确定构造函数的代码,只需要使用无参构造器进行实例化就行了
不管是通过工厂方法还是构造方法来实例化对象,到这里得到的也仅仅是一个 Bean 的最初实例,还不是我们最终期望的 Bean,因为后面还需要对 Bean 实例进行初始化处理,注入相应的属性值等。
Bean 初始化
【属性填充】populateBean
这个方法主要进行bean属性的装配工作,即依赖对象的注入。下面的逻辑看着很多,但其实很多逻辑都是为了处理旧版本XML属性配置的,但当我们使用@Autowired时,很多逻辑并不会执行。例如下面的pvs是一个MutablePropertyValues实例,里面实现了PropertyValues接口,提供属性的读写操作实现,同时可以通过调用构造函数实现深拷贝,但这个pvs主要是用来处理XML配置的属性的,当我们使用@Autowired时,其值基本都是null或者为空。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// 判断实例化的 Bean 是否为空if (bw == null) {// 返回是否有为此 Bean 定义的属性值,如果有,则抛异常,提示 “无法将属性值应用于空实例”if (mbd.hasPropertyValues()) {throw new BeanCreationException(...);}// 没有,则跳过属性填充阶段以获取空实例else {return;}}// 在进行依赖注入前,给InstantiationAwareBeanPostProcessors最后的机会根据用户需求自定义修改Bean的属性值// 如果此处返回了false,则后面不会再进行依赖注入。boolean continueWithPropertyPopulation = true;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}}// 当使用了 InstantiationAwareBeanPostProcessors 后置处理器注入属性,则结束属性注入流程,直接返回if (!continueWithPropertyPopulation) {return;}// 获取 Bean 实例的属性值集合PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// 根据名称自动注入if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// 根据类型自动注入if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}// 返回此工厂是否拥有 InstantiationAwareBeanPostProcessorboolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();// 是否进行依赖检查boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);if (hasInstAwareBpps || needsDepCheck) {if (pvs == null) {pvs = mbd.getPropertyValues();}PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);// 在属性注入前执行 InstantiationAwareBeanPostProcessor 后置处理器// @Autoware 的实现原理 // 这里涉及一个极其重要的后置处理器实现 AutowiredAnnotationBeanPostProcessor,其主要用来处理 @Autowired 注解,这部分会在后面详细讨论if (hasInstAwareBpps) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvs == null) {return;}}}}// 进行依赖检查if (needsDepCheck) {checkDependencies(beanName, mbd, filteredPds, pvs);}}if (pvs != null) {// 这里进行属性的类型转换,例如将字符串的 id 转为数字的 id// 执行属性注入,这里才真正的将属性进行填充,将解析和类型转换好的属性列表(pvs)赋值到包装对象(bw)上applyPropertyValues(beanName, mbd, bw, pvs);}
}
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {// ...// Create a deep copy, resolving any references for values.List<PropertyValue> deepCopy = new ArrayList<>(original.size());boolean resolveNecessary = false;for (PropertyValue pv : original) {// 遍历解析到的每个属性if (pv.isConverted()) {deepCopy.add(pv);}else {String propertyName = pv.getName();// 如果是 JDK 类型,返回的是 TypeStringValue// 如果是要注入其他的 Bean,则是 RuntimeBeanReference 类型Object originalValue = pv.getValue();// 这里是个重点,进行属性的解析,如果是 JDK 类型则返回的对应 JDK 的类型// 如果是 RuntimeBeanReference,则会递归调用工厂的 getBean 获取到要注入的对象Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);Object convertedValue = resolvedValue;boolean convertible = bw.isWritableProperty(propertyName) &&!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {// 进行类型准换convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);}if (resolvedValue == originalValue) {if (convertible) {// 将类型转换好的数据赋值给 PropertyValuepv.setConvertedValue(convertedValue);}deepCopy.add(pv);}else if (convertible && originalValue instanceof TypedStringValue &&!((TypedStringValue) originalValue).isDynamic() &&!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);}else {resolveNecessary = true;deepCopy.add(new PropertyValue(pv, convertedValue));}}}if (mpvs != null && !resolveNecessary) {mpvs.setConverted();}try {// 这里才真正的对创建的包装对象进行了属性的赋值,底层肯定是用到了反射了bw.setPropertyValues(new MutablePropertyValues(deepCopy));}catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);}
}
【初始化】initializeBean
接着进入 initializeBean 方法,在该方法中会回调许多在 Bean 初始化阶段执行的方法。
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 回调 Aware 系列接口if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}// 回调 BeanPostProcessor 后置处理器的 postProcessBeforeInitialization 方法(此处会执行@PostConstruct注解方法,且部分Aware接口会在此处处理)Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 执行InitializingBean接口的afterPropertiesSet方法及init-method方法invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// 回调 BeanPostProcessor 后置处理器的 postProcessAfterInitialization 方法wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}
在 Bean 的初始化阶段,分别回调了 Aware 系列接口、BeanPostProcessor 后置处理器、InitializingBean。这三个接口都属于 Spring 的钩子接口,是 Spring 开放出来的扩展接口,可以影响 Bean 的生命周期。
回调 Aware 系列接口
private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) {// 如果当前 Bean 继承了 BeanNameAware 接口,则回调接口中的 setBeanName 方法,并传递 beanName 参数if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}// 这个 BeanClassLoaderAware 接口传递的是 ClassLoaderif (bean instanceof BeanClassLoaderAware) {ClassLoader bcl = getBeanClassLoader();if (bcl != null) {((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);}}// 这个 BeanFactoryAware 接口传递的是 AbstractAutowireCapableBeanFactoryif (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}
}
回调 BeanPostProcessor 后置处理器的 postProcessBeforeInitialization 方法
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {Object result = existingBean;// getBeanPostProcessors 方法获取所有的 BeanPostProcessor 后置处理器的实现// 并循环执行 postProcessBeforeInitialization 方法,参数是当前 Bean 以及 beanNamefor (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}
其中有一个后置处理器的实现 ApplicationContextAwareProcessor ,其用来对剩余的 Aware 接口进行回调:
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {AccessControlContext acc = null;//...invokeAwareInterfaces(bean);//...return bean;
}private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}
}
回调 InitializingBean 的 afterPropertiesSet 方法
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {//..((InitializingBean) bean).afterPropertiesSet();//..
}
回调 BeanPostProcessor 后置处理器的 postProcessAfterInitialization 方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {Object result = existingBean;// getBeanPostProcessors 方法获取所有的 BeanPostProcessor 后置处理器的实现// 并循环执行 postProcessAfterInitialization 方法,参数是当前 Bean 以及 beanNamefor (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {Object current = beanProcessor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}
到这里,创建 Bean 的核心流程就讨论结束,我这里做一个完整的归纳:
- 调用者通过 getBean 方法调用工厂获取一个 Bean,传入的 name 可以是 Bean 的 id;也可以是别名;也可以是 &name,表示获取原始的 FactoryBean 对象。
- 通过一系列的方法重载,最后会到达 AbstractBeanFactory#doGetBean 方法处理,先要对传入的 name 进行解析,解析出 id。
- 接下来会尝试从单例缓存池中获取已创建好的对象,Spring 底层提供了三级缓存(三个Map)体系来解决循环依赖的问题:singletonObjects、earlySingletonObjects、singletonFactories。
- 如果拿到了缓存数据:对 FactoryBean 类型进行判定,决定是否直接返回缓存对象还是返回 FactoryBean.getObject()。
- 如果没有拿到缓存数据,说明要新建一个 Bean。
- 首先进行父子容器的处理,当父容器不为空并且获取的 Bean 不在子容器定义时,从父容器递归获取。
- 将 Bean 标记为已经创建(或将要创建),即将 beanName 加入 alreadyCreated 集合。
- 父子 Bean 的处理,将子标签 bean 的各个属性合并到父标签,生成 RootBeanDefinition。
- 处理 depends-on,若存在依赖则需要递归实例化依赖的 bean。
- 根据不同的作用域创建 Bean 实例,分为单例、原型和其他,这三种创建的时候都会调用 createBean 方法,这个方法由 AbstractAutowireCapableBeanFactory 提供。
- 首先需要处理 lookup-method 和 replaced-method。
- 然后要将创建实例的职责交给 doCreateBean,这个方法大概有三个重要的步骤:
- 创建实例 createBeaninstance:使用工厂方法实例化还是使用构造函数,默认是无参构造。这里返回的是包装对象,里面还封装了类型转换器的信息。
- 属性填充 populateBean:填充的时候设计到类型转换,底层再通过反射对创建的包装对象进行属性赋值。
- 初始化 initializeBean:在这个步骤,分别回调了 Aware 系列接口、BeanPostProcessor 后置处理器、InitializingBean。
- 如果 getBean 的时候传入了类型(getBean("user", User.clsss)),则需要进行类型转换后返回
Bean 销毁
在进行好依赖注入及各种操作后,容器会检查singleton类型的bean实例
- 是否实现了DisposableBean接口
- 是否实现了destory-method
- 是否利用@PreDestroy标注了销毁前需要执行的方法
如果满足任意条件,就会为该bean实例注册一个用于对象销毁的回调,以便在这些singleton类型的对象实例在销毁之前,执行销毁逻辑。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean.registerDisposableBeanIfNecessary
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {if (mbd.isSingleton()) {// Register a DisposableBean implementation that performs all destruction// work for the given bean: DestructionAwareBeanPostProcessors,// DisposableBean interface, custom destroy method.registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}else {// A bean with a custom scope...Scope scope = this.scopes.get(mbd.getScope());if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");}scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}}}
这些自定义的对象销毁逻辑,在对象实例初始化完成,并注册了相关的回调接口后,并不会马上执行。回调方法注册后,返回的对象即处于“可用”状态,只有该对象不再被使用的时候,才会执行相关的自定义销毁逻辑,此时通常也就是spring容器关闭的时候。在ApplicationContext容器中销毁接口的回调主要是通过jvm的shutdownHook实现的,ApplicationContext容器通过registerShutdownHook()方法注册jvm关闭的回调钩子,保证在jvm退出前,这些singleton类型bean对象的自定义销毁逻辑会被执行。
【spring容器启动】之bean的实例化和初始化(文末附:spring循环依赖原理)_实例化bean和初始化bean-CSDN博客
揭秘Spring生命周期:Bean的创建过程超详细解析_spring bean的生命周期创建过程-CSDN博客
相关文章:
Spring Bean创建流程
Spring Bean 创建流程图 大家总是会错误的理解Bean的“实例化”和“初始化”过程,总会以为初始化就是对象执行构造函数生成对象实例的过程,其实不然,在初始化阶段实际对象已经实例化出来了,初始化阶段进行的是依赖的注入和执行一…...
重学SpringBoot3-怎样优雅停机
更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机?2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制3.1 Tomcat 优雅停机3.2 Reac…...
【数据结构】顺序表和链表
1.线性表 我们在C语言当中学过数组,其实呢,数组可以实现线性表,线性表理解上类似于数组,那么什么是线性表呢?线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使 用的数据结构,常见…...
Training language models to follow instructions with human feedback解读
前置知识方法数据集结论 前置知识 GPT的全称是Generative Pre-Trained Transformer,预训练模型自诞生之始,一个备受诟病的问题就是预训练模型的偏见性。因为预训练模型都是通过海量数据在超大参数量级的模型上训练出来的,对比完全由人工规则…...
线性回归矩阵求解和梯度求解
正规方程求解线性回归 首先正规方程如下: Θ ( X T X ) − 1 X T y \begin{equation} \Theta (X^T X)^{-1} X^T y \end{equation} Θ(XTX)−1XTy 接下来通过线性代数的角度理解这个问题。 二维空间 在二维空间上,有两个向量 a a a和 b b b&…...
M3U8不知道如何转MP4?包能学会的4种格式转换教学!
在流媒体视频大量生产的今天,M3U8作为一种基于HTTP Live Streaming(HLS)协议的播放列表格式,广泛应用于网络视频直播和点播中。它包含了媒体播放列表的信息,指向了视频文件被分割成的多个TS(Transport Stre…...
C++第4课——swap、switch-case-for循环(含视频讲解)
文章目录 1、课程代码2、课程视频 1、课程代码 #include<iostream> using namespace std; int main(){/* //第一个任务:学会swap int a,b,c;//从小到大排序输出 升序 cin>>a>>b>>c;//5 4 3if(a>b)swap(a,b);//4 5 3 swap()函数是用于交…...
大数据新视界 -- 大数据大厂之大数据重塑影视娱乐产业的未来(4 - 4)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
在Java中,需要每120分钟刷新一次的`assetoken`,并且你想使用Redis作为缓存来存储和管理这个令牌
学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把手教你开发炫酷的vbs脚本制作(完善中……) 4、牛逼哄哄的 IDEA编程利器技巧(编写中……) 5、面经吐血整理的 面试技…...
linux网络编程7——协程设计原理与汇编实现
文章目录 协程设计原理与汇编实现1. 协程概念2. 协程的实现2.1 setjmp2.2 ucontext2.3 汇编实现2.4 优缺点2.5 实现协程原语2.5.1 create()2.5.2 yield()2.5.3 resume()2.5.4 exit()2.5.5 switch()2.5.6 sleep() 2.6 协程调度器 3. 利用hook使用协程版本的库函数学习参考 协程设…...
Ubuntu22.04版本左右,扩充用户可使用内存
1 取得root权限后,输入命令 lsblk 查看所有磁盘和分区,找到想要替换用户可使用文件夹内存的磁盘和分区。若没有进行分区,并转为所需要的分区数据类型,先进行分区与格式化,过程自行查阅。 扩充替换过程,例如…...
基于ArcMap中Python 批量处理栅格数据(以按掩膜提取为例)
注:图片来源于公众号,公众号也是我自己的。 ArcMap中的python编辑器是很多本科生使用ArcMap时容易忽略的一个工具,本人最近正在读一本书《ArcGIS Python 编程基础与应用》,在此和大家分享、交流一些相关的知识。 这篇文章主要分享…...
【flink】之集成mybatis对mysql进行读写
背景: 在现代大数据应用中,数据的高效处理和存储是核心需求之一。Flink作为一款强大的流处理框架,能够处理大规模的实时数据流,提供丰富的数据处理功能,如窗口操作、连接操作、聚合操作等。而MyBatis则是一款优秀的持…...
Java设计模式—观察者模式详解
引言 模式角色 UML图 示例代码 应用场景 优点 缺点 结论 引言 观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知…...
【Cri-Dockerd】安装cri-dockerd
cri-dockerd的作用: 在k8s1.24之前。k8s会通过dockershim来调用docker进行容器运行时containerd,并且会自动安装dockershim,但是从1.24版本之前k8s为了降低容器运行时的调用的复杂度和效率,直接调用containerd了,并且…...
GCC及GDB的使用
参考视频及博客 https://www.bilibili.com/video/BV1EK411g7Li/?spm_id_from333.999.0.0&vd_sourceb3723521e243814388688d813c9d475f https://www.bilibili.com/video/BV1ei4y1V758/?buvidXU932919AEC08339E30CE57D39A2BABF6A44F&from_spmidsearch.search-result.0…...
大数据新视界 -- 大数据大厂之大数据重塑影视娱乐产业的未来(4 - 3)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
数据结构——基础知识补充
1.队列 1.普通队列 queue.Queue 是 Python 标准库 queue 模块中的一个类,适用于多线程环境。它实现了线程安全的 FIFO(先进先出)队列。 2.双端队列 双端队列(Deque,Double-Ended Queue)是一种具有队列和…...
只有.git文件夹时如何恢复项目
有时候误删文件但由于.git是隐藏文件夹而幸存,或者项目太大,单单甩给你一个.git文件夹让你自己恢复整个项目,该怎么办呢? 不用担心,只要进行以下步骤,即可把原项目重新搭建起来: 创建一个文件…...
anchor、anchor box、bounding box之间关系
最近学YOLO接触到这些概念,一下子有点蒙,简单总结一下。 anchor和anchor box Anchor:表示一组预定义的尺寸比例,用来代表常见物体的宽高比。可以把它看成是一个模板或规格,定义了物体框的“形状”和“比例”ÿ…...
代码随想录算法训练营第三十天 | 452.用最少数量的箭引爆气球 435.无重叠区间 763.划分字母区间
LeetCode 452.用最少数量的箭引爆气球: 文章链接 题目链接:452.用最少数量的箭引爆气球 思路: 气球的区间有重叠部分,只要弓箭从重叠部分射出来,那么就能减少所使用的弓箭数 **局部最优:**只要有重叠部分…...
海亮科技亮相第84届中国教装展 尽显生于校园 长于校园教育基因
10月25日,第84届中国教育装备展示会(以下简称“教装展”)在昆明滇池国际会展中心开幕。作为国内教育装备领域规模最大、影响最广的专业展会,本届教装展以“数字赋能教育,创新引领未来”为主题,为教育领域新…...
C语言数据结构学习:栈
C语言 数据结构学习 汇总入口: C语言数据结构学习:[汇总] 1. 栈 栈,实际上是一种特殊的线性表。这里使用的是链表栈,链表栈的博客:C语言数据结构学习:单链表 2. 栈的特点 只能在一端进行存取操作&#x…...
如何快速分析音频中的各种频率成分
从视频中提取音频 from moviepy.editor import VideoFileClip# Load the video file and extract audio video_path "/mnt/data/WeChat_20241026235630.mp4" video_clip VideoFileClip(video_path)# Extract audio and save as a temporary file for further anal…...
MongoDB 6.0 主从复制配置
以下是 MongoDB 6.0 版本配置主从的详细安装步骤: 1. 安装 MongoDB:可以从官网下载 MongoDB 6.0 的安装包并进行安装,或者使用相应的包管理工具进行安装。 2. 配置主节点:在主节点的 MongoDB 配置文件(默认路径为 …...
NPU 神经网络处理单元
Ⅰ 什么是 NPU? 当前正处于神经网络和机器学习处理需求爆发的初期。传统的 CPU(中央处理器)/GPU(图形处理器)可以执行类似任务,但专门为神经网络优化的 NPU(神经处理单元)比 CPU/GP…...
安宝特分享 | AR技术引领:跨国工业远程协作创新模式
在当今高度互联的工业环境中,跨国合作与沟通变得日益重要。然而,语言障碍常常成为高效协作的绊脚石。安宝特AR眼镜凭借其强大的多语言自动翻译和播报功能,正在改变这一局面,让远程协作变得更加顺畅。 01 多语言翻译优势 安宝特A…...
Vulkan 开发(五):Vulkan 逻辑设备
图片来自《Vulkan 应用开发指南》 Vulkan 开发系列文章: 1. 开篇,Vulkan 概述 2. Vulkan 实例 3. Vulkan 物理设备 4. Vulkan 设备队列 在 Vulkan 中,逻辑设备(Logical Device)是与物理设备(Physical D…...
Kafka 解决消息丢失、乱序与重复消费
一、引言 在分布式系统中,Apache Kafka 作为一种高吞吐量的分布式发布订阅消息系统,被广泛应用于日志收集、流式处理、消息队列等场景。然而,在实际使用过程中,可能会遇到消息丢失、乱序、重复消费等问题,这些问题可能…...
计算机专业毕业生面试工具推荐:白瓜面试
随着毕业季的临近,计算机专业的毕业生们即将步入职场,面试成为了他们必须面对的挑战。在这个过程中,选择合适的面试工具可以大大提高求职成功率。今天,我要向大家推荐一款专为计算机专业毕业生设计的面试工具——白瓜面试。 为什…...
游戏网站的设计/百度下载并安装
linux 后台启动java的命令一例 查看已启动的进程实例命令: ps -aux|grep java启动java的进程实例命令: java -jar cloud-auth-1.0.jarjava -cp cloud-eureka-1.0-exec.jar:lib/*:./config/application.yml com.yzh.cloud.eureka.EurekaApplication后…...
厦门建设局耿家强/seo链接优化建议
曾经多次我的鼠标都是因为滚轴坏了而作废,我想这也是大部分小伙伴会遇到的问题。最近我的无线鼠标摔了一下,滚轴坏了。这次闲来无事,索性直接拆机,探索探索,看看可以不可以修好。结果还真的被朕修好了ahahah࿰…...
电脑怎么下免费wordpress/广州seo网络推广员
有关51单片机中断的形式和C语言编程格式void INT0()interrupt 0 using 1 {.... ..... } interrupt 0 指明是外部中断0; interrupt 1 指明是定时器中断0; interrupt 2 指明是外部中断1; interrupt 3 指明是定时器中断1; interrupt …...
上海it公司/站长工具seo优化建议
//简单邮件传输协议类 System.Net.Mail.SmtpClient client new System.Net.Mail.SmtpClient(); client.Host "smtp.163.com";//邮件服务器 client.Port 25;//smtp主机上的端口号,默认是25. client.DeliveryMetho…...
公司为什么建立网站/整合营销策略有哪些
关于讲理老公:你不讲理。老婆:和你我从来就没讲过理,家就不是讲理的地方。再说你是男的,还比我大8个月呢,你就得让着我。关于钱老公:以后我挣的钱,按比例给你吧,我挣的多时留得也多一…...
Asp.net 手机网站制作/淘宝推广哪种方式最好
1.算术运算符 注意: % 取余数 (1)自增 ()前自增:先自增完毕,再运算整个表达式,语句分号前面的都是运算表达式; 后自增,先运算完整个表达式(分号前面的都是表达式)&#x…...