spring boot实战(第五篇)配置源码解析

前言

前面的文章都采用markdown编写的,但编辑图片上极其不方便,以后还是采用网页的形式。
上一篇中讲述了spring boot配置文件的使用,本篇开始从源码的角度来看看配置文件。

环境(Environment)

学习过spring的同学都清楚,在bean中注入Enviroment实例即可调用配置资源信息,如以下代码
[java]  view plain  copy
  1. package com.lkl.springboot.config;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.core.env.Environment;  
  5. import org.springframework.stereotype.Component;  
  6.   
  7. /** 
  8.  * 注入enviroment 
  9.  *  
  10.  * @author liaokailin 
  11.  * @version $Id: DIEnviroment.java, v 0.1 2015年10月2日 下午9:17:19 liaokailin Exp $ 
  12.  */  
  13. @Component  
  14. public class DIEnviroment {  
  15.     @Autowired  
  16.     Environment environment;  
  17.   
  18.     public String getProValueFromEnviroment(String key) {  
  19.         return environment.getProperty(key);  
  20.     }  
  21. }  

      spring boot是如何来构建环境??

来看  SpringApplication.run(String... args)中的代码
[html]  view plain  copy
  1. // Create and configure the environment  
  2.             ConfigurableEnvironment environment = getOrCreateEnvironment();  
[html]  view plain  copy
  1. <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">  
  2. </p><p class="p1"><span>  </span>}</p>  
初始 environment 为空, this . webEnvironment 判断构建的是否为web环境,通过deduceWebEnvironment方法推演出为true
[html]  view plain  copy
  1. private boolean deduceWebEnvironment() {  
  2.         for (String className : WEB_ENVIRONMENT_CLASSES) {  
  3.             if (!ClassUtils.isPresent(className, null)) {  
  4.                 return false;  
  5.             }  
  6.         }  
  7.         return true;  
  8.     }  
由于可以得到得出构建的enviroment为 StandardServletEnvironment
其类继承关系如下
 

创建对象调用其父类已经自身构造方法,StandardServletEnvironment、StandardEnvironment无构造方法,调用AbstractEnvironment构造方法

[html]  view plain  copy
  1. public AbstractEnvironment() {  
  2.         customizePropertySources(this.propertySources);  
  3.         if (this.logger.isDebugEnabled()) {  
  4.             this.logger.debug(format(  
  5.                     "Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));  
  6.         }  
  7.     }  
首先看 this . propertySources定义

[html]  view plain  copy
  1. private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);  

从字面的意义可以看出MutablePropertySources为多PropertySource的集合,其定义如下:

[html]  view plain  copy
  1. public class MutablePropertySources implements PropertySources {  
  2.   
  3.     private final Log logger;  
  4.   
  5.     private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();  ...}  
其中PropertySource保存配置资源信息
[html]  view plain  copy
  1. public abstract class PropertySource<T> {  
  2.   
  3.     protected final Log logger = LogFactory.getLog(getClass());  
  4.   
  5.     protected final String name;  
  6.   
  7.     protected final T source; ...}  

资源信息元数据 PropertySource包含name和泛型,一份资源信息存在唯一的name以及对应泛型数据,在这里设计为泛型表明可拓展自定义类型。如需自定义或增加资源信息,即只需构建PropertySource或其子类,然后添加到

MutablePropertySources中属性List<PropertySource<?>>集合中,MutablePropertySources又作为AbstractEnvironment中的属性,因此将AbstractEnvironment保存在spring bean容器中即可访问到所有的PropertySource。

来看下对应的类图关系:

带着如上的猜想来继续查看源码。

继续来看AbstractEnvironment对应构造方法中的customizePropertySources

[html]  view plain  copy
  1. protected void customizePropertySources(MutablePropertySources propertySources) {  
  2.     }  

为protected且无实现的方法,将具体的实现放在子类来实现,调用StandardServletEnvironment中的具体实现:

[html]  view plain  copy
  1. protected void customizePropertySources(MutablePropertySources propertySources) {  
  2.         propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));  
  3.         propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));  
  4.         if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {  
  5.             propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));  
  6.         }  
  7.         super.customizePropertySources(propertySources);  
  8.     }  

这里调用的propertiesSources即为AbstractEnvironment中的属性,该方法将往集合中添加指定名称的PropertySource;来看下addLast方法:

[html]  view plain  copy
  1. public void addLast(PropertySource<?> propertySource) {  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",  
  4.                     propertySource.getName()));  
  5.         }  
  6.         removeIfPresent(propertySource);  
  7.         this.propertySourceList.add(propertySource);  
  8.     }  

其中 removeIfPresent(propertySource)从字面意义中也可以看出为如果存在该PropertySource的话则从集合中删除数据:

[html]  view plain  copy
  1. protected void removeIfPresent(PropertySource<?> propertySource) {  
  2.         this.propertySourceList.remove(propertySource);  
  3.     }  

由于PropertySource中属性T泛型是不固定并对应内容也不固定,因此判断 PropertySource在集合中的唯一性只能去看name,因此在PropertySource中重写equals,hashCode方法:

[html]  view plain  copy
  1. @Override  
  2.     public boolean equals(Object obj) {  
  3.         return (this == obj || (obj instanceof PropertySource &&  
  4.                 ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));  
  5.     }  
  6.   
  7.     /**  
  8.      * Return a hash code derived from the {@code name} property  
  9.      * of this {@code PropertySource} object.  
  10.      */  
  11.     @Override  
  12.     public int hashCode() {  
  13.         return ObjectUtils.nullSafeHashCode(this.name);  
  14.     }  

从上可看出name标识 PropertySource的唯一性。

至此StandardEnvironment的初始化完成.

创建Enviroment Bean

在bean中注入Enviroment实际为Enviroment接口的实现类,从类图中可以看出其子类颇多,具体在容器中是哪个子类就需要从代码获取答案。

在SpringApplication.run(String... args)中存在refresh(context)调用

[html]  view plain  copy
  1. protected void refresh(ApplicationContext applicationContext) {  
  2.         Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);  
  3.         ((AbstractApplicationContext) applicationContext).refresh();  
  4.     }  

实际调用 AbstractApplicationContext中的refresh方法,在refresh方法调用 prepareBeanFactory( beanFactory ),其实现如下:

[html]  view plain  copy
  1. protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {  
  2.          ...  
  3.   
  4.         // Register default environment beans.  
  5.         if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {  
  6.             beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());  
  7.         }  
  8.         if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {  
  9.             beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());  
  10.         }  
  11.         if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {  
  12.             beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());  
  13.         }  
  14.     }  
其中调用 beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment())注册名称为 environment的bean;

[html]  view plain  copy
  1. public ConfigurableEnvironment getEnvironment() {  
  2.         if (this.environment == null) {  
  3.             this.environment = createEnvironment();  
  4.         }  
  5.         return this.environment;  
  6.     }  
其中enviroment变量为前面创建 StandardServletEnvironment;前后得到验证。


实战:动态加载资源

在实际项目中资源信息如果能够动态获取在修改线上产品配置时及其方便,下面来展示一个加载动态获取资源的案例,而不是加载写死的properties文件信息

首先构造PropertySource,然后将其添加到Enviroment中

构造PropertySource

[html]  view plain  copy
  1. package com.lkl.springboot.config;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7. import java.util.Random;  
  8. import java.util.concurrent.ConcurrentHashMap;  
  9. import java.util.concurrent.Executors;  
  10. import java.util.concurrent.ScheduledExecutorService;  
  11. import java.util.concurrent.TimeUnit;  
  12.   
  13. import org.slf4j.Logger;  
  14. import org.slf4j.LoggerFactory;  
  15. import org.springframework.core.env.MapPropertySource;  
  16.   
  17. public class DynamicPropertySource extends MapPropertySource {  
  18.   
  19.     private static Logger                   log       = LoggerFactory.getLogger(DynamicPropertySource.class);  
  20.   
  21.     private static ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);  
  22.     static {  
  23.         scheduled.scheduleAtFixedRate(new Runnable() {  
  24.             @Override  
  25.             public void run() {  
  26.                 map = dynamicLoadMapInfo();  
  27.             }  
  28.   
  29.         }, 1, 10, TimeUnit.SECONDS);  
  30.     }  
  31.   
  32.     public DynamicPropertySource(String name) {  
  33.         super(name, map);  
  34.     }  
  35.   
  36.     private static Map<String, Object> map = new ConcurrentHashMap<String, Object>(64);  
  37.   
  38.     @Override  
  39.     public Object getProperty(String name) {  
  40.         return map.get(name);  
  41.     }  
  42.   
  43.     //动态获取资源信息  
  44.     private static Map<String, Object> dynamicLoadMapInfo() {  
  45.         //通过http或tcp等通信协议获取配置信息  
  46.         return mockMapInfo();  
  47.     }  
  48.   
  49.     private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");  
  50.   
  51.     private static Map<String, Object> mockMapInfo() {  
  52.         Map<String, Object> map = new HashMap<String, Object>();  
  53.         int randomData = new Random().nextInt();  
  54.         log.info("random data{};currentTime:{}", randomData, sdf.format(new Date()));  
  55.         map.put("dynamic-info", randomData);  
  56.         return map;  
  57.     }  
  58. }  

这里模拟动态获取配置信息;

添加到Enviroment

[html]  view plain  copy
  1. package com.lkl.springboot.config;  
  2.   
  3. import javax.annotation.PostConstruct;  
  4.   
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.context.annotation.Configuration;  
  7. import org.springframework.core.env.AbstractEnvironment;  
  8.   
  9. /**  
  10.  * 加载动态配置信息  
  11.  *   
  12.  * @author liaokailin  
  13.  * @version $Id: DynamicConfig.java, v 0.1 2015年10月2日 下午11:12:44 liaokailin Exp $  
  14.  */  
  15. @Configuration  
  16. public class DynamicConfig {  
  17.     public static final String DYNAMIC_CONFIG_NAME = "dynamic_config";  
  18.   
  19.     @Autowired  
  20.     AbstractEnvironment        environment;  
  21.   
  22.     @PostConstruct  
  23.     public void init() {  
  24.         environment.getPropertySources().addFirst(new DynamicPropertySource(DYNAMIC_CONFIG_NAME));  
  25.     }  
  26.   
  27. }  

在看完前面的源码以后 上面的两段代码非常容易理解~~


archaius为开源的配置管理api,有兴趣的同学可研究一下: https://github.com/Netflix/archaius。


下一篇将讲解spring boot如何加载application.xml。


本文转自http://blog.csdn.net/liaokailin/article/details/48186331

SpringBoot实战(第4版)清晰文字版,第 1 章 入门 ................................................ 1 1.1 Spring 风云再起 ........................................ 1 1.1.1 重新认识 Spring ............................ 2 1.1.2 Spring Boot 精要 ........................... 3 1.1.3 Spring Boot 不是什么 ................... 6 1.2 Spring Boot 入门 ....................................... 6 1.2.1 安装 Spring Boot CLI .................... 7 1.2.2 使用 Spring Initializr 初始化 Spring Boot 项目 .......................... 10 1.3 小结 ......................................................... 18 第 2 章 开发第一个应用程序 .................... 19 2.1 运用 Spring Boot ..................................... 19 2.1.1 查看初始化的 Spring Boot 新项目 .......................................... 21 2.1.2 Spring Boot 项目构建过程 解析 .............................................. 24 2.2 使用起步依赖 .......................................... 27 2.2.1 指定基于功能的依赖 ................... 28 2.2.2 覆盖起步依赖引入的传递依赖 .... 29 2.3 使用自动配置 .......................................... 30 2.3.1 专注于应用程序功能 ................... 31 2.3.2 运行应用程序 .............................. 36 2.3.3 刚刚发生了什么 ........................... 38 2.4 小结 ......................................................... 41 第 3 章 自定义配置 .................................... 42 3.1 覆盖 Spring Boot 自动配置 ..................... 42 3.1.1 保护应用程序 .............................. 43 3.1.2 创建自定义的安全配置 ............... 44 3.1.3 掀开自动配置的神秘面纱 ........... 48 3.2 通过属性文件外置配置 ........................... 49 3.2.1 自动配置微调 .............................. 50 3.2.2 应用程序 Bean 的配置外置 ......... 55 3.2.3 使用 Profile 进行配置 .................. 59 3.3 定制应用程序错误页面 ........................... 62 3.4 小结 ......................................................... 64 第 4 章 测试 ............................................... 66 4.1 集成测试自动配置 .................................. 66 4.2 测试 Web 应用程序 ................................. 68 4.2.1 模拟 Spring MVC ........................ 69 4.2.2 测试 Web 安全 ............................. 72 4.3 测试运行中的应用程序 ........................... 74 4.3.1 用随机端口启动服务器 ............... 75 4.3.2 使用 Selenium 测试 HTML 页面 ............................................. 76 4.4 小结 ......................................................... 78 第 5 章 Groovy 与 Spring Boot CLI ......... 80 5.1 开发 Spring Boot CLI 应用程序 .............. 80 5.1.1 设置 CLI 项目 .............................. 81 5.1.2 通过 Groovy 消除代码噪声 ......... 81 5.1.3 发生了什么 .................................. 85 5.2 获取依赖 .................................................. 86 5.2.1 覆盖默认依赖版本 ....................... 87 5.2.2 添加依赖仓库 .............................. 88 5.3 用 CLI 运行测试 ...................................... 89 5.4 创建可部署的产物 .................................. 91 5.5 小结 ......................................................... 91 第 6 章 在 Spring Boot 中使用 Grails ...... 93 6.1 使用 GORM 进行数据持久化 ................. 93 2 目 录 6.2 使用 Groovy Server Pages 定义视图 ....... 98 6.3 结合 Spring Boot 与 Grails 3 ................. 100 6.3.1 创建新的 Grails 项目 ................. 100 6.3.2 定义领域模型 ............................ 103 6.3.3 开发 Grails 控制器 ..................... 104 6.3.4 创建视图 .................................... 105 6.4 小结 ....................................................... 107 第 7 章 深入 Actuator .............................. 108 7.1 揭秘 Actuator 的端点 ............................ 108 7.1.1 查看配置明细 ............................ 109 7.1.2 运行时度量 ................................ 115 7.1.3 关闭应用程序 ............................ 121 7.1.4 获取应用信息 ............................ 121 7.2 连接 Actuator 的远程 shell .................... 122 7.2.1 查看 autoconfig 报告 ........... 123 7.2.2 列出应用程序的 Bean ............... 124 7.2.3 查看应用程序的度量信息 ......... 124 7.2.4 调用 Actuator 端点 .................... 125 7.3 通过 JMX 监控应用程序 ....................... 126 7.4 定制 Actuator......................................... 128 7.4.1 修改端点 ID ............................... 128 7.4.2 启用和禁用端点 ........................ 129 7.4.3 添加自定义度量信息 ................. 129 7.4.4 创建自定义跟踪仓库 ................. 132 7.4.5 插入自定义健康指示器 ............. 134 7.5 保护 Actuator 端点 ................................ 136 7.6 小结 ....................................................... 138 第 8 章 部署 Spring Boot 应用程序 ........ 139 8.1 衡量多种部署方式 ................................ 139 8.2 部署到应用服务器 ................................ 140 8.2.1 构建 WAR 文件 ......................... 141 8.2.2 创建生产 Profile ........................ 142 8.2.3 开启数据库迁移 ........................ 145 8.3 推上云端 ............................................... 150 8.3.1 部署到 Cloud Foundry ............... 150 8.3.2 部署到 Heroku ........................... 153 8.4 小结 ....................................................... 155 附录 A Spring Boot 开发者工具.............. 157 附录 B Spring Boot 起步依赖 ................. 163 附录 C 配置属性 ...................................... 169 附录 D Spring Boot 依赖 ......................... 202
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值