我们都知道在SpringBoot启动类上添加@SpringBootApplication注解后执行main方法就可以自动启动服务 Spring会自动帮我们找到需要管理的Bean的呢
探究:
经典的八股文AbstractApplicationContext#refresh()方法 相信大家已经比较熟悉了
进入invokeBeanFactoryPostProcessors()调用BeanFactory后置处理器方法
进入PostProcessorRegistrationDelegate的invokeBeanDefinitionRegistryPostProcessors方法 注意此方法执行后registry参数(BeanDefinitionRegistry)中的beanDefinitionMap会扫描到需要的bean信息 说明此方法才是真正起到扫描作用的地方 重点!!!
继续进 兄弟们 往里进 ConfigurationClassPostProcessor#processConfigBeanDefinitions 两张图都是此方法
ps:代码太长 其中的这个parser.parse()就是真正解析的方法
ConfigurationClassParser#doProcessConfigurationClass到了 很近了 你要问我 我只能说 快到顶了 仔细的同学应该已经看出来了 图上的这个Set会获取@ComponentScan类扫描注解 而这个入参即为我们的启动类Class 其中启动注解@SpringBootApplication中正包含了@CompentScan这个注解 所以此时这个Set中获取到了我们的启动类 红线标注的这个地方继续走哦
componentScanAnnotationParser#parse中的scanner.doScan(StringUtils.toStringArray(basePackages)) 这里说明一下这个basePackages由于我们没有指定 所以默认是启动类所在的包路径 ps:这也是需要将启动类放到最外层包的原因 放里面的话无法扫描到对应Bean
ClassPathBeanDefinitionScanner#doScan 继续往里 还是那句 红线标注的地方
ClassPathScanningCandidateComponentProvider#scanCandidateComponents 好了 到站 请各位乘客下车吧 这个方法就是真实找到底层bean的地方 原理很简单 参数basePackage为我们的包根路径 即启动类所在的路径 假设为com/juejin/drink 那么此方法会递归调用扫描com/juejin/drink下的所有类和目录 如果是需要注册的bean 那么放入new的LinkedHashSet中返回
经过如上步骤 程序会返回到PostProcessorRegistrationDelegate的invokeBeanDefinitionRegistryPostProcessors方法继续执行 但此时我们的目的达到了 实际上SpringBoot就是通过@SpringBootApplication的@CompentScan注解 拿到启动类的包路径 最终去递归调用 获取到哪些是我们标注了@Compent这些需要注册进容器的 此步骤是refresh方法的invokeBeanFactoryPostProcessors()中执行的
结语:
本文只是简单的叙述了下Spring是如何将我们的Bean加载到beanDefinitionMap中的 比较简单 不涉及其他复杂逻辑
作者:爱喝可乐h
链接:https://juejin.cn/post/7203620183172382776
来源:稀土掘金
网友评论