zoukankan      html  css  js  c++  java
  • springboot-01 springboot 启动 enviroment环境加载

    springboot 创建环境

    SpringApplication 准备环境

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    			ApplicationArguments applicationArguments) {
    	// Create and configure the environment
    	ConfigurableEnvironment environment = getOrCreateEnvironment(); //默认应用是SERVLET,new StandardServletEnvironment();
    	configureEnvironment(environment, applicationArguments.getSourceArgs());// enviroment和args进行绑定,如果配置文件不是defaultProperty,这个步骤实际上是个空操作
    	ConfigurationPropertySources.attach(environment);//给enviroment的source中增加key为configurationProperties的SpringConfigurationPropertySources对象
    	listeners.environmentPrepared(environment);//主要步骤,查找active的profile
    	bindToSpringApplication(environment);//与启动类进行绑定
    	if (!this.isCustomEnvironment) {
    		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    				deduceEnvironmentClass());
    	}
    	ConfigurationPropertySources.attach(environment);
    	return environment;
    }
    
    void environmentPrepared(ConfigurableEnvironment environment) {
    	for (SpringApplicationRunListener listener : this.listeners) {
    		listener.environmentPrepared(environment);
    	}
    }
    

    EventPublishingRunListener 执行监听事件

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    	try {
    		listener.onApplicationEvent(event);
    	}catch (ClassCastException ex) {
    		
    	}
    }
    

    ConfigFileApplicationListener 类进行环境加载操作

    主要是判断那些profile是active的,并对其配置文件进行解析

    1.解析环境

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    	postProcessors.add(this);//增加自身实例
    	AnnotationAwareOrderComparator.sort(postProcessors);
    	for (EnvironmentPostProcessor postProcessor : postProcessors) {
    		postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    	}
    }
    

    2.loadPostProcessors()方法获取spring.factories中EnvironmentPostProcessor 类配置

    # Environment Post Processors
    org.springframework.boot.env.EnvironmentPostProcessor=
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
    org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
    org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,
    org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
    

    3.ConfigFileApplicationListener类的postProcessEnvironment方法

           加载配置文件,以yml文件进行分析;

    protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    	RandomValuePropertySource.addToEnvironment(environment);
    	new Loader(environment, resourceLoader).load();
    }
    
    Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    	this.environment = environment;
    	this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
    	this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
    	this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
    			getClass().getClassLoader());//获取实例类,如下
    }
    
    
    
    

    spring.factories中PropertySourceLoader.class 配置的类
    yml文件解析的解析类和property文件的解析类

    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=
    org.springframework.boot.env.PropertiesPropertySourceLoader,
    org.springframework.boot.env.YamlPropertySourceLoader
    

    4.加载配置的yml或propertis文件的详细过程

    万恶的load方法

           以YML文件解析为例,总共涉及到六个load方法,每个方法都处理不同的业务

    ConfigFileApplicationListener类的总的加载方法

    void load() {
    	FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
    	(defaultProperties) -> {
    		this.profiles = new LinkedList<>();
    		this.processedProfiles = new LinkedList<>();
    		this.activatedProfiles = false;
    		this.loaded = new LinkedHashMap<>();
    		initializeProfiles();
    		while (!this.profiles.isEmpty()) {
    			Profile profile = this.profiles.poll();
    			if (isDefaultProfile(profile)) {
    				addProfileToEnvironment(profile.getName());
    			}
    			load(profile, this::getPositiveProfileFilter,
    					addToLoaded(MutablePropertySources::addLast, false));
    			this.processedProfiles.add(profile);
    		}
    		load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
    		addLoadedPropertySources();
    		applyActiveProfiles(defaultProperties);
    	});
    }
    

    根据路径加载配置文件,load()方法

    1. getSearchLocations()方法获取配置文件扫描路径。默认是DEFAULT_SEARCH_LOCATIONS。

    2. 配置文件的名称names 默认是 DEFAULT_NAMES 也即是application。

    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
    
    private static final String DEFAULT_NAMES = "application";
    
    private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    	getSearchLocations().forEach((location) -> {
    		boolean isFolder = location.endsWith("/");
    		Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
    		names.forEach((name) -> load(location, name, profile, filterFactory, consumer));//遍历路径查找配置文件
    	});
    }
    

    根据路径,查找yml和properites的配置文件

           根据路径 + 文件名称 + 后缀进行配置文件查找

    private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
    		DocumentConsumer consumer) {
    	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);
    			}
    		}
    	}
    }
    //PropertiesPropertySourceLoader  properties加载器的后缀
    public String[] getFileExtensions() {
    	return new String[] { "properties", "xml" };
    }
    
    //YamlPropertySourceLoader yml加载器的后缀
    @Override
    public String[] getFileExtensions() {
    	return new String[] { "yml", "yaml" };
    }
    
    private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
    				Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    	//省略部分代码。。。。。。。。。。。。。。。。。
    	load(loader, prefix + fileExtension, profile, profileFilter, consumer); //这里的load方法是加载资源的方法,具体过程如下
    }
    
    

    加载资源,配置数据解析

           判断是否为激活的YML配置文件,并且获取YML文件的配置信息

    private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
    				DocumentConsumer consumer) {
    	try {
    		Resource resource = this.resourceLoader.getResource(location);
    		//。。。。。。。。。。。。。。。。。
    		String name = "applicationConfig: [" + location + "]";
    		List<Document> documents = loadDocuments(loader, name, resource); //这里是yml文件的加载解析过程,具体如下代码
    		//。。。。。。。。。。。。。。。。
    		List<Document> loaded = new ArrayList<>();
    		for (Document document : documents) {
    			if (filter.match(document)) {
    				addActiveProfiles(document.getActiveProfiles());//设置激活yml配置文件
    				addIncludedProfiles(document.getIncludeProfiles());
    				loaded.add(document);
    			}
    		}
    		Collections.reverse(loaded);
    		if (!loaded.isEmpty()) {
    			loaded.forEach((document) -> consumer.accept(profile, document));
    		        //。。。。。。。。。。。。。。。。。    
    		}
    	}
    	catch (Exception ex) {
    		throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
    	}
    }
    //获取配置文件解析数据
    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);//又是load方法,这里是yml的数据解析过程
    		documents = asDocuments(loaded);
    		this.loadDocumentsCache.put(cacheKey, documents); 
    	}
    	return documents;
    }
    
    
    

    YamlPropertySourceLoader 加载yml配置文件

           这个类是YML文件的加载解析类,下面的类是具体的解析操作

    @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();//解析yml配置数据
    	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;
    }
    

    OriginTrackedYamlLoader类

          load()方法用于解析数据,将YML配置参数解析成为MAP集合

    List<Map<String, Object>> load() {
    	final List<Map<String, Object>> result = new ArrayList<>();
    	process((properties, map) -> result.add(getFlattenedMap(map)));
    	return result;
    }
    
    // YamlProcess类的process方法进行yml文件解析
    
    protected void process(MatchCallback callback) {
    	Yaml yaml = createYaml();
    	for (Resource resource : this.resources) {
    		boolean found = process(callback, yaml, resource);
    		if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
    			return;
    		}
    	}
    }
    
    

          以上就是springboot启动时environment的加载过程。

  • 相关阅读:
    K好数
    最大最小公倍数
    十六进制转十进制
    利用malloc定义数组
    01字串
    ubuntu 14.04 下jdk和sdk+eclipse 的配置
    Mysql3
    求最大连续子串
    UC笔试
    java实现随机洗牌算法
  • 原文地址:https://www.cnblogs.com/perferect/p/13326296.html
Copyright © 2011-2022 走看看