文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载导读本文以 CodeGuide 仓库中《面经手册 · 第 30 篇》为骨架完整拆解 Spring 中beanFactory.getBean()从入口到返回实例的全流程源码覆盖transformedBeanName别名与符处理、getSingleton三级缓存、depends-on依赖解析、单例/原型/自定义 Scope 三种创建分支以及FactoryBean的getObject取数逻辑。读完你不仅能回答getBean 里有哪些分支判断这类面试题还能在遇到别名失效、依赖顺序错乱、符前缀、循环依赖等真实问题时直接从源码定位根因。一、为什么获取一个 Bean 会这么复杂很多初看 Spring 源码的伙伴都会疑惑不就取一个 Bean 吗怎么这么多流程答案在于 Spring 需要同时适配大量差异化场景一个 Bean 可能有别名、可能有depends-on 依赖、也可能是被BeanFactory包装过的所以需要transformedBeanName统一处理这些名字差异化要判断循环依赖、父工厂、单例还是原型、懒加载还是预加载、是否在缓存中所以有各种组合判断走不同的流程分支提早暴露对象、三级缓存、创建标记清理所有这些优化处理都是为了让 Bean 的获取更高效、更健壮。所以Spring 为了适应各类需求把 getBean 设计成了一个复杂的分支流程。深入理解这部分源码不只是为了应付八股文更能在日常使用 Spring 遇到复杂问题时有一个大致知晓的执行流程快速定位问题同时它也是一种工厂 模板方法 回调设计的完整落地范例对自研框架和中间件的开发有直接指导意义。在 CodeGuide 仓库中《面经手册 · 第 29 篇》 讲解过 Bean 的定义、注册与获取的整体轮廓《面经手册 · 第 31 篇》 则专门深入了三级缓存与循环依赖本文则聚焦于 getBean 这个总入口把主干流程串起来。二、getBean 从哪开始读源码在日常业务开发中基本都是基于注解几乎不会自己去调用beanFactory.getBean。所以当你找不到 getBean 源码的阅读入口、也不方便调试时可以像下面这样写一个单元测试类点进getBean就能顺着源码一路往下读了Test public void test_getBean() { BeanFactory beanFactory new ClassPathXmlApplicationContext(spring-config.xml); UserDao userDao beanFactory.getBean(userDao, UserDao.class); logger.info(获取 Bean{}, userDao); }入口调用链为ClassPathXmlApplicationContext其内部持有DefaultListableBeanFactory是 Spring 中最核心的 BeanFactory 实现→BeanFactory#getBean(name, requiredType)→AbstractBeanFactory#doGetBean()。三、getBean 源码全局预览源码位置AbstractBeanFactory - getBean() - doGetBean()Override public T T getBean(String name, ClassT requiredType) throws BeansException { // getBean 就像你的领导其实没做啥都在 doGetBean 里 return doGetBean(name, requiredType, null, false); }protected T T doGetBean(final String name, final ClassT requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { // 处理别名BeanName、处理带符的工厂BeanName final String beanName transformedBeanName(name); Object bean; // 先尝试从缓存中获取Bean实例这个位置就是三级缓存解决循环依赖的方法 Object sharedInstance getSingleton(beanName); if (sharedInstance ! null args null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug(Returning eagerly cached instance of singleton bean beanName that is not fully initialized yet - a consequence of a circular reference); } else { logger.debug(Returning cached instance of singleton bean beanName ); } } // 1. 如果 sharedInstance 是普通的 Bean 实例则下面的方法会直接返回 // 2. 如果 sharedInstance 是工厂Bean类型则需要获取 getObject 方法可以参考关于 FactoryBean 的实现类 bean getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 循环依赖有三种setter注入、多实例和构造函数Spring 只能解决 setter 注入所以这里是 Prototype 则会抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 1. 父 bean 工厂存在 // 2. 当前 bean 不存在于当前bean工厂则到父工厂查找 bean 实例 BeanFactory parentBeanFactory getParentBeanFactory(); if (parentBeanFactory ! null !containsBeanDefinition(beanName)) { // 获取 name 对应的 beanName如果 name 是以 开头则返回 beanName String nameToLookup originalBeanName(name); // 根据 args 参数是否为空调用不同的父容器方法获取 bean 实例 if (args ! null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else { return parentBeanFactory.getBean(nameToLookup, requiredType); } } // 1. typeCheckOnly用于判断调用 getBean 方法时是否仅是做类型检查 // 2. 如果不是只做类型检查就会调用 markBeanAsCreated 进行记录 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 从容器 getMergedLocalBeanDefinition 获取 beanName 对应的 GenericBeanDefinition转换为 RootBeanDefinition final RootBeanDefinition mbd getMergedLocalBeanDefinition(beanName); // 检查当前创建的 bean 定义是否为抽象 bean 定义 checkMergedBeanDefinition(mbd, beanName, args); // 处理使用了 depends-on 注解的依赖创建 bean 实例 String[] dependsOn mbd.getDependsOn(); if (dependsOn ! null) { for (String dep : dependsOn) { // 监测是否存在 depends-on 循环依赖若存在则会抛出异常 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, Circular depends-on relationship between beanName and dep ); } // 注册依赖记录 registerDependentBean(dep, beanName); try { // 加载 depends-on 依赖dep 是 depends-on 缩写 getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, beanName depends on missing bean dep , ex); } } } // 创建单例 bean 实例 if (mbd.isSingleton()) { // 把 beanName 和 new ObjectFactory 匿名内部类传入回调 sharedInstance getSingleton(beanName, new ObjectFactoryObject() { Override public Object getObject() throws BeansException { try { // 创建 bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // 创建失败则销毁 destroySingleton(beanName); throw ex; } } }); bean getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // 创建其他类型的 bean 实例 else if (mbd.isPrototype()) { // Its a prototype - create a new instance. Object prototypeInstance null; try { beforePrototypeCreation(beanName); prototypeInstance createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName mbd.getScope(); final Scope scope this.scopes.get(scopeName); if (scope null) { throw new IllegalStateException(No Scope registered for scope name scopeName ); } try { Object scopedInstance scope.get(beanName, new ObjectFactoryObject() { Override public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); bean getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, Scope scopeName is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton, ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // 如果需要类型转换这里会进行操作 if (requiredType ! null bean ! null !requiredType.isInstance(bean)) { try { return getTypeConverter().convertIfNecessary(bean, requiredType); } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug(Failed to convert bean name to required type ClassUtils.getQualifiedName(requiredType) , ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } // 返回 Bean return (T) bean; }综上getBean 过程涉及的核心处理方法基本包括transformedBeanName处理别名 BeanName、处理带符的工厂 BeanNamegetSingleton先尝试从缓存中获取 Bean 实例这个位置就是三级缓存解决循环依赖的方法getObjectForBeanInstance若sharedInstance是普通 Bean 实例则直接返回若是工厂 Bean 类型则需要调用getObject方法获取真正实例isPrototypeCurrentlyInCreation循环依赖有三种setter 注入、多实例、构造函数Spring 只能解决 setter 注入所以原型模式下检测到正在创建会直接抛出异常getParentBeanFactory父 Bean 工厂存在、且当前 Bean 不存在于当前工厂时到父工厂查找 Bean 实例originalBeanName获取 name 对应的 beanName如果 name 以开头则返回 beanNameargs ! null根据 args 参数是否为空调用不同的父容器方法获取 Bean 实例!typeCheckOnly用于判断本次调用是否仅是类型检查若不是则调用markBeanAsCreated记录创建状态mbd.getDependsOn处理使用了 depends-on 配置的依赖创建 Bean 实例isDependent监测是否存在 depends-on 循环依赖若存在则抛出异常registerDependentBean注册依赖记录getBean(dep)加载 depends-on 依赖dep 是 depends-on 的缩写mbd.isSingleton()走单例 Bean 创建分支mbd.isPrototype()走原型 Bean 创建分支其他scopeName走自定义 Scope 创建分支return (T) bean返回 Bean 实例。四、beanName 转换操作1. 处理 符transformedBeanName() - BeanFactoryUtils.transformedBeanName(name)public static String transformedBeanName(String name) { Assert.notNull(name, name must not be null); String beanName name; while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { beanName beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } return beanName; }使用FactoryBean创建出的对象会在DefaultListableBeanFactory初始化的时候使用getBean(FACTORY_BEAN_PREFIX beanName)给 beanName 加上String FACTORY_BEAN_PREFIX 这里是使用while循环逐步地把去掉只要截取的首个字符是符就继续循环截取userService - userService - userService - userService。面试追问你调试代码时见过 BeanName 前面带的情况吗答案就在这里——这是 Spring 内部用来标识我要取 FactoryBean 本身而不是它生产的对象的前缀。在调试DefaultListableBeanFactory初始化过程时能看到getBean(userService)这类调用。2. 别名转换transformedBeanName() - canonicalNamepublic String canonicalName(String name) { String canonicalName name; // Handle aliasing... String resolvedName; do { resolvedName this.aliasMap.get(canonicalName); if (resolvedName ! null) { canonicalName resolvedName; } } while (resolvedName ! null); return canonicalName; }对应的 XML 配置示例bean iduserService classorg.itstack.interview.UserService/ alias nameuserService aliasuserService-alias01/ alias nameuserService-alias01 aliasuserService-alias02/首先Spring 对 Bean 的存放并不会使用别名作为 Map 中的 key所以遇到所有通过别名获取 Bean 的场景都需要查到对应的原始名字才可以。如果你知道这个事遇到此类问题时就知道从哪下手查了do...while循环会依次像链条一样不断寻找别名对应的名称直到当前这个名称没有别名了就返回对应 BeanName。上面的配置中userService-alias02 - userService-alias01 - userService就是一个典型的别名链条。五、depends-on 依赖 Bean1. 依赖循环检测isDependentAbstractBeanFactory - isDependent(beanName, dep) - DefaultSingletonBeanRegistryprotected boolean isDependent(String beanName, String dependentBeanName) { synchronized (this.dependentBeanMap) { return isDependent(beanName, dependentBeanName, null); } }bean iduserService classorg.itstack.interview.UserService depends-onuserDao/ bean iduserDao classorg.itstack.interview.UserDao/isDependent处理的是使用了depends-on配置的 Bean 定义。private boolean isDependent(String beanName, String dependentBeanName, SetString alreadySeen) { if (alreadySeen ! null alreadySeen.contains(beanName)) { return false; } String canonicalName canonicalName(beanName); SetString dependentBeans this.dependentBeanMap.get(canonicalName); if (dependentBeans null) { return false; } if (dependentBeans.contains(dependentBeanName)) { return true; } for (String transitiveDependency : dependentBeans) { if (alreadySeen null) { alreadySeen new HashSetString(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; }alreadySeen ! null监测已经依赖的 Bean避免递归死循环canonicalName处理别名配置找到最原始的 BeanNameSetString dependentBeans获取依赖的 Bean 集合for循环递归检测依赖的 Bean并把当前 Bean 添加到alreadySeen中。2. 注册依赖记录registerDependentBeanAbstractBeanFactory - registerDependentBean(dep, beanName) - DefaultSingletonBeanRegistrypublic void registerDependentBean(String beanName, String dependentBeanName) { String canonicalName canonicalName(beanName); synchronized (this.dependentBeanMap) { SetString dependentBeans this.dependentBeanMap.get(canonicalName); if (dependentBeans null) { dependentBeans new LinkedHashSetString(8); this.dependentBeanMap.put(canonicalName, dependentBeans); } dependentBeans.add(dependentBeanName); } synchronized (this.dependenciesForBeanMap) { SetString dependenciesForBean this.dependenciesForBeanMap.get(dependentBeanName); if (dependenciesForBean null) { dependenciesForBean new LinkedHashSetString(8); this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean); } dependenciesForBean.add(canonicalName); } }canonicalName(beanName)获取原始的 beanNamesynchronized (this.dependentBeanMap)添加canonicalName, dependentBeanName到dependentBeanMap中记录谁依赖我synchronized (this.dependenciesForBeanMap)添加dependentBeanName, canonicalName到dependenciesForBeanMap中记录我依赖谁。两张 Map 互为反向索引这也是isDependent能够快速判断 depends-on 循环依赖的数据基础。最后getBean(dep)就可以真正获取到 depends-on 依赖的 Bean 了。六、处理单实例 BeanAbstractBeanFactory - mbd.isSingleton()if (mbd.isSingleton()) { sharedInstance getSingleton(beanName, new ObjectFactoryObject() { Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }这一部分使用beanName和singletonFactory匿名内部类传入等待回调的方式创建单实例 Bean。public Object getSingleton(String beanName, ObjectFactory? singletonFactory) { Assert.notNull(beanName, beanName must not be null); synchronized (this.singletonObjects) { Object singletonObject this.singletonObjects.get(beanName); if (singletonObject null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)); } if (logger.isDebugEnabled()) { logger.debug(Creating shared instance of singleton bean beanName ); } beforeSingletonCreation(beanName); boolean newSingleton false; boolean recordSuppressedExceptions (this.suppressedExceptions null); if (recordSuppressedExceptions) { this.suppressedExceptions new LinkedHashSetException(); } try { singletonObject singletonFactory.getObject(); newSingleton true; } catch (IllegalStateException ex) { 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 ! NULL_OBJECT ? singletonObject : null); } }this.singletonObjects.get(beanName)先尝试从缓存池中获取对象没有就继续往下执行beforeSingletonCreation(beanName)标记当前 Bean 正在创建如果有构造函数注入的循环依赖会在此报错singletonObject singletonFactory.getObject()创建 Bean 的过程就是回调createBean()方法afterSingletonCreation(beanName)最后把创建标记从集合中移除addSingleton(beanName, singletonObject)新创建的单例会加入缓存集合。七、从缓存中获取 Bean 实例三级缓存doCreateBean - if (earlySingletonExposure) - getSingleton(beanName, false)protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从 singletonObjects 获取实例singletonObjects 中缓存的实例都是完全实例化好的 bean可以直接使用 Object singletonObject this.singletonObjects.get(beanName); // 如果 singletonObject 为空则没有创建或创建中 if (singletonObject null isSingletonCurrentlyInCreation(beanName)) { // 加锁 synchronized (this.singletonObjects) { // 单例缓存池中没有当前beanName singletonObject this.earlySingletonObjects.get(beanName); if (singletonObject null allowEarlyReference) { ObjectFactory? singletonFactory this.singletonFactories.get(beanName); if (singletonFactory ! null) { // 加入到三级缓存暴漏早期对象用于解决循环依赖 singletonObject singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject ! NULL_OBJECT ? singletonObject : null); }这一段代码主要就是使用三级缓存解决 setter 注入循环依赖的《面经手册 · 第 31 篇》 对循环依赖做了完整的实验验证singletonObjects用于存放初始化好的 Bean 实例成品对象一级缓存earlySingletonObjects用于存放初始化中的 Bean半成品对象用来解决循环依赖二级缓存singletonFactories用于存放 Bean 工厂工厂所生成的 Bean 还没有完成初始化三级缓存存的是ObjectFactory类型的 lambda 表达式主要用于 AOP 循环依赖的代理创建时机控制。八、FactoryBean 中获取 Bean 实例AbstractBeanFactory - getObjectForBeanInstance(sharedInstance, name, beanName, null)protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { // 如果 beanName 以 开头但 beanInstance 却不是 FactoryBean则会抛出异常 if (BeanFactoryUtils.isFactoryDereference(name) !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } // 这里判断就是这个 bean 是不是 FactoryBean不是就直接返回了 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object null; if (mbd null) { // 如果 mbd 为空则从缓存加载 beanFactoryBean 生成的单例 bean 实例会缓存到 factoryBeanObjectCache 集合中方便使用 object getCachedObjectForFactoryBean(beanName); } if (object null) { // 到这beanInstance 是 FactoryBean 类型所以就强转了 FactoryBean? factory (FactoryBean?) beanInstance; // mbd 为空且判断 containsBeanDefinition 是否包含 beanName if (mbd null containsBeanDefinition(beanName)) { // 合并 BeanDefinition mbd getMergedLocalBeanDefinition(beanName); } boolean synthetic (mbd ! null mbd.isSynthetic()); // 调用 getObjectFromFactoryBean 获取实例 object getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }!(beanInstance instanceof FactoryBean)判断这个 Bean 是不是 FactoryBean不是就直接返回了如果mbd为空则从缓存加载 BeanFactoryBean生成的单例 Bean 实例会缓存到factoryBeanObjectCache集合中方便下次直接使用调用getObjectFromFactoryBean获取实例这里会包含对单例以及非单例的处理最终返回factory.getObject()对应的 Bean 实例。九、测试案例以下三个案例覆盖了本文讲解的三大核心分支别名、depends-on 依赖、FactoryBean。1. 别名bean iduserService classorg.itstack.interview.UserService/ !-- 起个别名 -- alias nameuserService aliasuserService-alias01/ !-- 别名的别名 -- alias nameuserService-alias01 aliasuserService-alias02/Test public void test_alias() { BeanFactory beanFactory new ClassPathXmlApplicationContext(spring-config-alias.xml); UserService userService beanFactory.getBean(userService-alias02, UserService.class); logger.info(获取 Bean 通过别名{}, userService); }在单元测试的 getBean 断点中可以看到它会通过canonicalName把别名逐步处理掉最终获取到原有的 BeanNameuserService。这个行为在 《面经手册 · 第 29 篇》 的测试日志中也能看到印证——连续用userService、userService-alias01、userService-alias02三个名字获取同一个 Bean日志均为Returning cached instance of singleton bean userService。2. 依赖bean iduserService classorg.itstack.interview.UserService depends-onuserDao/ bean iduserDao classorg.itstack.interview.UserDao/Test public void test_depends_on() { BeanFactory beanFactory new ClassPathXmlApplicationContext(spring-config-depends-on.xml); UserService userService beanFactory.getBean(UserService.class, userService); logger.info(获取 Bean{}, userService.getUserDao()); }涉及到依赖时会走到dependsOn ! null分支下先完成isDependent循环检测、registerDependentBean注册依赖记录再通过getBean(dep)获取到依赖的 Bean 实例。3. BeanFactorybean iduserDao classorg.itstack.interview.MyFactoryBean/Test public void test_factory_bean() { BeanFactory beanFactory new ClassPathXmlApplicationContext(spring-config-factory-bean.xml); UserDao userDao beanFactory.getBean(userDao, UserDao.class); logger.info(获取 Bean{}, userDao); }实现FactoryBean的类需要实现getObject方法所以此类 Bean 最终获取到的都是getObject()的返回结果而不是 FactoryBean 本身。如果想要拿到 FactoryBean 实例本身就需要用userDao这种带前缀的名字去取。十、结合手写 Spring 源码印证 getBean 的设计CodeGuide 仓库中的《Spring 手撸专栏》用最小实现还原了 Spring 的核心设计恰好可以从三个层面印证本文分析的 getBean 骨架。1. 模板方法Bean 定义、注册、获取的骨架《手撸 Spring 第 3 章实现 Bean 的定义、注册、获取》 给出了与AbstractBeanFactory一致的模板方法设计public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory { Override public Object getBean(String name) throws BeansException { Object bean getSingleton(name); if (bean ! null) { return bean; } BeanDefinition beanDefinition getBeanDefinition(name); return createBean(name, beanDefinition); } protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException; protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException; }AbstractBeanFactory继承DefaultSingletonBeanRegistry从而具备单例 Bean 的注册与获取能力这与 Spring 中DefaultSingletonBeanRegistry提供三级缓存和getSingleton的定位一致getBean只定义调用过程把getBeanDefinition和createBean定义为抽象方法由DefaultListableBeanFactory和AbstractAutowireCapableBeanFactory分别实现——这正是模板模式统一收口调用逻辑的体现也是真实doGetBean中先查缓存、取不到再创建骨架的简化版。2. FactoryBean 的注册支撑《手撸 Spring 第 10 章Bean 对象作用域以及 FactoryBean 的实现和使用》 在DefaultSingletonBeanRegistry与AbstractBeanFactory之间增加了一层FactoryBeanRegistrySupport专门处理 FactoryBean 注册支撑protected Object getCachedObjectForFactoryBean(String beanName) { ... } protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) { ... return doGetObjectFromFactoryBean(factory, beanName); } private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName) { return factory.getObject(); ... }这正好对应真实源码getObjectForBeanInstance中先查factoryBeanObjectCache缓存取不到再调getObjectFromFactoryBean最终执行factory.getObject()的逻辑。该章还提到 MyBatis 就是实现了MapperFactoryBean在getObject方法中提供 SqlSession 执行 CRUD 操作——这也是为什么本文getObjectForBeanInstance分支如此重要的原因。3. 三级缓存解决循环依赖《手撸 Spring 第 17 章通过三级缓存解决循环依赖》 实现了与 Spring 一致的三级缓存结构private MapString, Object singletonObjects new ConcurrentHashMap(); // 一级缓存成品对象 protected final MapString, Object earlySingletonObjects new HashMap(); // 二级缓存半成品对象 private final MapString, ObjectFactory? singletonFactories new HashMap(); // 三级缓存工厂对象lambda并在doCreateBean中提供提前暴露对象的操作addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, beanDefinition, finalBean))之后通过getSingleton从三个缓存中依次寻找对象一级、二级有则直接取走如果是三级缓存中的工厂对象则getObject后把实际对象放入二级缓存并删除工厂对象。这与本文第七节分析的getSingleton(beanName, allowEarlyReference)源码完全同构可以作为循环依赖分支的落地实验样本。十一、总结到这里关于 Spring IOC 获取 Bean 的核心流程就基本全部介绍完了。整个篇章让我们看到获取一个 Bean 的流程非常复杂涉及非常多的分支流程。之所以会有这么多流程就是因为 Spring 的 Bean 获取需要满足很多种情况别名、前缀、depends-on、父工厂、单例/原型/自定义 Scope、三级缓存、FactoryBean、类型转换……学习时可以优先按照 getBean 的流程脉络入口 → transformedBeanName → 缓存命中 → 分支创建 → 类型转换进行梳理再对照源码按步骤分析配合 《面经手册 · 第 31 篇》 的循环依赖专题、《面经手册 · 第 29 篇》 的 IOC 特性总览以及《Spring 手撸专栏》的简化实现交叉验证整个过程基本就能对 getBean 没有陌生感了。学习几乎就是一个慢慢磨的过程就像走迷宫一样虽然有时候会走错路但那些错了的路也是知识学习的一部分。在编程学习中不只是看结果过程更重要学会学习的方式更有意义。赞分享文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载相关推荐面经手册·第39篇MyBatis 缓存机制全解析——从一级缓存、二级缓存到 Redis 整合的源码级拆解面经手册·第39篇MyBatis 缓存机制全解析——从一级缓存、二级缓存到 Redis 整合的源码级拆解 本文是 CodeGuide 仓库面经手册系列的第文档教程后端Spring IoC 单例注册中心源码解析DefaultSingletonBeanRegistry 与三级缓存设计Spring IoC 单例注册中心源码解析DefaultSingletonBeanRegistry 与三级缓存设计 导读 DefaultSingletonBe文档教程知识库Spring 源码猎手IoC 容器在 Web 环境中的启动全流程解析——从 web.xml 配置到 ContextLoader 的源码级拆解Spring 源码猎手IoC 容器在 Web 环境中的启动全流程解析——从 web.xml 配置到 ContextLoader 的源码级拆解 导读 Sprin文档教程知识库上一篇Cilium 的 cilium-dbg bpf nat retries list 命令详解解读 SNAT 端口分配重试直方图下一篇Activepieces 的 Node/TypeScript MCP Server 实现指南从项目搭建到生产级工具注册创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
大麦自动抢票3分钟跑通:移动端配置实操指南 大麦自动抢票3分钟跑通:移动端配置实操指南 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase
倒计时归零,拇指戳下屏幕&#x… · 2026/9/24 17:19:44
数据分析最常用的9个模型,撑起80%的分析工作 做数据分析这些年,我越来越觉得,真正高频、真正能解决业务问题的分析模型,其实没有想象中那么多。刚开始做分析的时候,很容易有一种错觉:模型越高级,分析能力越强。于是很多人会去学回归、聚类、决策树、时… · 2026/9/24 17:54:40
【架构专栏】第16章 嵌入式系统架构设计 3/3 架构设计 相关文档,希望互相学习,共同进步
风123456789~-CSDN博客 系统架构设计 相关文章: 【架构专栏】架构考试介绍 【架构专栏】架构知识点 知识总览 共19章内容,主要包括: 1)1绪论、2计算… · 2026/9/24 17:54:40
财务岗:先混进去,再谈专业 刚入行的时候,很多人都会把“专业”想得很复杂。证书要有,公式要熟,准则要背,Excel 要会,最好还能在面试时把成本核算、预算管理、合并报表和税务处理讲得头头是道。但真正进了公司才会发现,财务岗最先考验… · 2026/9/24 17:54:40
【架构专栏】第16章 嵌入式系统架构设计 2/3 架构设计 相关文档,希望互相学习,共同进步
风123456789~-CSDN博客 系统架构设计 相关文章: 【架构专栏】架构考试介绍 【架构专栏】架构知识点 知识总览 共19章内容,主要包括: 1)1绪论、2计算… · 2026/9/24 17:54:40
终于搞懂了!原来这才叫盈亏平衡分析 做经营分析久了以后,我发现“盈亏平衡”这个词几乎所有财务都听过,但真正拿到业务里会用的人,其实没有想象中那么多。很多人对盈亏平衡的理解还停在:收入刚好等于成本,不赚也不亏。这个定义当然没错。但如果只知道这一… · 2026/9/24 17:54:34
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44