• springboot源码(七)


    springboot源码之属性文件加载原理剖析

    还是先看run方法

    public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    //此处就是配置文件application.yml 就在这个配置环境信息的事件中完成的 ConfigurableEnvironment environment
    = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }

     进入preparEnvironment()方法

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            ConfigurationPropertySources.attach(environment);
    //发布ApplicationEnvironmentPreparedEvent事件 listeners.environmentPrepared(environment); bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }

    进入到SpringPublishingRunListener中的environmentPrepared()中

    @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
    //发布ApplicationEnvironmentPreparedEvent事件
    this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); }

    进入multicasEvent()--》invokeListener()--》doInvokeListener()---》ConfigFileApplicationListener.onApplicationEvent()

    @Override
        public void onApplicationEvent(ApplicationEvent event) {
    //跟发布的事件类型吻合,会处理相应的逻辑
    if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } }

    进入onApplicationEnvironmentPreparedEvent()中

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    //会将spring.factories中的EnvironmentPostProcessor类型加载进来(但这并不是关键,关键是下一句) List
    <EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    //将ConfigFileApplicationListener加到postProcessor中 postProcessors.add(
    this); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }

    进入postProcessorEnvironment()方法中  ---》主要看ConfigFileApplicationListener.postProcessorEnvironment()

    @Override
        public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
            addPropertySources(environment, application.getResourceLoader());
        }
    protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            RandomValuePropertySource.addToEnvironment(environment);
    //创建Loader对象,并且完成属性加载器的加载,并调用load()方法
    new Loader(environment, resourceLoader).load(); }

    先看Loader的构造方法:

    Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
                this.environment = environment;
                this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
                this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
    //将spring.factories中的key为PropertySourceLoader的加载进来 (PropertiesPropertiesSourceLoader,YamlPropertiesSourceLoader)
    this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); }

    接着看load()方法

    void load() {
                FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
                        (defaultProperties) -> {
    //创建默认的profile链表
    this.profiles = new LinkedList<>();
    //创建已经处理过的profile类别
    this.processedProfiles = new LinkedList<>();
    //默认设置为未激活
    this.activatedProfiles = false;
    //创建loaded对象
    this.loaded = new LinkedHashMap<>();
    //加载配置profile的信息,默认为default initializeProfiles();
    //遍历profiles,并加载解析
    while (!this.profiles.isEmpty()) {
    //从双向链表中获取一个profile对象 Profile profile
    = this.profiles.poll();
    //非默认的就加入,进去看源码即可清楚
    if (isDefaultProfile(profile)) { addProfileToEnvironment(profile.getName()); } load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); }
    //解析profile load(
    null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
    //加载默认的属性文件 application.yml addLoadedPropertySources(); applyActiveProfiles(defaultProperties); }); }

    然后进入具体的apply()方法中查看

    static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,
                Consumer<PropertySource<?>> operation) {
            MutablePropertySources propertySources = environment.getPropertySources();
    //根据propertySourceName从众多的加载器中获取对应的加载器 默认的没有 PropertySource
    <?> original = propertySources.get(propertySourceName); if (original == null) {
    //此处会回调前面在apply方法中定义的lambda表达式 operation.accept(
    null); return; } propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties)); try { operation.accept(original); } finally { propertySources.replace(propertySourceName, original); } }

    接下来看load()方法

    private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    //getSearchLocations()是个关键,获取配置文件的位置(如果没有自定义的话) getSearchLocations().forEach((location)
    -> { boolean isDirectory = location.endsWith("/"); Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES; names.forEach((name) -> load(location, name, profile, filterFactory, consumer)); }); }
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
    
    //获得默认的扫描路径,如果没有特殊指定
    //就采用常量DEFAULT_SEARCH_LOCATIONS里定义的5个路径(之前是4个,现在变成5个了),加载顺序就是上述从前到后的顺序
    //getSearchNames()方法获取的是application这个默认的配置文件名(当然如果没有自定义的话)
    //然后逐一遍历加载目录机器指定文件名的文件
    private Set<String> getSearchLocations() { Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY); if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) { locations.addAll(getSearchLocations(CONFIG_LOCATION_PROPERTY)); } else { locations.addAll( asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS)); } return locations; }

    继续回到前面的方法:

    names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
    进入这个load()方法:
    private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
                    DocumentConsumer consumer) {
                if (!StringUtils.hasText(name)) {
    //循环遍历2个加载器PropertiesSourceLoader和YamlPropertiesSourceLoader加载properties、xml文件,和yml、yaml文件。
    for (PropertySourceLoader loader : this.propertySourceLoaders) { if (canLoadFileExtension(loader, location)) { load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer); return; } } throw new IllegalStateException("File extension of config file location '" + location + "' is not known to any PropertySourceLoader. If the location is meant to reference " + "a directory, it must end in '/'"); } Set<String> processed = new HashSet<>(); for (PropertySourceLoader loader : this.propertySourceLoaders) { for (String fileExtension : loader.getFileExtensions()) { if (processed.add(fileExtension)) { loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer); } } } }

     

    private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
                    DocumentConsumer consumer) {
    //开始加载默认的application.yml文件了 Resource[] resources
    = getResources(location); for (Resource resource : resources) { try { if (resource == null || !resource.exists()) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped missing config ", location, resource, profile); this.logger.trace(description); } continue; } if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped empty config extension ", location, resource, profile); this.logger.trace(description); } continue; } String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
    //开始加载application.yml List
    <Document> documents = loadDocuments(loader, name, resource); if (CollectionUtils.isEmpty(documents)) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped unloaded config ", location, resource, profile); this.logger.trace(description); } continue; } List<Document> loaded = new ArrayList<>(); for (Document document : documents) { if (filter.match(document)) { addActiveProfiles(document.getActiveProfiles()); addIncludedProfiles(document.getIncludeProfiles()); loaded.add(document); } } Collections.reverse(loaded); if (!loaded.isEmpty()) { loaded.forEach((document) -> consumer.accept(profile, document)); if (this.logger.isDebugEnabled()) { StringBuilder description = getDescription("Loaded config file ", location, resource, profile); this.logger.debug(description); } } } catch (Exception ex) { StringBuilder description = getDescription("Failed to load property source from ", location, resource, profile); throw new IllegalStateException(description.toString(), ex); } } }

    进入localDocuments()方法:

    private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
                    throws IOException {
                DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
    //先从缓存中获取 List
    <Document> documents = this.loadDocumentsCache.get(cacheKey); if (documents == null) {
    //缓存中没有直接加载 List
    <PropertySource<?>> loaded = loader.load(name, resource); documents = asDocuments(loaded); this.loadDocumentsCache.put(cacheKey, documents); } return documents; }

    进入load()---》YamlPropertiesSourceLoader.load()

    @Override
        public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
            if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
                throw new IllegalStateException(
                        "Attempted to load " + name + " but snakeyaml was not found on the classpath");
            }
            List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
            if (loaded.isEmpty()) {
                return Collections.emptyList();
            }
            List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
            for (int i = 0; i < loaded.size(); i++) {
                String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
                propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
                        Collections.unmodifiableMap(loaded.get(i)), true));
            }
            return propertySources;
        }

    得到yml文件中的信息。

    本文到此结束。下一篇:springboot源码之Tomcat容器加载原理。

  • 相关阅读:
    Zookeeper 选举机制
    Hadoop Yarn任务调度器
    Hadoop Yarn工作机制 Job提交流程
    Hadoop 切片机制
    Hadoop MapReduce工作流程
    Hadoop HDFS读写数据流程
    数据仓库 拉链表
    高动态范围照片*5
    Java实现的窗口计算器
    拍摄制作星轨拖尾视频 之 前期拍摄
  • 原文地址:https://www.cnblogs.com/bentuzi/p/16093478.html
Copyright © 2020-2023  润新知