SpringBoot源码分析(8)--内置ApplicationContextInitializer

news2024/11/27 9:51:08

文章目录

  • 1、DelegatingApplicationContextInitializer
  • 2、SharedMetadataReaderFactoryContextInitializer
  • 3、ContextIdApplicationContextInitializer
  • 4、ConfigurationWarningsApplicationContextInitializer
  • 5、ServerPortInfoApplicationContextInitializer
  • 6、ConditionEvaluationReportLoggingListener
  • 7、RSocketPortInfoApplicationContextInitializer

本文基于spring-boot-2.2.14.BUILD-SNAPSHOT源码分析。

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

protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

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

spring-boot/src/main/resources/META-INF/spring.factories

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

1、DelegatingApplicationContextInitializer

获取环境中属性context.initializer.classesInitializer配置的ApplicationContextInitializer列表, 执行其initialize()方法。

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

//代理初始化器
public class DelegatingApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
        
    private static final String PROPERTY_NAME = "context.initializer.classes";
    
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        //获取environment中配置的context.initializer.classes属性
        //其值为各个initializer,用逗号分隔开
        List<Class<?>> initializerClasses = getInitializerClasses(environment);
        if (!initializerClasses.isEmpty()) {
            //获取到各个initializer,并执行其initialize()方法
            applyInitializerClasses(context, initializerClasses);
        }
    }
            
}

进入getInitializerClasses方法

private static final String PROPERTY_NAME = "context.initializer.classes";

private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
	String classNames = env.getProperty(PROPERTY_NAME);
	List<Class<?>> classes = new ArrayList<>();
	if (StringUtils.hasLength(classNames)) {
		for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
			classes.add(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<>();
	for (Class<?> initializerClass : initializerClasses) {
		initializers.add(instantiateInitializer(contextClass, initializerClass));
	}
	applyInitializers(context, initializers);
}

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

private void applyInitializers(ConfigurableApplicationContext context,
			List<ApplicationContextInitializer<?>> initializers) {
	initializers.sort(new AnnotationAwareOrderComparator());
	for (ApplicationContextInitializer initializer : initializers) {
		initializer.initialize(context);
	}
}

最终调用了它们的initialize方法

2、SharedMetadataReaderFactoryContextInitializer

添加了一个类型为CachingMetadataReaderFactoryPostProcessor的BeanFactoryPostProcessor, CachingMetadataReaderFactoryPostProcessor会做两件事情

  • 注册一个名称为internalCachingMetadataReaderFactory, 类型为SharedMetadataReaderFactoryBean的bean, 用于读取bean的元数据Metadata
  • 获取名称为internalConfigurationAnnotationProcessor, 类型为ConfigurationClassPostProcessor的bean定义, 为其添加name为metadataReaderFactory, value为internalCachingMetadataReaderFactory的internalCachingMetadataReaderFactory
class SharedMetadataReaderFactoryContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

    public static final String BEAN_NAME = "org.springframework.boot.autoconfigure."
            + "internalCachingMetadataReaderFactory";

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //spring上下文容器添加一个CachingMetadataReaderFactoryPostProcessor
        applicationContext.addBeanFactoryPostProcessor(
                new 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的元数据解析

3、ContextIdApplicationContextInitializer

初始化容器ID,这个类的作用是给容器设置一个ID,其实就是我们的项目名, 获取属性spring.application.name配置的应用名称, 如果不存在的话, 默认使用application

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
    //获取并设置容器ID
    ContextId contextId = getContextId(applicationContext);
    applicationContext.setId(contextId.getId());
    //容器beanFactory中注册一个名称为ContextId类名
    //值为contextId的bean
    applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
            contextId);
}

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

//获取ContextID
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
    //父容器获取spring.application.name对应的bean
    ApplicationContext parent = applicationContext.getParent();
    if (parent != null && parent.containsBean(ContextId.class.getName())) {
        return parent.getBean(ContextId.class).createChildId();
    }
    //父容器获取不到,则生成一个contextId
    return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}

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

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

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

//获取应用ID
private String getApplicationId(ConfigurableEnvironment environment) {
    //获取属性:spring.application.name
    String name = environment.getProperty("spring.application.name");
    //如果为空,默认名application
    return StringUtils.hasText(name) ? name : "application";
}

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

 //ContextId类
class ContextId {
    //原子Long类
    private final AtomicLong children = new AtomicLong(0);

    private final String id;

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

    ContextId createChildId() {
        //线程安全递增
        return new ContextId(this.id + "-" + this.children.incrementAndGet());
    }

    String getId() {
        return this.id;
    }
}

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

4、ConfigurationWarningsApplicationContextInitializer

配置告警初始化器(通过分析源码实际情况,默认扫描启动类所在的路径(或者@ComponentScan注解指定的路径)如果系统配置包扫描到了org或者org.springframework包就会发出警告打印warn日志并停止系统启动

在这里插入图片描述
ConfigurationWarningsApplicationContextInitializer初始化器源码如下:

public class ConfigurationWarningsApplicationContextInitializer
		implements ApplicationContextInitializer<ConfigurableApplicationContext> {

	private static final Log logger = LogFactory.getLog(ConfigurationWarningsApplicationContextInitializer.class);
	//初始化方法会在容器启动时调用,并将ConfigurationWarningsPostProcessor后置处理器注入到应用上下文中
	@Override
	public void initialize(ConfigurableApplicationContext context) {
		context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
	}

	/**
	 * 返回有问题的扫描包(@ComponentScan)Check对象
	 * @return the checks to apply
	 */
	protected Check[] getChecks() {
		return new Check[] { new ComponentScanPackageCheck() };
	}
}

ComponentScanPackageCheck是ConfigurationWarningsApplicationContextInitializer的一个内部类,源码分析

/**
* 可以应用的单一检查
 */
@FunctionalInterface
protected interface Check {

	/**
	 * 返回检查结果,如果检查失败,则返回警告,如果没有问题,则返回null
	 * @param registry the {@link BeanDefinitionRegistry}
	 * @return a warning message or {@code null}
	 */
	String getWarning(BeanDefinitionRegistry registry);

}

/**
 * 检查@ComponentScan注解扫描有问题的包
 */
protected static class ComponentScanPackageCheck implements Check {

	private static final Set<String> PROBLEM_PACKAGES;
	//定义扫描有问题的包
	static {
		Set<String> packages = new HashSet<>();
		packages.add("org.springframework");
		packages.add("org");
		PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
	}
	
	//检查@ComponentScan注解扫描的包是否有问题,如果有,则返回警告,否则返回null
	@Override
	public String getWarning(BeanDefinitionRegistry registry) {
     //获取@ComponentScan注解扫描的包集合
		Set<String> scannedPackages = getComponentScanningPackages(registry);
     //获取有问题的扫描包集合
		List<String> problematicPackages = getProblematicPackages(scannedPackages);
		if (problematicPackages.isEmpty()) {
			return null;
		}
		return "Your ApplicationContext is unlikely to start due to a @ComponentScan of "
				+ StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
	}
	
	//获取@ComponentScan注解扫描的包
	protected Set<String> getComponentScanningPackages(BeanDefinitionRegistry registry) {
		Set<String> packages = new LinkedHashSet<>();
     //获取容器中所有bean定义名称
		String[] names = registry.getBeanDefinitionNames();
		for (String name : names) {
       //获取name对应的bean定义对象
			BeanDefinition definition = registry.getBeanDefinition(name);
			if (definition instanceof AnnotatedBeanDefinition) {
				AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
				addComponentScanningPackages(packages, annotatedDefinition.getMetadata());
			}
		}
		return packages;
	}
	
	//将bean实例上注解@ComponentScan扫描包
	private void addComponentScanningPackages(Set<String> packages, AnnotationMetadata metadata) {
		AnnotationAttributes attributes = AnnotationAttributes
				.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true));
		if (attributes != null) {
			addPackages(packages, attributes.getStringArray("value"));
			addPackages(packages, attributes.getStringArray("basePackages"));
			addClasses(packages, attributes.getStringArray("basePackageClasses"));
			if (packages.isEmpty()) {
				packages.add(ClassUtils.getPackageName(metadata.getClassName()));
			}
		}
	}

	private void addPackages(Set<String> packages, String[] values) {
		if (values != null) {
			Collections.addAll(packages, values);
		}
	}

	private void addClasses(Set<String> packages, String[] values) {
		if (values != null) {
			for (String value : values) {
				packages.add(ClassUtils.getPackageName(value));
			}
		}
	}
	
	//获取有问题的扫描包集合,即包名是:org或org.springframework
	private List<String> getProblematicPackages(Set<String> scannedPackages) {
		List<String> problematicPackages = new ArrayList<>();
		for (String scannedPackage : scannedPackages) {
       //判定包名是否有问题,即包名是:org或org.springframework
			if (isProblematicPackage(scannedPackage)) {
				problematicPackages.add(getDisplayName(scannedPackage));
			}
		}
		return problematicPackages;
	}
	//判定包名是否有问题,即包名是:org或org.springframework
	private boolean isProblematicPackage(String scannedPackage) {
		if (scannedPackage == null || scannedPackage.isEmpty()) {
			return true;
		}
		return PROBLEM_PACKAGES.contains(scannedPackage);
	}

	private String getDisplayName(String scannedPackage) {
		if (scannedPackage == null || scannedPackage.isEmpty()) {
			return "the default package";
		}
		return "'" + scannedPackage + "'";
	}
}

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

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

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

5、ServerPortInfoApplicationContextInitializer

服务端口初始化器, 分别实现了ApplicationContextInitializer和ApplicationListener接口, 在applicationContext中添加了事件监听器this, 监听了WebServerInitializedEvent事件, 配置服务的端口号

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

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //把this添加到Application的listener中
        applicationContext.addApplicationListener(this);
    }
    
    //监听处理WebServerInitializedEvent事件
    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        //获取environment中配置的server.ports
        String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
        setPortProperty(event.getApplicationContext(), propertyName,
                event.getWebServer().getPort());
    }
}

6、ConditionEvaluationReportLoggingListener

条件评估日志监听器, 主要作用是给applicationContext添加了一个ConditionEvaluationReportListener监听器, ConditionEvaluationReportListener监听了ContextRefreshedEvent和ApplicationFailedEvent事件, 打印相应日志

public class ConditionEvaluationReportLoggingListener
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        //applicationContext中添加一个ConditionEvaluationReportListener
        applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
        if (applicationContext instanceof GenericApplicationContext) {
            //注册一个ConditionEvaluationReport bean
            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方法

//用于记录Condition注解的评估情况
public final class ConditionEvaluationReport {

    //bean名称为autoConfigurationReport
    //类型为ConditionEvaluationReport
    private static final String BEAN_NAME = "autoConfigurationReport";
    
    //从beanFactory中获取ConditionEvaluationReport实例
    public static ConditionEvaluationReport get(
            ConfigurableListableBeanFactory beanFactory) {
        synchronized (beanFactory) {
            ConditionEvaluationReport report;
            if (beanFactory.containsSingleton(BEAN_NAME)) {
                //如果bean已经被注册,立即返回
                report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
            }
            else {
                //否则注册bean
                report = new ConditionEvaluationReport();
                beanFactory.registerSingleton(BEAN_NAME, 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)
       ......
       ......
       ......

7、RSocketPortInfoApplicationContextInitializer

添加了一个 RSocketServerInitializedEvent事件的 监听器到 ApplicationContext中。

public class RSocketPortInfoApplicationContextInitializer
		implements ApplicationContextInitializer<ConfigurableApplicationContext> {

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		/**
		  * 注入一个端口检查和设置的监听器,对应的事件RSocketServerInitializedEvent
		  **/
		applicationContext.addApplicationListener(new Listener(applicationContext));
	}
    
    //这里直接写了个内部类实现RSocketServerInitializedEvent事件的监听
	private static class Listener implements ApplicationListener<RSocketServerInitializedEvent> {

		private static final String PROPERTY_NAME = "local.rsocket.server.port";

		private final ConfigurableApplicationContext applicationContext;

		Listener(ConfigurableApplicationContext applicationContext) {
			this.applicationContext = applicationContext;
		}

		@Override
		public void onApplicationEvent(RSocketServerInitializedEvent event) {
			if (event.getServer().address() != null) {
				setPortProperty(this.applicationContext, event.getServer().address().getPort());
			}
		}

		private void setPortProperty(ApplicationContext context, int port) {
			if (context instanceof ConfigurableApplicationContext) {
				setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), port);
			}
			if (context.getParent() != null) {
				setPortProperty(context.getParent(), port);
			}
		}

		private void setPortProperty(ConfigurableEnvironment environment, int port) {
			MutablePropertySources sources = environment.getPropertySources();
			PropertySource<?> source = sources.get("server.ports");
			if (source == null) {
				source = new MapPropertySource("server.ports", new HashMap<>());
				sources.addFirst(source);
			}
			setPortProperty(port, source);
		}

		@SuppressWarnings("unchecked")
		private void setPortProperty(int port, PropertySource<?> source) {
			((Map<String, Object>) source.getSource()).put(PROPERTY_NAME, port);
		}

	}

}

所有的这些初始化类都没有进行启动服务的实质性操作,都是通过注册对象,埋点,后面invokeBeanFactoryPostProcessors才真正调用初始化方法,而且在项目启动之前

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[mongo]应用场景及选型

应用场景及选型 MongoDB 数据库定位 OLTP 数据库横向扩展能力&#xff0c;数据量或并发量增加时候架构可以自动扩展灵活模型&#xff0c;适合迭代开发&#xff0c;数据模型多变场景JSON 数据结构&#xff0c;适合微服务/REST API基于功能选择 MongoDB 关系型数据库迁移 从基…

Databend 开源周报第 105 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Databend 轻量级…

【我们一起60天准备考研算法面试(大全)-第三十八天 38/60】【双指针】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

机载激光快速建模赋能美丽乡村建设

二十大报告将“城乡人居环境明显改善&#xff0c;美丽中国建设成效显著”列入未来五年的主要目标任务&#xff0c;而乡村规划是美丽乡村建设中最为重要的一环。但是&#xff0c;传统测绘作业方式无法完全满足乡村规划工作中对高效率获取高现势性、多元化测绘成果的需求。 项目…

Signal Desktop for Mac(专业加密通讯软件)中文版安装教程

想让您的聊天信息更安全和隐藏吗&#xff1f; Mac版本的Signal Desktop是MACOS上的专业加密通信工具&#xff0c;非常安全。使用信号协议&#xff0c;该协议结合了固定前密钥&#xff0c;双重RATCHES算法和3-DH握手信号&#xff0c;该信号可以确保第三方实体将不会传达您的消息…

AI量化模型预测——baseline学习笔记

一、赛题理解 1. 赛题名称 AI量化模型预测 2. 赛题理解 本赛事是一个量化金融挑战&#xff0c;旨在通过大数据与机器学习的方法&#xff0c;使用给定的训练集和测试集数据&#xff0c;预测未来中间价的移动方向。参赛者需要理解市场行为的原理&#xff0c;创建量化策略&#…

【excel密码】excel数据加密,如何设置?

Excel数据完成制作之后&#xff0c;想要保护工作表数据不被修改&#xff0c;我们可以对excel数据设置保护&#xff0c;确保数据的准确性。今天分享两种方法设置数据保护。 方法一&#xff1a;工作表/工作簿保护 这里的限制编辑被分为了两种方式&#xff0c;分别是保护工作表、…

opencv基础-34 图像平滑处理-2D 卷积 cv2.filter2D()

2D卷积是一种图像处理和计算机视觉中常用的操作&#xff0c;用于在图像上应用滤波器或卷积核&#xff0c;从而对图像进行特征提取、平滑处理或边缘检测等操作。 在2D卷积中&#xff0c;图像和卷积核都是二维的矩阵或数组。卷积操作将卷积核在图像上滑动&#xff0c;对每个局部区…

代码随想录算法训练营day59

文章目录 Day59 下一个更大元素II题目思路代码 接雨水题目思路代码 Day59 下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 题目 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每…

无涯教程-Perl - exit函数

描述 该函数判断EXPR,退出Perl解释器,并返回该值作为退出值。始终在退出之前运行脚本(和导入的程序包)中定义的所有END {}块。如果省略EXPR,则解释器以0值退出。不应用于退出子例程&#xff1b;否则,解释器将退出子例程。使用eval而死或使用return。 语法 以下是此函数的简单…

ffmpeg下载安装教程

ffmpeg官网下载地址https://ffmpeg.org/download.html 这里以windows为例,鼠标悬浮到windows图标上,再点击 Windows builds from gyan.dev 或者直接打开 https://www.gyan.dev/ffmpeg/builds/ 下载根据个人需要下载对应版本 解压下载的文件,并复制bin所在目录 新打开一个命令…

有谁可以介绍一些团队任务分配管理软件?

首先我们谈一谈做好团队任务分配管理能够有哪些帮助—— 最明显的就是能够大大提高工作效率。通过合理的任务分配&#xff0c;可以确保每个团队成员都能够专注于自己的职责和任务&#xff0c;避免资源浪费和重复劳动。团队成员清楚自己的任务&#xff0c;能够更加高效地完成工…

23款奔驰S450 4MATIC加装车载冰箱系统,快乐就是这么朴实无华呀

凉爽餐饮随时触手可及。容积10升的可拆卸冷藏箱与后排扶手和谐融合。如此一来&#xff0c;即使在炎炎夏日&#xff0c;也可享受沁凉的冷饮。

裂墙推荐服务设计:品牌设计的大方向,全流程设计

裂墙推荐服务设计&#xff1a;品牌设计的大方向 通过简单LOGO和口号形象打造好品牌 是太幼稚想法&#xff0c;图样图森破 品牌设计已经向全流程体验方向走 甚至供应链体系也必须加入其中 趣讲大白话&#xff1a;品牌建设太卷了 【趣讲信息科技250期】 ************************…

MySQL及SQL语句(3)

MySQL及SQL语句(3) 文章目录 MySQL及SQL语句(3)一、多表查询1.1 准备sql1.2 笛卡尔积1.3 多表查询的分类&#xff1a;内连接查询外连接查询子查询多表查询练习 二、事务2.1 事务的基本介绍概念操作实例事务提交的两种方式 2.2 事务的四大特征原子性持久性隔离性一致性 2.3 事务…

STL容器之vector

1、构造初始化 #include <iostream> #include <vector> using namespace std;void PrintVector(vector<int>& v) {for (vector<int>::iterator it v.begin(); it ! v.end(); it){cout << *it << " ";}cout << endl;…

Linux Shell 编程入门

从程序员的角度来看&#xff0c; Shell本身是一种用C语言编写的程序&#xff0c;从用户的角度来看&#xff0c;Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行&#xff0c;又可以利用 Shell脚本编程&#xff0c;完成更加复杂的操作。在Linux GUI日益完善的今天…

MyBatics-学习笔记

文章目录 1.概念介绍2.Mybatis快速入门1.创建用户表2.创建模块导入坐标3.编写配置文件4.编写sql映射文件5.编码 3.Mapper代理开发4.MyBatis核心配置文件5.商品增删改查案例&#xff08;重点&#xff09;6.参数传递6.2 多个参数 1.概念介绍 概念 MyBatis 是一款支持普通 SQL 查询…

【构建卷积神经网络】

构建卷积神经网络 卷积网络中的输入和层与传统神经网络有些区别&#xff0c;需重新设计&#xff0c;训练模块基本一致 全连接层&#xff1a;batch784&#xff0c;各个像素点之间都是没有联系的。 卷积层&#xff1a;batch12828&#xff0c;各个像素点之间是有联系的。 impor…