Java教程

springboot源码(七)

本文主要是介绍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容器加载原理。

 

这篇关于springboot源码(七)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!