package com.u51.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 $ */ @Component public class DIEnviroment { @Autowired Environment environment; public String getProValueFromEnviroment(String key) { return environment.getProperty(key); } }
// Create and configure the environment ConfigurableEnvironment 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; ...}
MutablePropertySources中属性List<PropertySource<?>>集合中,MutablePropertySources又作为AbstractEnvironment中的属性,因此将AbstractEnvironment保存在spring bean容器中即可访问到所有的PropertySource。
来看下对应的类图关系:
带着如上的猜想来继续查看源码。
继续来看AbstractEnvironment对应构造方法中的customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) { }
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); }
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); }
protected void removeIfPresent(PropertySource<?> propertySource) { this.propertySourceList.remove(propertySource); }
@Override public 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. */ @Override public int hashCode() { return ObjectUtils.nullSafeHashCode(this.name); }
至此StandardEnvironment的初始化完成.
在bean中注入Enviroment实际为Enviroment接口的实现类,从类图中可以看出其子类颇多,具体在容器中是哪个子类就需要从代码获取答案。
在SpringApplication.run(String... args)中存在refresh(context)调用
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
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;前后得到验证。
首先构造PropertySource,然后将其添加到Enviroment中
package com.u51.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; } }
package com.u51.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 $ */ @Configuration public 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。