Spring Boot 外部化配置(二) - @ConfigurationProperties 、@EnableConfigurationProperties
3、外部化配置的核心
接着上一章,《Spring Boot 外部化配置(一)》
3.2 @ConfigurationProperties
众所周知,当 Spring Boot 集成外部组件后,就可在 properties 或 YAML 配置文件中定义组件需要的属性,如 Redis 组件:
spring.redis.url=redis://user:password@example.com:6379
spring.redis.host=localhost
spring.redis.password=123456
spring.redis.port=6379
其中都是以 spring.redis 为前缀。这其实是 Spring Boot 为每个组件提供了对应的 Properties 配置类,并将配置文件中的属性值給映射到配置类中,而且它们有个特点,都是以 Properties 结尾,如 Redis 对应的配置类是 RedisProperties:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
...
}
其中有个名为 @ConfigurationProperties 的注解,它的 prefix 参数就是约定好的前缀。该注解的功能就是将配置文件中的属性和 Properties 配置类中的属性进行映射,来达到自动配置的目的。这个过程分为两步,第一步是注册 Properties 配置类,第二步是绑定配置属性,过程中还涉及到一个注解,它就是 @EnableConfigurationProperties ,该注解是用来触发那两步操作的。我们以 Redis 为例来看它使用方式:
...
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
...
}
可以看到它的参数是 RedisProperties 配置类。通过前面的 《Spring Boot 自动装配(一)》 我们知道,该注解是属于 @Enable 模块注解,所以,该注解中必然有 @Import 导入的实现了 ImportSelector 或 ImportBeanDefinitionRegistrar 接口的类,具体的功能都由导入的类来实现。我们进入该注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
/**
* Convenient way to quickly register {@link ConfigurationProperties} annotated beans
* with Spring. Standard Spring Beans will also be scanned regardless of this value.
* @return {@link ConfigurationProperties} annotated beans to register
*/
Class<?>[] value() default {};
}
果不其然,通过 @Import 导入了 EnableConfigurationPropertiesImportSelector 类,整个的处理流程都是在该类中进行处理:
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = { ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
...
}
该类实现了 ImportSelector 接口,并重写了 selectImports 方法,该方法返回的类会被 Spring 加载。可以看到这里返回了两个类,其中 ConfigurationPropertiesBeanRegistrar 就是用来注册 Properties 配置类的,而 ConfigurationPropertiesBindingPostProcessorRegistrar 则是用来绑定配置属性,且它们都实现了 ImportBeanDefinitionRegistrar 接口,会在重写的 registerBeanDefinitions 方法中进行直接注册 Bean 的操作。以上特性都在 《Spring Boot 自动装配(一)》的 3.1 小节介绍过,这里不在叙述。接下来,我们分别介绍这两个类。
3.2.1 注册 Properties 配置类
我们先来看看 ConfigurationPropertiesBeanRegistrar 是如何注册这些配置类的。我们直接进入该类的实现:
public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
// 1、第一步会先执行重写的 registerBeanDefinitions 方法,
// 入参分别是 AnnotationMetadata 和 BeanDefinitionRegistry。
// AnnotationMetadata 是获取类的元数据的,如注解信息、 classLoader 等,
// BeanDefinitionRegistry 则是直接注册所需要的 Bean
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 2、调用 getTypes 方法,返回 Properties 配置类集合。进入 2.1 详细查看
// 3、调用 register 方法,把 Properties 配置类注册到 Spring 容器中。进入 3.1 详细查看
getTypes(metadata).forEach((type) -> register(registry, (ConfigurableListableBeanFactory) registry, type));
}
// 2.1
private List<Class<?>> getTypes(AnnotationMetadata metadata) {
// 获取指定注解的所有属性值,key是属性名称,Value是值
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
// 返回 key 名称为 value 的值,这里返回的就是 Properties 配置类
return collectClasses((attributes != null) ? attributes.get("value") : Collections.emptyList());
}
// 3.1
private void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory,
Class<?> type) {
// getName 返回的是 Bean 的名称。进入 3.2 详细查看
String name = getName(type);
// 判断有没有注册过这个 Bean
if (!containsBeanDefinition(beanFactory, name)) {
// 没有则注册该 Bean。入参是注册器、Bean 的名称、需注册的 Bean。进入 4 详细查看
registerBeanDefinition(registry, name, type);
}
}
// 3.2
private String getName(Class<?> type) {
// 获取 Properties 配置类上标注的 ConfigurationProperties 注解信息
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(type, ConfigurationProperties.class);
// 获取该注解中 prefix 的属性值
String prefix = (annotation != null) ? annotation.prefix() : "";
// 最后返回的是名称格式是 属性前缀-配置类全路径名,如:
// spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties
return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
}
// 4、
private void registerBeanDefinition(BeanDefinitionRegistry registry, String name, Class<?> type) {
assertHasAnnotation(type);
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(type);
// 通过 registerBeanDefinition 方法,注册 Bean 。
// 后期会有 Spring 系列的文章详细介绍该过程,到时候大家再一起讨论。
registry.registerBeanDefinition(name, definition);
}
}
执行完后,我们所有的 Properties 配置类就被注册到了 Spring 容器中。接下来,我们来看看配置文件中的数据是如何与 Properties 配置类中的属性进行绑定的。
3.2.2 绑定配置属性
我们直接进入 ConfigurationPropertiesBindingPostProcessorRegistrar 类中进行查看:
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
registerConfigurationPropertiesBindingPostProcessor(registry);
registerConfigurationBeanFactoryMetadata(registry);
}
}
...
}
这里也是在重写的 registerBeanDefinitions 方法中注册了两个 Bean,一个是 ConfigurationBeanFactoryMetadata,这个是用来存储元数据的,我们不做过多关注;另一个是 ConfigurationPropertiesBindingPostProcessor ,该类就是用来绑定属性的,我们主要对该类进行讨论:
public class ConfigurationPropertiesBindingPostProcessor
implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
...
}
可以看到,该类实现了几个接口,且都是 Spring 提供的扩展接口。这里我们简要介绍一下:
1、BeanPostProcessor:这是
Bean的后置处理器。该类有两个方法,一个是 postProcessBeforeInitialization ,Bean初始化前该方法会被调用;
另一个是 postProcessAfterInitialization ,Bean初始化后该方法会被调用;需注意的是,Spring上下文中所有Bean的初始化都会触发这两个方法。
2、ApplicationContextAware:这是Spring的Aware系列接口之一。该类有一个 setApplicationContext 方法,主要是用来获取ApplicationContext上下文对象;同理,如果是其它前缀的Aware,则获取相应前缀名的对象。
3、InitializingBean:这是Bean的生命周期相关接口。该类有一个 afterPropertiesSet 方法,当Bean的所有属性初始化后,该方法会被调用。
其中,BeanPostProcessor和InitializingBean的功能都是在Bean的生命周期中执行额外的操作。
这里我们简单的了解就行,后面会在 Spring 系列的文章中详细讨论。
接着,我们介绍该类中的方法:
public class ConfigurationPropertiesBindingPostProcessor
implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
...
public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
private ConfigurationBeanFactoryMetadata beanFactoryMetadata;
private ApplicationContext applicationContext;
private ConfigurationPropertiesBinder configurationPropertiesBinder;
// 1、这是重写的 ApplicationContextAware 接口中的方法,用来获取 ApplicationContext 上下文对象
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 2、这是重写的 InitializingBean 接口中的方法,当 Bean 的属性初始化后会被调用。
// 该方法主要对 ConfigurationBeanFactoryMetadata 和 ConfigurationPropertiesBinder 进行实例化
@Override
public void afterPropertiesSet() throws Exception {
this.beanFactoryMetadata = this.applicationContext.getBean(ConfigurationBeanFactoryMetadata.BEAN_NAME,
ConfigurationBeanFactoryMetadata.class);
this.configurationPropertiesBinder = new ConfigurationPropertiesBinder(this.applicationContext,
VALIDATOR_BEAN_NAME);
}
// 3、这是重写的 BeanPostProcessor 接口中的方法,在 Bean 初始化前会被调用,绑定属性的操作就是从这里开始。
// 入参 bean 就是待初始化的 Bean,beanName 就是 Bean 的名称
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ConfigurationProperties annotation = getAnnotation(bean, beanName, ConfigurationProperties.class);
if (annotation != null) {
bind(bean, beanName, annotation);
}
return bean;
}
...
}
我们先来看第二步的 afterPropertiesSet 方法,该方法中实例化了两个类,一个是从 ApplicationContext 中获取的
ConfigurationBeanFactoryMetadata 类,是用来操作元数据的,不做过多关注;另一个是通过带参构造器初始化的 ConfigurationPropertiesBinder 类,参数是 ApplicationContext 对象和 configurationPropertiesValidator 字符串。我们进入该类的构造器中:
class ConfigurationPropertiesBinder {
private final ApplicationContext applicationContext;
private final PropertySources propertySources;
private final Validator configurationPropertiesValidator;
private final boolean jsr303Present;
...
ConfigurationPropertiesBinder(ApplicationContext applicationContext, String validatorBeanName) {
this.applicationContext = applicationContext;
this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();
this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext,
validatorBeanName);
this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext);
}
...
}
该类中又实例化了四个类,我们重点关注 PropertySources 的实例化过程,具体是通过 PropertySourcesDeducer 类的 getPropertySources 方法,我们进入该类:
class PropertySourcesDeducer {
...
private final ApplicationContext applicationContext;
PropertySourcesDeducer(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
// 1、通过 extractEnvironmentPropertySources 方法,返回 MutablePropertySources 对象,
// MutablePropertySources 是 PropertySources 的实现类
public PropertySources getPropertySources() {
...
MutablePropertySources sources = extractEnvironmentPropertySources();
if (sources != null) {
return sources;
}
throw new IllegalStateException(
"Unable to obtain PropertySources from " + "PropertySourcesPlaceholderConfigurer or Environment");
}
// 2、调用 Environment 的 getPropertySources 方法,返回 MutablePropertySources
private MutablePropertySources extractEnvironmentPropertySources() {
Environment environment = this.applicationContext.getEnvironment();
if (environment instanceof ConfigurableEnvironment) {
return ((ConfigurableEnvironment) environment).getPropertySources();
}
return null;
}
...
}
看到这,大家应该比较熟悉了,Environment 就是我们在 《Spring Boot 外部化配置(一)》中 3.1 小节讲过的应用运行时的环境,通过该类可获取所有的外部化配置数据,而 MutablePropertySources 则是底层真正存储外部化配置对象的。
到这里,第二步的 afterPropertiesSet 方法就执行完了,主要是实例化了 ConfigurationPropertiesBinder 对象,而该对象中存储了所有的外部化配置。接着看第三步的 postProcessBeforeInitialization 方法:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ConfigurationProperties annotation = getAnnotation(bean, beanName, ConfigurationProperties.class);
if (annotation != null) {
bind(bean, beanName, annotation);
}
return bean;
}
上面说过,所有 Bean 初始化都会调用这个方法,所以先判断当前 Bean 有没有标注 @ConfigurationProperties 注解,有则表示当前 Bean 是 Properties 配置类,并调用 bind 方法对该类进行绑定属性的操作,我们进入该方法:
private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
...
try {
this.configurationPropertiesBinder.bind(target);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(beanName, bean, annotation, ex);
}
}
这里调用了在第二步实例化的 ConfigurationPropertiesBinder 对象中的 bind 方法:
class ConfigurationPropertiesBinder {
...
public void bind(Bindable<?> target) {
...
getBinder().bind(annotation.prefix(), target, bindHandler);
}
...
private Binder getBinder() {
if (this.binder == null) {
this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),
getConversionService(), getPropertyEditorInitializer());
}
return this.binder;
}
private Iterable<ConfigurationPropertySource> getConfigurationPropertySources() {
return ConfigurationPropertySources.from(this.propertySources);
}
...
}
里面先通过 getBinder() 返回 Binder 对象。在 getBinder 方法中是通过 Binder 带参构造器创建的该对象,我们主要关注 getConfigurationPropertySources 方法返回的第一个参数:
class ConfigurationPropertiesBinder {
...
private final PropertySources propertySources;
...
private Iterable<ConfigurationPropertySource> getConfigurationPropertySources() {
return ConfigurationPropertySources.from(this.propertySources);
}
...
}
具体的是通过 ConfigurationPropertySources 中的 from 方法返回,入参 propertySources 是在第二步实例化 ConfigurationPropertiesBinder 对象时初始化好的值,里面存储的是外部化配置的源对象 PropertySource ,我们进入该方法:
public final class ConfigurationPropertySources {
...
public static Iterable<ConfigurationPropertySource> from(Iterable<PropertySource<?>> sources) {
return new SpringConfigurationPropertySources(sources);
}
...
}
最终返回的就是 SpringConfigurationPropertySources 配置源对象,在 《Spring Boot 外部化配置(一)》中讲过,该类主要是做一个适配器的工作,将 MutablePropertySources 转换为 ConfigurationPropertySource。
之后,该对象传入了 Binder 的构造器中,用于创建该对象:
public class Binder {
...
private final Iterable<ConfigurationPropertySource> sources;
...
public Binder(Iterable<ConfigurationPropertySource> sources,
PlaceholdersResolver placeholdersResolver,
ConversionService conversionService,
Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
this.sources = sources;
...
}
...
}
至此, Binder 对象中就存有一份外部化配置的数据,且后续所有的绑定操作都在该类中进行。因后续中间过程实在太过庞杂,且不易理解,这里我们直接进入最后一步,对详细过程感兴趣的同学请自行研究,这里不再赘述。
进入最后阶段的 bind 方法:
// 这里着重介绍一下 BeanProperty 类,该类存储了 properties 配置类中的字段及字段的set、get方法,存储的是反射中的类。
// 如 RedisProperties 中的 url 字段,则 BeanProperty 对象中存储的是
// url 的 Field 类、setUrl 的 Method 类、getUrl 的 Method 类。
private <T> boolean bind(BeanSupplier<T> beanSupplier,
BeanPropertyBinder propertyBinder, BeanProperty property) {
// 这里获取的是字段名
String propertyName = property.getName();
// 这里获取的是字段类型
ResolvableType type = property.getType();
Supplier<Object> value = property.getValue(beanSupplier);
Annotation[] annotations = property.getAnnotations();
// 这里获取到了配置文件中的值,该值来源于 SpringConfigurationPropertySources 对象
Object bound = propertyBinder.bindProperty(propertyName,
Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
if (bound == null) {
return false;
}
if (property.isSettable()) {
// 最后则是通过 set Method 的 invoke 方法,也就是反射的形式进行赋值。
property.setValue(beanSupplier, bound);
}
else if (value == null || !bound.equals(value.get())) {
throw new IllegalStateException(
"No setter found for property: " + property.getName());
}
return true;
}
至此,整个绑定配置属性的流程结束。可以看到,最终获取的外部化配置数据来源于前文加载的 Environment 对象。
最后来简单回顾一下 @ConfigurationProperties 注解实现配置文件中属性值和配置类属性映射的过程:
1、首先将
@ConfigurationProperties标注在Properties配置类中,参数是约定好的属性前缀。
2、然后通过@EnableConfigurationProperties来触发整个流程,参数是Properties配置类。
3、在@EnableConfigurationProperties中通过@import导入了EnableConfigurationPropertiesImportSelector类,该类中又加载了两个类,一个用来注册Properties配置类,另一个用来绑定配置属性。
4、最后,是通过反射的方式进行属性绑定,且属性值来源于Environment。
3.1.3 ConfigurationPropertiesAutoConfiguration
其实,当我们使用 @ConfigurationProperties 时,无需标注 @EnableConfigurationProperties 注解,因为 Spring Boot 在自动装配的过程中会帮我们加载一个名为 ConfigurationPropertiesAutoConfiguration 的类,该类是在 spring.factories 中定义好的:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
具体的自动装配过程在 《Spring Boot 自动装配(二)》 这篇文章中讨论过,这里不再赘述。我们来看看 ConfigurationPropertiesAutoConfiguration 实现:
@Configuration
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {
}
很简单,直接通过标注 @EnableConfigurationProperties 注解来开启自动配置的流程。那这样怎么注册 Properties 配置类呢?因为上面说过,Properties 配置类是通过该注解的参数传递进来的。其实,只需在配置类上标注 @Component 注解就行了,之后会被 Spring 扫描到,然后注册。
4、总结
最后,来对 Spring Boot 外部化配置做一个整体的总结:
1、首先,外部化配置是
Spring Boot的一个特性,主要是通过外部的配置资源实现与代码的相互配合,来避免硬编码,提供应用数据或行为变化的灵活性。
2、然后介绍了几种外部化配置的资源类型,如properties和YAML配置文件类型,并介绍了获取外部化配置资源的几种方式。
3、其次,介绍了Environment类的加载流程,以及所有外部化配置加载到Environment中的底层是实现。Environment是Spring Boot外部化配置的核心类,该类存储了所有的外部化配置资源,且其它获取外部化配置资源的方式也都依赖于该类。
4、最后,介绍了Spring Boot框架中核心的@ConfigurationProperties注解,该注解是将application配置文件中的属性值和Properties配置类中的属性进行映射,来达到自动配置的目的,并带大家探讨了这一过程的底层实现。
以上就是本章的内容,如过文章中有错误或者需要补充的请及时提出,本人感激不尽。
Spring Boot 外部化配置(二) - @ConfigurationProperties 、@EnableConfigurationProperties的更多相关文章
- Spring Boot 外部化配置(一)- Environment、ConfigFileApplicationListener
目录 前言 1.起源 2.外部化配置的资源类型 3.外部化配置的核心 3.1 Environment 3.1.1.ConfigFileApplicationListener 3.1.2.关联 Spri ...
- Spring Boot外部化配置实战解析
一.流程分析 1.1 入口程序 在 SpringApplication#run(String... args) 方法中,外部化配置关键流程分为以下四步 public ConfigurableAppli ...
- Spring配置文件外部化配置及.properties的通用方法
摘要:本文深入探讨了配置化文件(即.properties)的普遍应用方式.包括了Spring.一般的.远程的三种使用方案. 关键词:.properties, Spring, Disconf, Java ...
- 曹工谈Spring Boot:Spring boot中怎么进行外部化配置,一不留神摔一跤;一路debug,原来是我太年轻了
spring boot中怎么进行外部化配置,一不留神摔一跤:一路debug,原来是我太年轻了 背景 我们公司这边,目前都是spring boot项目,没有引入spring cloud config,也 ...
- Dubbo 新编程模型之外部化配置
外部化配置(External Configuration) 在Dubbo 注解驱动例子中,无论是服务提供方,还是服务消费方,均需要转配相关配置Bean: @Bean public Applicatio ...
- 玩转Spring Boot 自定义配置、导入XML配置与外部化配置
玩转Spring Boot 自定义配置.导入XML配置与外部化配置 在这里我会全面介绍在Spring Boot里面如何自定义配置,更改Spring Boot默认的配置,以及介绍各配置的优先 ...
- SpringBoot官方文档学习(二)Externalized Configuration(外部化配置)
Spring Boot允许您将配置外部化,以便可以在不同的环境中使用相同的应用程序代码.您可以使用属性文件.YAML文件.环境变量和命令行参数来具体化配置.属性值可以通过使用@Value注释直接注入b ...
- 初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置
在上篇博客初识Spring Boot框架中我们初步见识了SpringBoot的方便之处,很多小伙伴可能也会好奇这个Spring Boot是怎么实现自动配置的,那么今天我就带小伙伴我们自己来实现一个简单 ...
- Spring Boot -- 外部配置的属性使用
Spring Boot允许使用propertities文件.yaml文件或者命令行参数作为外部配置. 命令行参数配置 Spring Boot可以基于jar包运行,打成jar包的程序可以直接通过下面的命 ...
随机推荐
- GitHub远程库的搭建以及使用
GitHub远程库的搭建 一).配置SSH 步骤: 1).注册GitHub账号 2).本地git仓库与远程的GitHub仓库的传输要通过SSH进行加密 3).创建SSH key 1.检查在用户主目 ...
- 迁移桌面程序到MS Store(12)——WPF使用UWP InkToolbar和InkCanvas
我们在<迁移桌面程序到MS Store(4)——桌面程序调用Win10 API>提到了对Win10 API的调用,但仍存在无法在WPF中使用UWP控件的问题,虽然都是XAML控件,但却是两 ...
- [FPGA]浅谈LCD1602字符型液晶显示器(Verilog)
目录 概述 LCD1602 LCD1602是什么? LCD1602的管脚 RS_数据/命令选择 E_使能 D0-D7 LCD1602有个DDRAM LCD1602还有个CGROM 指令集 清屏 进入模 ...
- 作业要求20191031-7 beta week 1/2 Scrum立会报告+燃尽图 05
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/9915 一.小组情况 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 韩 ...
- 一图读懂Spring Core,Spring MVC, Spring Boot,Spring Cloud 的关系与区别
Spring框架自诞生到现在,历经多次革新,形成了多种不同的产品,分别应用于不同的项目中,为了帮助自己理解这些产品之间的关系,特此整理此图,以便自己记忆和复习.
- 甲小蛙战记:PHP2Java 排雷指南
(马蜂窝技术原创内容,申请转载请在公众后后台留言,ID:mfwtech ) 大家好,我是来自马蜂窝电商旅游平台的甲小蛙,从前是一名 PHP 工程师,现在可能是一名 PHJ 工程师,以后...... 前 ...
- Ansible Playbooks 介绍 和 使用 二
目录 handlers playbook 案例 2 handlers vars 变量 setup facts 变量使用 案例 inventory 中定义变量 案例 条件测试 when 语句 案例 迭代 ...
- Windows系统中下载Earthdata数据
总的来说,为四大步: 1.注册Earthdata用户. 注册时需注意的是,最好把所有需打勾的都勾上,在最后[注册]按钮前,弹出[人机验证]才能注册成功.如果注册不成功,除了检查用户名和密码是否符合要求 ...
- eclipse导出jar(java打包导出jar)
有时候需要将j2se工程导出,这样可以在别处运作,就不必拘泥于开发感觉中才能运行了.具体做法如下:方法一:(工程没有引用外部jar包时,直接导出) 选中工程---->右键,Export...-- ...
- Maven项目多环境之间的配置文件的切换
前言:对于一个项目,开发和生产环境之间会使用不同的配置文件,最简单的例子就是数据库连接池的配置了.当然,可以在打包上线前对配置文件进行替换,不过这也太low了吧. 简单的pom.xml中的配置内容 比 ...