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...
}

该方法整体流程如下:

  1. 如果是单例,尝试从缓存中获取 Bean 的包装器 BeanWrapper,并清除缓存
  2. 如果不存在对应的 Wrapper,则说明 Bean 未被实例化,创建 Bean 实例
  3. 执行 MergedBeanDefinitionPostProcessor 后置处理器
  4. 检查是否需要提前曝光,避免循环依赖
  5. 属性填充,将所有属性填充至 Bean 的实例中
  6. 执行一系列的初始化方法(回调钩子接口)
  7. 再次检查是否存在循环依赖
  8. 注册 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;}

上面代码非常长,总体的功能逻辑如下:

  1. 确定实例化时构造器传入的参数:
    1. 如果调用getBean方式时传入的参数不为空,则可以直接使用传入的参数。
    2. 否则尝试从缓存中获取参数。
    3. 否则解析xml中配置的构造器参数。
  2. 确定使用的构造函数: 根据第一步中确定下来的参数,接下来的任务就是根据参数的个数、类型来确定最终调用的构造函数。首先是根据参数个数匹配,把所有构造函数根据参数个数升序排序,筛选出参数个数匹配的构造函数。因为配置文件中可以通过参数位置索引,也可以通过参数名称来设定参数值,所以还需要解析参数的名称。最后根据解析好的参数名称、参数类型、实际参数就可以确定构造函数,并且将参数转换成对应的类型。
  3. 构造函数不确定性的验证。 因为有一些构造函数的参数类型为父子关系,所以Spring会做一次验证。
  4. 如果条件符合(传入参数为空),将解析好的构造函数、参数放入缓存。
  5. 根据实例化策略通过构造函数、参数实例化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 的核心流程就讨论结束,我这里做一个完整的归纳:

  1. 调用者通过 getBean 方法调用工厂获取一个 Bean,传入的 name 可以是 Bean 的 id;也可以是别名;也可以是 &name,表示获取原始的 FactoryBean 对象。
  2. 通过一系列的方法重载,最后会到达 AbstractBeanFactory#doGetBean 方法处理,先要对传入的 name 进行解析,解析出 id。
  3. 接下来会尝试从单例缓存池中获取已创建好的对象,Spring 底层提供了三级缓存(三个Map)体系来解决循环依赖的问题:singletonObjects、earlySingletonObjects、singletonFactories。
  4. 如果拿到了缓存数据:对 FactoryBean 类型进行判定,决定是否直接返回缓存对象还是返回 FactoryBean.getObject()。
  5. 如果没有拿到缓存数据,说明要新建一个 Bean。
    1. 首先进行父子容器的处理,当父容器不为空并且获取的 Bean 不在子容器定义时,从父容器递归获取。
    2. 将 Bean 标记为已经创建(或将要创建),即将 beanName 加入 alreadyCreated 集合。
    3. 父子 Bean 的处理,将子标签 bean 的各个属性合并到父标签,生成 RootBeanDefinition。
    4. 处理 depends-on,若存在依赖则需要递归实例化依赖的 bean。
    5. 根据不同的作用域创建 Bean 实例,分为单例、原型和其他,这三种创建的时候都会调用 createBean 方法,这个方法由 AbstractAutowireCapableBeanFactory 提供。
      1. 首先需要处理 lookup-method 和 replaced-method。
      2. 然后要将创建实例的职责交给 doCreateBean,这个方法大概有三个重要的步骤:
        1. 创建实例 createBeaninstance:使用工厂方法实例化还是使用构造函数,默认是无参构造。这里返回的是包装对象,里面还封装了类型转换器的信息。
        2. 属性填充 populateBean:填充的时候设计到类型转换,底层再通过反射对创建的包装对象进行属性赋值。
        3. 初始化 initializeBean:在这个步骤,分别回调了 Aware 系列接口、BeanPostProcessor 后置处理器、InitializingBean。
  6. 如果 getBean 的时候传入了类型(getBean("user", User.clsss)),则需要进行类型转换后返回

Bean 销毁

在进行好依赖注入及各种操作后,容器会检查singleton类型的bean实例

  1. 是否实现了DisposableBean接口
  2. 是否实现了destory-method
  3. 是否利用@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博客

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rtp协议:rtcp包格式和传输间隔

RTP Control Protocol -- RTCP-rtp控制协议 实时传输控制协议&#xff08;RTCP&#xff09;基于对会话中的所有参与者定期传输控制包&#xff0c;使用与数据包相同的分发机制。底层协议必须提供数据包和控制包的多路复用&#xff0c;例如使用UDP时使用不同的端口号。RTCP执行四…

2024年医疗人工智能研究报告-生成式AI爆发,医疗人工智能走到新的十字路口(附下载)

前言 2024的医疗AI&#xff0c;既是坎坷&#xff0c;又是新生。 快速发展的大语言模型&#xff0c;携着生成式AI掠过医疗领域。过往的互联网医疗、医学影像、新药研发……一个一个场景经由新一代AI重塑&#xff0c;焕发出前所未有的价值。 不过&#xff0c;发现价值并不意味着…

网络请求自定义header导致跨域问题

我记得我的项目之前已经解决了跨域问题。 后来在功能开发着&#xff0c;需要添加一个自定义的header&#xff0c;发现又出现跨域报错。 于是又开始一通摸索折腾。 我的项目前面端是用axios网络请求&#xff0c;通过拦截器添加header&#xff0c;代码如下&#xff1a; //添加请…

macOS 15 Sequoia dmg格式转用于虚拟机的iso格式教程

想要把dmg格式转成iso格式&#xff0c;然后能在虚拟机上用&#xff0c;最起码新版的macOS镜像是不能用UltraISO&#xff0c;dmg2iso这种软件了&#xff0c;你直接转放到VMware里绝对读不出来&#xff0c;办法就是&#xff0c;在Mac系统中转换为cdr&#xff0c;然后再转成iso&am…

大语言模型数据流程源码解读(基于llama3模型)

文章目录 前言一、数据进入LlamaForCausalLM(LlamaPreTrainedModel)类二、数据进入LlamaModel(LlamaPreTrainedModel)类1、input_ids的embedding编码2、position_ids位置获取3、causal_mask因果mask构建1、causal_mask调用2、因果mask代码解读(_update_causal_mask)4、hidden_s…

MATLAB人脸考勤系统

MATLAB人脸考勤系统课题介绍 该课题为基于MATLAB平台的人脸识别系统。传统的人脸识别都是直接人头的比对&#xff0c;现实意义不大&#xff0c;没有一定的新意。该课题识别原理为&#xff1a;先采集待识别人员的人脸&#xff0c;进行训练&#xff0c;得到人脸特征值。测试的时…

Http 状态码 301 Permanent Rediret 302 Temporary Redirect、 重定向 重写

HTTP状态码301和302是什么&#xff1f; 1、HTTP状态码301 HTTP状态码301表示永久性转移&#xff08;Permanent Redirect&#xff09;&#xff0c;这意味着请求的资源已经被分配了一个新的URI&#xff0c;以后的引用应该使用资源现在所指的URI。 HTTP 301状态码表示请求的资源…

如何用猿大师办公助手实现OA系统中Word公文/合同在线编辑及流转?

在OA系统或者合同管理系统中&#xff0c;我们会经常遇到网页在线编辑Word文档形式的公文及合同的情况&#xff0c;并且需要上级对下级的公文进行批注等操作&#xff0c;或者不同部门的人需要签字审核&#xff0c;这就需要用到文档流转功能&#xff0c;如何用猿大师办公助手实现…

前端零基础入门到上班:【Day3】从零开始构建网页骨架HTML

HTML 基础入门&#xff1a;从零开始构建网页骨架 目录 1. 什么是 HTML&#xff1f;HTML 的核心作用 2. HTML 基本结构2.1 DOCTYPE 声明2.2 <html> 标签2.3 <head> 标签2.4 <body> 标签 3. HTML 常用标签详解3.1 标题标签3.2 段落和文本标签3.3 链接标签3.4 图…

力扣hot100-->递归/回溯

目录 递归/回溯 1. 17. 电话号码的字母组合 2. 22. 括号生成 3. 39. 组合总和 4. 46. 全排列 5. 78. 子集 递归/回溯 1. 17. 电话号码的字母组合 中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到…

[MySQL#4] 表约束(1) | NULL | default | zerofill | 主键 | 自增长

目录 1. 表约束概述 2. 空属性&#xff08;null/not null&#xff09; 3. 默认值&#xff08;default&#xff09; 4. 列描述&#xff08;comment&#xff09; 5. zerofill 6. 主键&#xff08;primary key&#xff09; 7. 自增长&#xff08;auto_increment&#xff09…

Android中常用adb命令

目录 1.adb连接安卓模拟器 2.adb列出所有已经连接的设备 3.adb显示设备的日志信息 4.adb 电脑文件推送到安卓模拟器中 5.adb 手机传送文件到电脑 6.adb获取安卓应用的包名和Activity名 附录 1--命令 1&#xff09;adb devices 2&#xff09;adb install 路径> 3&#xff09;…

【项目管理】PMP冲刺真题200题 (题目+解析)乱序版 【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

尚硅谷 | Nginx | 学习笔记

尚硅谷 | Nginx | 学习笔记 尚硅谷Nginx教程由浅入深&#xff08;一套打通丨初学者也可掌握&#xff09;_哔哩哔哩_bilibili 文章目录 尚硅谷 | Nginx | 学习笔记一、Nginx相关概念1.Nginx是什么2.正向代理和反向代理正向代理反向代理 3.负载均衡和动静分离负载均衡动静分离 二…

小米迎来「新起点」:硬核创新从超越到引领,小米SU7 Ultra 发布

发布 | 大力财经 10月29日&#xff0c;小米15系列暨小米澎湃OS 2新品发布会在北京召开&#xff0c;小米集团创始人、董事长兼CEO雷军宣布了小米汽车原型车在纽北跑出6分46秒874的圈速&#xff0c;登顶“纽北全球最速四门车”的好消息&#xff0c;并领衔发布了小米15系列手机、…

若依微服务架构遇到的一些问题记录

一、nacos启动问题 需要看官网的准备工作&#xff0c;认真看&#xff0c;版本问题卡了两天 https://doc.ruoyi.vip/ruoyi-cloud/document/hjbs.html#%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C 1.下载nacos&#xff0c;版本需要对应上 版本说明链接 2.记得运行数据库&#xff0…

【工具】Charles对360浏览器抓包抓包

Charles 和 switchy sharp 配合&#xff0c;可以对 Chrome 进行抓包也可以配合对360安全浏览器抓包。 本文以Windows 电脑中的配置为例&#xff0c;介绍如何实现抓包。&#xff08;Mac中操作基本一致&#xff09; 1.安装Charles 可根据自己的电脑下载对应的版本&#xff1a;…

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

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

Kafka-代码示例

一、构建开发环境 File > New > Project 选择一个最简单的模板 项目和坐标命名 配置maven路径 添加maven依赖 <dependencies><!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients --><dependency><groupId>org.apache.kaf…