首页 / 资讯中心 / 文章详情

一,AnnotationConfigServletWebServerApplicationContext类源码学习

一,AnnotationConfigServletWebServerApplicationContext类源码学习 ★ FEATURED ARTICLE
版本背景SpringBoot2.x 核心容器Servlet 类型 Web 应用上下文对应 SpringBoot Servlet Web传统 Tomcat 嵌入式 web环境非响应式 Web (Reactive)。继承链AnnotationConfigServletWebServerApplicationContext→ServletWebServerApplicationContext→GenericWebApplicationContext→GenericApplicationContext→AbstractApplicationContext实现接口AnnotationConfigRegistry提供register()注册配置类、scan()扫描包能力。架构设计层面解析1. 定位与职责这是SpringBoot Servlet Web 场景的默认应用上下文由ApplicationContextFactory.Factory工厂进行实例化。融合两套能力父类ServletWebServerApplicationContext嵌入式 web 服务器能力refresh 流程中会创建 Tomcat/Jetty 等 web 容器是 SpringBoot 内置 web 服务的核心自身实现AnnotationConfigRegistry注解驱动配置能力等价于传统 Spring 的AnnotationConfigApplicationContext支持直接注册配置类、包路径扫描 Bean 定义抛弃 xml。对比AnnotationConfigApplicationContext普通非 web 注解容器Spring 原生AnnotationConfigServletWebServerApplicationContextSpringBoot 封装的 Servlet Web 注解容器AnnotationConfigReactiveWebServerApplicationContext响应式 web 容器2. 核心成员变量设计// 读取注解类把Configuration/Component类解析成BeanDefinitionprivatefinalAnnotatedBeanDefinitionReaderreader;// 类路径扫描器扫描包下Component等注解的类生成BeanDefinitionprivatefinalClassPathBeanDefinitionScannerscanner;// 缓存通过register()方法手动注册的配置Class集合privatefinalSetClass?annotatedClasses;// 缓存通过scan()传入的扫描包路径privateString[]basePackages;⚠️关键设计点register()、scan()没有立刻执行 Bean 定义注册只是把 class、package 暂存到成员变量真正执行扫描注册延迟到postProcessBeanFactory。为什么要延迟refresh()是父类AbstractApplicationContext定义的标准生命周期BeanFactory 还没完成初始化前不能向 BeanFactory 注册 BeanDefinition遵循 Spring 生命周期扩展点postProcessBeanFactory这是refresh()内部的标准回调时机允许子类对 beanFactory 做后置加工保证环境 Environment、BeanNameGenerator、ScopeMetadataResolver 全部设置完成后再执行扫描解析。3. 扩展点与设计模式模板方法模式继承AbstractApplicationContext的 refresh () 完整生命周期重写prepareRefresh、postProcessBeanFactory做子类定制逻辑父类控制主流程子类实现差异化工厂模式内部静态Factory实现ApplicationContextFactorySpringBoot 会根据WebApplicationType来选择实例化哪一种 ApplicationContextSERVLET/REACTIVE/NONE组合模式组合AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner自身不实现解析逻辑委托给这两个组件符合单一职责reader负责单个注解类解析注册scanner负责 classpath 包扫描配置透传设计setEnvironment/setBeanNameGenerator/setScopeMetadataResolver配置不仅设置给上下文同时同步委托给内部 reader 和 scanner保证解析行为统一。4. 与 SpringApplication 启动流程关系SpringBoot 启动时通过ApplicationContextFactory.Factory#create()构造该上下文之后执行 refresh ()refresh 内部链路refresh()→prepareRefresh()→postProcessBeanFactory()→ 执行 scan 和 register 加载 bean 定义 → 后续执行 Bean 实例化、创建嵌入式 Tomcat。注意构造函数// 传入配置类内部调用registerrefreshpublicAnnotationConfigServletWebServerApplicationContext(Class?...annotatedClasses)// 传入包路径内部调用scanrefreshpublicAnnotationConfigServletWebServerApplicationContext(String...basePackages)这两个构造方法手动调用 refresh ()日常 SpringApplication 启动时不会使用这两个构造SpringBoot 会调用无参构造统一外部调用 refresh。源码解析/** * SpringBoot Servlet Web环境注解驱动的应用上下文 * 1. 支持注解配置类register注册、包路径scan扫描 * 2. 继承ServletWebServerApplicationContext拥有创建嵌入式Tomcat等web服务器能力 * 3. 实现AnnotationConfigRegistry对齐AnnotationConfigApplicationContext的注解驱动API * 仅用于WebApplicationType.SERVLET场景 */publicclassAnnotationConfigServletWebServerApplicationContextextendsServletWebServerApplicationContextimplementsAnnotationConfigRegistry{/** * 注解Bean定义读取器解析单个Configuration、Component注解类生成BeanDefinition注册到容器 */privatefinalAnnotatedBeanDefinitionReaderreader;/** * classpath扫描器扫描指定包路径下带Component派生注解的类生成BeanDefinition */privatefinalClassPathBeanDefinitionScannerscanner;/** * 缓存调用register()传入的配置类延迟到postProcessBeanFactory阶段才真正注册 * LinkedHashSet保证顺序 */privatefinalSetClass?annotatedClasses;/** * 缓存调用scan()传入的扫描包路径延迟执行扫描 */privateString[]basePackages;/** * 无参构造SpringBoot框架启动时实际使用的构造函数 * 初始化reader、scanner初始化存储配置类的集合 */publicAnnotationConfigServletWebServerApplicationContext(){this.annotatedClassesnewLinkedHashSet();this.readernewAnnotatedBeanDefinitionReader(this);this.scannernewClassPathBeanDefinitionScanner(this);}/** * 指定BeanFactory的构造父类ServletWebServerApplicationContext支持传入自定义beanFactory * param beanFactory 自定义DefaultListableBeanFactory */publicAnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactorybeanFactory){super(beanFactory);this.annotatedClassesnewLinkedHashSet();this.readernewAnnotatedBeanDefinitionReader(this);this.scannernewClassPathBeanDefinitionScanner(this);}/** * 传入配置类的构造器【注意会直接执行refresh()】 * 一般不会被SpringBoot主流程调用多用于单元测试手动启动容器 * param annotatedClasses 需要注册的Configuration等注解配置类 */publicAnnotationConfigServletWebServerApplicationContext(Class?...annotatedClasses){this();// 将配置类存入缓存集合不是立刻注册BeanDefinitionthis.register(annotatedClasses);// 立刻触发完整Spring生命周期刷新this.refresh();}/** * 指定扫描包路径的构造器【注意会直接执行refresh()】 * 多用于手动编码启动web容器场景业务开发极少使用 * param basePackages 需要扫描的基础包路径 */publicAnnotationConfigServletWebServerApplicationContext(String...basePackages){this();// 包路径存入成员变量缓存不马上扫描this.scan(basePackages);this.refresh();}/** * 重写设置环境把Environment同步透传给内部reader和scanner * 保证扫描、解析注解时使用同一个Environment(profile、properties配置) * param environment 环境对象 */publicvoidsetEnvironment(ConfigurableEnvironmentenvironment){super.setEnvironment(environment);this.reader.setEnvironment(environment);this.scanner.setEnvironment(environment);}/** * 设置BeanName生成器同步给reader、scanner同时注册单例bean到beanFactory * Spring内部会使用这个BeanNameGenerator处理Configuration类命名 * param beanNameGenerator bean名称生成策略 */publicvoidsetBeanNameGenerator(BeanNameGeneratorbeanNameGenerator){this.reader.setBeanNameGenerator(beanNameGenerator);this.scanner.setBeanNameGenerator(beanNameGenerator);// 向beanFactory注册单例key是Spring内部固定常量供内部组件获取this.getBeanFactory().registerSingleton(org.springframework.context.annotation.internalConfigurationBeanNameGenerator,beanNameGenerator);}/** * 设置Scope元数据解析器同步透传给reader和scanner * 用于解析Scope注解处理singleton/prototype等作用域支持自定义scope解析 * param scopeMetadataResolver scope元解析器 */publicvoidsetScopeMetadataResolver(ScopeMetadataResolverscopeMetadataResolver){this.reader.setScopeMetadataResolver(scopeMetadataResolver);this.scanner.setScopeMetadataResolver(scopeMetadataResolver);}/** * AnnotationConfigRegistry接口实现注册注解配置类 * 【重要这里只是存入集合缓存并不会立刻解析注册BeanDefinition】 * 真正执行reader.register推迟到postProcessBeanFactory * param annotatedClasses 配置类数组 */publicfinalvoidregister(Class?...annotatedClasses){Assert.notEmpty(annotatedClasses,At least one annotated class must be specified);this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));}/** * AnnotationConfigRegistry接口实现指定扫描包 * 【重要仅缓存包路径不会立刻执行扫描】 * scanner.scan(basePackages)推迟到postProcessBeanFactory生命周期回调 * param basePackages 待扫描包路径 */publicfinalvoidscan(String...basePackages){Assert.notEmpty(basePackages,At least one base package must be specified);this.basePackagesbasePackages;}/** * 重写父类AbstractApplicationContext#prepareRefresh * refresh()生命周期第一个阶段准备刷新上下文 * 清理scanner缓存scanner内部会缓存扫描过的元数据刷新前清空防止旧缓存干扰 */protectedvoidprepareRefresh(){this.scanner.clearCache();super.prepareRefresh();}/** * ✨核心扩展方法父类模板方法钩子 postProcessBeanFactory * 在refresh()中执行时机BeanFactory已经创建完成BeanDefinition还没有开始实例化bean * 这里才真正执行包扫描、注册配置类BeanDefinition * param beanFactory 已经初始化完成的bean工厂 */protectedvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory){// 调用父类ServletWebServerApplicationContext的后置处理super.postProcessBeanFactory(beanFactory);// 如果存在扫描包路径委托scanner执行类路径扫描注册BeanDefinitionif(this.basePackages!nullthis.basePackages.length0){this.scanner.scan(this.basePackages);}// 如果缓存了注册的配置类委托reader执行注册配置类的BeanDefinitionif(!this.annotatedClasses.isEmpty()){this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));}}/** * 内部静态工厂实现SpringBoot SPI接口 ApplicationContextFactory * SpringBoot通过SPI加载该Factory根据WebApplicationType选择创建对应上下文 */staticclassFactoryimplementsApplicationContextFactory{Factory(){}/** * 返回该上下文对应的Environment类型只有SERVLET类型web返回ApplicationServletEnvironment */publicClass?extendsConfigurableEnvironmentgetEnvironmentType(WebApplicationTypewebApplicationType){returnwebApplicationType!WebApplicationType.SERVLET?null:ApplicationServletEnvironment.class;}/** * 创建Servlet web对应的环境实例 ApplicationServletEnvironment */publicConfigurableEnvironmentcreateEnvironment(WebApplicationTypewebApplicationType){returnwebApplicationType!WebApplicationType.SERVLET?null:newApplicationServletEnvironment();}/** * 创建上下文实例只有SERVLET场景返回AnnotationConfigServletWebServerApplicationContext * SpringBoot启动流程会调用该方法拿到无参构造的上下文实例 */publicConfigurableApplicationContextcreate(WebApplicationTypewebApplicationType){returnwebApplicationType!WebApplicationType.SERVLET?null:newAnnotationConfigServletWebServerApplicationContext();}}}关键源码要点总结register ()/scan () 延迟执行是最大的特点很多人误以为调用register()就注册 Bean实际上只是放到集合等到postProcessBeanFactory才真正解析如果在 refresh 之前修改 environment、beanNameGenerator都会对这些配置类、扫描的 bean 生效。postProcessBeanFactory是什么时机refresh () 生命周期obtainFreshBeanFactory()→拿到全新 BeanFactory→postProcessBeanFactory(beanFactory)BeanDefinition 还没解析完成Bean 还没有实例化是修改 BeanFactory、追加 BeanDefinition 的标准扩展点。和父类分工ServletWebServerApplicationContext负责 web 服务器Tomcat 创建、onRefresh () 中启动 web 服务当前类只负责注解驱动 BeanDefinition 加载职责分离开闭原则。内部Factory是 SpringBoot SPI 机制META-INF/services 中配置ApplicationContextFactorySpringBoot 根据 web 应用类型选择实例化该容器、reactive 容器或者非 web 容器。两个带 refresh 的构造函数业务代码几乎不用主要用于单元测试手动启动嵌入式 web 容器SpringApplication.run 不会调用SpringApplication 自己调用无参构造外部统一调用 refresh ()。架构取舍Q为什么不直接在 register 方法中调用reader.register(clazz)非要缓存到 postProcessBeanFactory 再执行如果在 register 直接解析调用 register 的时候 BeanFactory 可能还没初始化完成用户可以多次 setEnvironment、setBeanNameGenerator这些配置可以在 register 之后、refresh 之前设置如果 register 立刻解析后面修改的配置不会生效完全遵循 Spring 统一生命周期和AbstractApplicationContext模板流程对齐保证 web 与非 web 容器行为一致。Q对比原生AnnotationConfigApplicationContext原生AnnotationConfigApplicationContextregister/scan 会立刻执行而本类做了延迟这是 SpringBoot 为 web 场景做的适配改动。
阅读完成 · 觉得有帮助?
咨询建站