spring boot实战(第五篇)配置源码解析
来源:互联网 发布:u盘格式化怎么恢复数据 编辑:程序博客网 时间:2024/05/30 02:51
前言
环境(Environment)
package com.lkl.springboot.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.env.Environment;import org.springframework.stereotype.Component;/** * 注入enviroment * * @author liaokailin * @version $Id: DIEnviroment.java, v 0.1 2015年10月2日 下午9:17:19 liaokailin Exp $ */@Componentpublic class DIEnviroment { @Autowired Environment environment; public String getProValueFromEnviroment(String key) { return environment.getProperty(key); }}
// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();
<p class="p1"><span class="s1">private</span> ConfigurableEnvironment getOrCreateEnvironment() {</p><p class="p1"><span></span><span></span><span class="s1">if</span> (<span class="s1">this</span>.<span class="s2">environment</span> != <span class="s1">null</span>) {</p><p class="p2"><span class="s3"><span></span><span></span><span></span></span><span class="s1">return</span><span class="s3"> </span><span class="s1">this</span><span class="s3">.</span>environment<span class="s3">;</span></p><p class="p1"><span></span><span></span>}</p><p class="p2"><span class="s3"><span></span><span></span></span><span class="s1">if</span><span class="s3"> (</span><span class="s1">this</span><span class="s3">.</span>webEnvironment<span class="s3">) {</span></p><p class="p1"><span></span><span></span><span></span><span class="s1">return</span> <span class="s1">new</span> StandardServletEnvironment();</p><p class="p1"><span></span><span></span>}</p><p class="p1"><span></span><span></span><span class="s1">return</span> <span class="s1">new</span> StandardEnvironment();</p><p class="p3"></p><p class="p1"><span></span>}</p>初始environment 为空,this.webEnvironment 判断构建的是否为web环境,通过deduceWebEnvironment方法推演出为true
private boolean deduceWebEnvironment() {for (String className : WEB_ENVIRONMENT_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return false;}}return true;}由于可以得到得出构建的enviroment为StandardServletEnvironment
创建对象调用其父类已经自身构造方法,StandardServletEnvironment、StandardEnvironment无构造方法,调用AbstractEnvironment构造方法
public AbstractEnvironment() {customizePropertySources(this.propertySources);if (this.logger.isDebugEnabled()) {this.logger.debug(format("Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));}}首先看this.propertySources定义
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
从字面的意义可以看出MutablePropertySources为多PropertySource的集合,其定义如下:
public class MutablePropertySources implements PropertySources {private final Log logger;private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>(); ...}其中PropertySource保存配置资源信息
public abstract class PropertySource<T> {protected final Log logger = LogFactory.getLog(getClass());protected final String name;protected final T source; ...}
资源信息元数据PropertySource包含name和泛型,一份资源信息存在唯一的name以及对应泛型数据,在这里设计为泛型表明可拓展自定义类型。如需自定义或增加资源信息,即只需构建PropertySource或其子类,然后添加到
MutablePropertySources中属性List<PropertySource<?>>集合中,MutablePropertySources又作为AbstractEnvironment中的属性,因此将AbstractEnvironment保存在spring bean容器中即可访问到所有的PropertySource。
来看下对应的类图关系:
带着如上的猜想来继续查看源码。
继续来看AbstractEnvironment对应构造方法中的customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {}
为protected且无实现的方法,将具体的实现放在子类来实现,调用StandardServletEnvironment中的具体实现:
protected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));}super.customizePropertySources(propertySources);}
这里调用的propertiesSources即为AbstractEnvironment中的属性,该方法将往集合中添加指定名称的PropertySource;来看下addLast方法:
public void addLast(PropertySource<?> propertySource) {if (logger.isDebugEnabled()) {logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",propertySource.getName()));}removeIfPresent(propertySource);this.propertySourceList.add(propertySource);}
其中removeIfPresent(propertySource)从字面意义中也可以看出为如果存在该PropertySource的话则从集合中删除数据:
protected void removeIfPresent(PropertySource<?> propertySource) {this.propertySourceList.remove(propertySource);}
由于PropertySource中属性T泛型是不固定并对应内容也不固定,因此判断PropertySource在集合中的唯一性只能去看name,因此在PropertySource中重写equals,hashCode方法:
@Overridepublic boolean equals(Object obj) {return (this == obj || (obj instanceof PropertySource &&ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));}/** * Return a hash code derived from the {@code name} property * of this {@code PropertySource} object. */@Overridepublic int hashCode() {return ObjectUtils.nullSafeHashCode(this.name);}
从上可看出name标识PropertySource的唯一性。
至此StandardEnvironment的初始化完成.
创建Enviroment Bean
在bean中注入Enviroment实际为Enviroment接口的实现类,从类图中可以看出其子类颇多,具体在容器中是哪个子类就需要从代码获取答案。
在SpringApplication.run(String... args)中存在refresh(context)调用
protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}
实际调用AbstractApplicationContext中的refresh方法,在refresh方法调用prepareBeanFactory(beanFactory),其实现如下:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { ...// Register default environment beans.if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}}其中调用beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment())注册名称为environment的bean;
public ConfigurableEnvironment getEnvironment() {if (this.environment == null) {this.environment = createEnvironment();}return this.environment;}其中enviroment变量为前面创建StandardServletEnvironment;前后得到验证。
实战:动态加载资源
在实际项目中资源信息如果能够动态获取在修改线上产品配置时及其方便,下面来展示一个加载动态获取资源的案例,而不是加载写死的properties文件信息首先构造PropertySource,然后将其添加到Enviroment中
构造PropertySource
package com.lkl.springboot.config;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.Random;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.env.MapPropertySource;public class DynamicPropertySource extends MapPropertySource { private static Logger log = LoggerFactory.getLogger(DynamicPropertySource.class); private static ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1); static { scheduled.scheduleAtFixedRate(new Runnable() { @Override public void run() { map = dynamicLoadMapInfo(); } }, 1, 10, TimeUnit.SECONDS); } public DynamicPropertySource(String name) { super(name, map); } private static Map<String, Object> map = new ConcurrentHashMap<String, Object>(64); @Override public Object getProperty(String name) { return map.get(name); } //动态获取资源信息 private static Map<String, Object> dynamicLoadMapInfo() { //通过http或tcp等通信协议获取配置信息 return mockMapInfo(); } private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); private static Map<String, Object> mockMapInfo() { Map<String, Object> map = new HashMap<String, Object>(); int randomData = new Random().nextInt(); log.info("random data{};currentTime:{}", randomData, sdf.format(new Date())); map.put("dynamic-info", randomData); return map; }}
这里模拟动态获取配置信息;
添加到Enviroment
package com.lkl.springboot.config;import javax.annotation.PostConstruct;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.AbstractEnvironment;/** * 加载动态配置信息 * * @author liaokailin * @version $Id: DynamicConfig.java, v 0.1 2015年10月2日 下午11:12:44 liaokailin Exp $ */@Configurationpublic class DynamicConfig { public static final String DYNAMIC_CONFIG_NAME = "dynamic_config"; @Autowired AbstractEnvironment environment; @PostConstruct public void init() { environment.getPropertySources().addFirst(new DynamicPropertySource(DYNAMIC_CONFIG_NAME)); }}
在看完前面的源码以后 上面的两段代码非常容易理解~~
archaius为开源的配置管理api,有兴趣的同学可研究一下: https://github.com/Netflix/archaius。
下一篇将讲解spring boot如何加载application.xml。
转载请注明
http://blog.csdn.net/liaokailin/article/details/48186331
欢迎关注,您的肯定是对我最大的支持
- spring boot实战(第五篇)配置源码解析
- spring boot实战(第五篇)配置源码解析
- 【Spring Boot】SpringBoot-自动配置源码解析
- spring boot实战(第四篇)分散配置
- spring boot实战(第四篇)分散配置
- 【Spring实战】Spring注解配置工作原理源码解析
- Spring Boot自动配置实战
- spring boot banner 源码解析
- spring boot实战(第三篇)事件监听源码分析
- spring boot实战(第九篇)Application创建源码分析
- spring boot实战(第十四篇)整合RabbitMQ源码分析前言
- spring boot实战(第十五篇)嵌入tomcat源码分析
- spring boot实战(第三篇)事件监听源码分析
- spring boot实战(第三篇)事件监听源码分析
- spring boot实战(第九篇)Application创建源码分析
- spring boot实战(第十四篇)整合RabbitMQ源码分析前言
- spring boot实战(第十五篇)嵌入tomcat源码分析
- Spring Boot实战之Spring基础配置
- Android应用启动界面的实现方法
- ocp-72
- Xcode7.0
- ocp-73
- Android开发--CardView使用
- spring boot实战(第五篇)配置源码解析
- IOS开发笔记-01按钮操作-08.git的简单使用
- ocp-74
- ocp-75
- Unity mesh 合并
- ocp-76
- hdu 4777
- ocp-77
- ocp-78