SpringBoot源码解析(十五)内置ApplicationContextInitializer

本篇文章是对上篇prepareContext的补充,在该方法的执行过程中,遍历了最初从META-INF/spring.factories文件中加载到的ApplicationContextInitializer,依次调用了其initialize方法

回顾第一篇文章,SpringApplication初始化的时候,一共加载到了6个内置的ApplicationContextInitializer,本篇文章就逐一分析每个内置的初始化器做了哪些事情

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

ConfigurationWarningsApplicationContextInitializer

这个类的作用是检查扫描路径是否合法,对非法的扫描路径,在控制台打印一行警告日志

直接看它的initialize方法

    public void initialize(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(new ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(this.getChecks()));
    }

往容器中添加了一个BeanFactoryPostProcessor,类型是其内部类ConfigurationWarningsPostProcessor,这个类实际上实现了BeanDefinitionRegistryPostProcessor 接口,它是BeanFactoryPostProcessor的子类

    protected static final class ConfigurationWarningsPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
        
        private ConfigurationWarningsApplicationContextInitializer.Check[] checks;
        
        public ConfigurationWarningsPostProcessor(ConfigurationWarningsApplicationContextInitializer.Check[] checks) {
            this.checks = checks;
        }

        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }

        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            ConfigurationWarningsApplicationContextInitializer.Check[] var2 = this.checks;
            int var3 = var2.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                ConfigurationWarningsApplicationContextInitializer.Check check = var2[var4];
                String message = check.getWarning(registry);
                if (StringUtils.hasLength(message)) {
                    this.warn(message);
                }
            }

        }
        ......
    }

在Spring容器执行refresh的时候,最开始就会先处理BeanFactoryPostProcessor,很多中间件都是依赖这个扩展点和Spring容器整合在一起的,而在这个处理过程中,又会优先处理子类BeanDefinitionRegistryPostProcessor 的回调,再处理父类BeanFactoryPostProcessor的回调,也就是先执行postProcessBeanDefinitionRegistry方法,再执行postProcessBeanFactory方法,这块涉及到Spring容器刷新的流程,之前我们也说过本系列文章重点在于SpringBoot在Spring基础上的扩展,所以这块内容就不详细展开了

而该类的postProcessBeanFactory方法为空,所以我们只需要看postProcessBeanDefinitionRegistry的实现就行了,它遍历了内部的Check数组,依次调用了其getWarning方法,这个Check数组,是在构造函数中传进来的,调用了内部的getChecks()方法

首先看下Check的定义,它也是当前初始化器内部定义的函数式接口

    @FunctionalInterface
    protected interface Check {
        String getWarning(BeanDefinitionRegistry registry);
    }

getChecks()方法返回的Check数组只包含一个元素ComponentScanPackageCheck,也是一个内部类

    protected ConfigurationWarningsApplicationContextInitializer.Check[] getChecks() {
        return new ConfigurationWarningsApplicationContextInitializer.Check[]{new ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck()};
    }

重点看下ComponentScanPackageCheck的getWarning方法实现

    protected static class ComponentScanPackageCheck implements ConfigurationWarningsApplicationContextInitializer.Check {
        private static final Set<String> PROBLEM_PACKAGES;

        protected ComponentScanPackageCheck() {
        }

        public String getWarning(BeanDefinitionRegistry registry) {
            Set<String> scannedPackages = this.getComponentScanningPackages(registry);
            List<String> problematicPackages = this.getProblematicPackages(scannedPackages);
            return problematicPackages.isEmpty() ? null : "Your ApplicationContext is unlikely to start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
        }
        ......
        ......
        static {
            Set<String> packages = new HashSet();
            packages.add("org.springframework");
            packages.add("org");
            PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
        }
    }

其内部有一个路径列表PROBLEM_PACKAGES,在静态代码块中初始化了两个元素,org和org.springframework
scannedPackages就是我们要扫描的路径,默认情况下即启动类所在的路径
然后检查PROBLEM_PACKAGES是否包含了我们的扫描路径,如果包含了,就把相关的路径拼接成字符串返回,否则返回null
在外面遍历Check调用getWarning方法的时候,如果返回的字符串不是null,就打印一行警告

    private void warn(String message) {
        if (ConfigurationWarningsApplicationContextInitializer.logger.isWarnEnabled()) {
            ConfigurationWarningsApplicationContextInitializer.logger.warn(String.format("%n%n** WARNING ** : %s%n%n", message));
        }
    }

最终在控制台打印如下日志

** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of 'org'.

这里只是打印一行日志,并不会停止程序,不过实际测试下来程序是没办法正常启动的,这个路径是Spring框架本身的包路径,扫描这个包会干扰Spring正常执行流程,陷入循环,当然正常情况下我们项目的路径也不会这样定义,除非是在整活…

ContextIdApplicationContextInitializer

这个类的作用是给容器设置一个ID,其实就是我们的项目名

    public void initialize(ConfigurableApplicationContext applicationContext) {
        ContextIdApplicationContextInitializer.ContextId contextId = this.getContextId(applicationContext);
        applicationContext.setId(contextId.getId());
        applicationContext.getBeanFactory().registerSingleton(ContextIdApplicationContextInitializer.ContextId.class.getName(), contextId);
    }

第一行先获取了一个容器ID对象,然后把ID属性设置到容器中,并把这个ID对象作为一个单例Bean注册到容器的单例池
我们看下这个ContextId怎么来的,进入getContextId方法

    private ContextIdApplicationContextInitializer.ContextId getContextId(ConfigurableApplicationContext applicationContext) {
        ApplicationContext parent = applicationContext.getParent();
        return parent != null && parent.containsBean(ContextIdApplicationContextInitializer.ContextId.class.getName()) ? ((ContextIdApplicationContextInitializer.ContextId)parent.getBean(ContextIdApplicationContextInitializer.ContextId.class)).createChildId() : new ContextIdApplicationContextInitializer.ContextId(this.getApplicationId(applicationContext.getEnvironment()));
    }

如果有父容器,就根据父容器的ID创建一个子容器ID,格式为 父容器ID - 子容器序号

	ContextIdApplicationContextInitializer.ContextId createChildId() {
        return ContextIdApplicationContextInitializer.this.new ContextId(this.id + "-" + this.children.incrementAndGet());
    }

如果没有父容器,就到environment中取spring.application.name属性,没有配置的话默认为application

    private String getApplicationId(ConfigurableEnvironment environment) {
        String name = environment.getProperty("spring.application.name");
        return StringUtils.hasText(name) ? name : "application";
    }

将取到的结果作为参数传给ContextId的构造函数

   class ContextId {
   		......
        private final String id;

        ContextId(String id) {
            this.id = id;
        }
        ......
    }

也就是说默认情况下,这个ContextId存了我们的项目名,然后把它设置到了容器中

DelegatingApplicationContextInitializer

SpringBoot允许我们通过各种属性配置方式自定义一些ApplicationContextInitializer,它们的调用时机就是该类的initialize方法

    public void initialize(ConfigurableApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
        if (!initializerClasses.isEmpty()) {
            this.applyInitializerClasses(context, initializerClasses);
        }
    }

进入getInitializerClasses方法

    private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
        String classNames = env.getProperty("context.initializer.classes");
        List<Class<?>> classes = new ArrayList();
        if (StringUtils.hasLength(classNames)) {
            String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
            int var5 = var4.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String className = var4[var6];
                classes.add(this.getInitializerClass(className));
            }
        }
        return classes;
    }

它从environment中找到context.initializer.classes属性,以逗号为分隔符解析成Class列表并返回

由于这里只通过getProperty方法取了一次,我们之前分析过,这个方法会从前往后遍历所有的PropertySource,取到了就立即返回,所以通过不同方式,比如启动参数、系统配置等不同途径设置的context.initializer.classes,只有优先级最高的那个会生效

然后对于找到的自定义初始化器,调用applyInitializerClasses方法

    private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
        Class<?> contextClass = context.getClass();
        List<ApplicationContextInitializer<?>> initializers = new ArrayList();
        Iterator var5 = initializerClasses.iterator();

        while(var5.hasNext()) {
            Class<?> initializerClass = (Class)var5.next();
            initializers.add(this.instantiateInitializer(contextClass, initializerClass));
        }

        this.applyInitializers(context, initializers);
    }

先实例化,然后调用applyInitializers方法

    private void applyInitializers(ConfigurableApplicationContext context, List<ApplicationContextInitializer<?>> initializers) {
        initializers.sort(new AnnotationAwareOrderComparator());
        Iterator var3 = initializers.iterator();

        while(var3.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var3.next();
            initializer.initialize(context);
        }
    }

最终调用了它们的initialize方法

ServerPortInfoApplicationContextInitializer

这个类本身也是个监听器,实现了ApplicationListener接口,监听的事件类型为WebServerInitializedEvent

    public class ServerPortInfoApplicationContextInitializer 
		 implements ApplicationContextInitializer<ConfigurableApplicationContext>, 
					ApplicationListener<WebServerInitializedEvent> {

它通过initialize方法将自己添加到了容器的监听器列表中

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addApplicationListener(this);
    }

SharedMetadataReaderFactoryContextInitializer

这个类跟第一个初始化器一样,也在initialize方法中添加了一个BeanFactoryPostProcessor

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor());
    }

具体类型为CachingMetadataReaderFactoryPostProcessor,跟第一个初始化器的逻辑一样,它也是内部类,并且实现的也是BeanDefinitionRegistryPostProcessor接口,其中postProcessBeanFactory方法为空,所以只需要看其postProcessBeanDefinitionRegistry方法

    private static class CachingMetadataReaderFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
        private CachingMetadataReaderFactoryPostProcessor() {
        }

		......
		......

        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }

        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            this.register(registry);
            this.configureConfigurationClassPostProcessor(registry);
        }
        ......
    }

首先register方法从内部类SharedMetadataReaderFactoryBean获取了一个BeanDefinition,并注册到了容器的BeanDefinitionMap中

    private void register(BeanDefinitionRegistry registry) {
        BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean.class, SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean::new).getBeanDefinition();
        registry.registerBeanDefinition("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory", definition);
    }

这个SharedMetadataReaderFactoryBean顾名思义,是一个FactoryBean,同时它实现了BeanClassLoaderAware接口,在这个接口的回调方法setBeanClassLoader中初始化了内部的ConcurrentReferenceCachingMetadataReaderFactory,并在getObject方法返回

	static class SharedMetadataReaderFactoryBean implements FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>, BeanClassLoaderAware, ApplicationListener<ContextRefreshedEvent> {
        private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory;

        SharedMetadataReaderFactoryBean() {
        }

        public void setBeanClassLoader(ClassLoader classLoader) {
            this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(classLoader);
        }

        public ConcurrentReferenceCachingMetadataReaderFactory getObject() throws Exception {
            return this.metadataReaderFactory;
        }

返回的这个ConcurrentReferenceCachingMetadataReaderFactory,它可以生产一个MetadataReader,这个Reader的作用就是读取类的元数据,包括Class相关的信息,比如是否接口、是否抽象类、是否有注解等等,以及获得注解相关的元数据,比如加了哪些注解等等,在整个Bean的生命周期中起到了非常重要的作用

register方法执行完毕,调用configureConfigurationClassPostProcessor方法

    private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
        try {
            BeanDefinition definition = registry.getBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor");
            definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"));
        } catch (NoSuchBeanDefinitionException var3) {
            ;
        }
    }

先从容器获取了名为internalConfigurationAnnotationProcessor的BeanDefinition,然后把上面生成的metadataReaderFactory设置到它的属性中

在新建Spring容器的时候,会初始化一个BeanDefinitionReader,而这个Reader的初始化过程中,会往容器中注册一个ConfigurationClassPostProcessor,名字就是上面的internalConfigurationAnnotationProcessor,它是Spring容器完成扫描的起点,包括@Component、@Configuration的处理都是在这个类中进行的,而完成这些工作,自然需要解析每个类的元数据,所以它把metadataReaderFactory赋给了ConfigurationClassPostProcessor的属性,后续就会用它来完成一些Bean的元数据解析

ConditionEvaluationReportLoggingListener

    public void initialize(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        applicationContext.addApplicationListener(new ConditionEvaluationReportLoggingListener.ConditionEvaluationReportListener());
        if (applicationContext instanceof GenericApplicationContext) {
            this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
        }
    }

先往容器的监听器列表添加一个监听器ConditionEvaluationReportListener,这个类是其内部类,通过supportsEventType方法指定了感兴趣的事件类型为ContextRefreshedEvent和ApplicationFailedEvent

    private class ConditionEvaluationReportListener implements GenericApplicationListener {
        private ConditionEvaluationReportListener() {
        }
		......
		......
        public boolean supportsEventType(ResolvableType resolvableType) {
            Class<?> type = resolvableType.getRawClass();
            if (type == null) {
                return false;
            } else {
                return ContextRefreshedEvent.class.isAssignableFrom(type) || ApplicationFailedEvent.class.isAssignableFrom(type);
            }
        }
        ......
    }

具体在这两个事件做了什么处理,我们后面说到具体事件的时候再来分析

然后下面的if分支,当前Spring容器的类型是AnnotationConfigServletWebServerApplicationContext,它是GenericApplicationContext的子类,所以会进if分支,调用ConditionEvaluationReport的get方法

    public static ConditionEvaluationReport get(ConfigurableListableBeanFactory beanFactory) {
        synchronized(beanFactory) {
            ConditionEvaluationReport report;
            if (beanFactory.containsSingleton("autoConfigurationReport")) {
                report = (ConditionEvaluationReport)beanFactory.getBean("autoConfigurationReport", ConditionEvaluationReport.class);
            } else {
                report = new ConditionEvaluationReport();
                beanFactory.registerSingleton("autoConfigurationReport", report);
            }

            locateParent(beanFactory.getParentBeanFactory(), report);
            return report;
        }
    }

先到容器中找名为autoConfigurationReport的单例Bean,如果没有的话就新建一个,并存储到容器的单例池,然后调用locateParent方法,如果存在父容器,检查父容器中有没有名为autoConfigurationReport的单例Bean,有的话,将父容器中的Report设置到当前Report的parent属性中

    private static void locateParent(BeanFactory beanFactory, ConditionEvaluationReport report) {
        if (beanFactory != null && report.parent == null && beanFactory.containsBean("autoConfigurationReport")) {
            report.parent = (ConditionEvaluationReport)beanFactory.getBean("autoConfigurationReport", ConditionEvaluationReport.class);
        }
    }

ConditionEvaluationReport的作用是在SpringBoot自动配置的过程中,打印一些匹配结果的DEBUG日志,包括哪些类完成了自动配置,哪些类的哪些条件没有满足而装载失败等等,比如下图中的部分结果

============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   CodecsAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)

   CodecsAutoConfiguration.JacksonCodecConfiguration matched:
      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
      ......
      ......
      ......

Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.aspectj.lang.annotation.Aspect' (OnClassCondition)
       ......
       ......
       ......
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Springboot源码解析PDF是一本深入解析Springboot框架的技术书籍,涵盖了Springboot的背景、原理、设计思路、运行机制、开发实践等方面。全书主要分为三部分:第一部分介绍Springboot的基础知识,包括Spring框架的常用注解、Springboot的配置、自动配置原理等;第二部分深入探讨Springboot的主要功能,如数据访问、Web开发、缓存、消息、安全等;第三部分着重介绍开发Springboot应用的最佳实践,包括Springboot与其他框架的结合使用、集成测试、监控与诊断等。 阅读Springboot源码解析PDF可以让开发者更深入理解Springboot的设计理念、技术实现以及应用场景,在实际项目开发中更加灵活、高效地使用Springboot。该书对于有一定JavaSpring框架基础的开发者来说是一本非常优秀的参考资料,也是Java开发者必不可少的技术读物。同时,该书也是借助源码解析的方式,让读者更加系统化地学习Springboot技术,具有很高的实用性和参考价值。总之,阅读Springboot源码解析PDF有助于开发者更好地掌握Springboot技术,提高应用开发效率和代码质量。 ### 回答2: Spring Boot源码解析pdf是一本介绍Spring Boot框架的开源书籍,该书的目的是帮助开发者深入了解Spring Boot的内部工作原理和实现细节。 该书首先从Spring Boot框架的源码结构和核心模块入手,详细介绍了Spring Boot的MVC、ORM、缓存、安全等核心功能的实现原理。同时,该书还介绍了Spring Boot对微服务的支持和整合Spring Cloud的方式,让开发者更深入了解Spring Boot在分布式架构中的应用。 在讲解源码实现原理的同时,该书还指出了一些常见的开发问题和易错点,并提供了相应的解决方案。此外,该书还通过一系列的案例,全面展示了Spring Boot的实际开发应用场景,帮助开发者更好地应用Spring Boot框架。 总的来说,Spring Boot源码解析pdf是一本非常实用的书籍,能够帮助开发者快速掌握Spring Boot框架的内部实现原理,提高开发效率和代码质量。同时,该书还可以作为学习Spring Boot的参考资料,对于想要深入学习和研究Spring Boot的开发者来说是非常有用的。 ### 回答3: Spring Boot 是一个很受欢迎的 Java 框架,它简化了开发者的工作,允许他们更快速地构建和部署应用程序。Spring Boot 的优点包括简洁的配置、内嵌的 Web 容器和现成的插件,让开发者可以更专注于业务逻辑。Spring Boot源码解析是学习它的关键。 Spring Boot源码解析Java初学者来说可能会比较复杂,但是它对于学习框架和原理是非常有益的。一个好的 Spring Boot 项目需要基于很好的基础,这就要求开发者了解其源码源码解析可以帮助开发者了解各种设计模式和组件的原理,有助于解决实际问题。 在 Spring Boot源码解析中,我们将会找到很多有用的信息。例如,我们可以看到 Spring Boot 如何通过注解处理器创建并配置 bean,这可以帮助我们更好地理解依赖注入和 IoC 容器。此外,我们还可以了解 Spring Boot 如何处理 HTTP 请求、创建模板、处理安全性等。这些知识可以帮助我们更好地理解 Spring Boot 内部的工作机制。 总之,Spring Boot源码解析是必不可少的一部分,它可以帮助开发者更好地了解和使用该框架,掌握在实践中所需的知识和技能。如果您是一名 Spring 开发者,那么深入了解 Spring Boot源码将会是一个很好的学习过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值