本文共 17343 字,大约阅读时间需要 57 分钟。
前面我们描述了spring profile和maven profile的异同
通常意义上我们说的配置一般都是properties文件,但是spring支持yml【结构程度更好】
原先我们在使用Spring时更多的是使用value注解进行注入,但是对于比较多的属性的情况下
也只能一味的进行复制拷贝
Spring支持了EnableConfigurationProperties
/** * Enable support for {@link ConfigurationProperties} annotated beans. * {@link ConfigurationProperties} beans can be registered in the standard way (for * example using {@link Bean @Bean} methods) or, for convenience, can be specified * directly on this annotation. * * @author Dave Syer */@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 {}; }
对于EnableConfigurationProperties import EnableConfigurationPropertiesImportSelector
该selector做了如下操作
class EnableConfigurationPropertiesImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata metadata) { MultiValueMapattributes = metadata.getAllAnnotationAttributes( EnableConfigurationProperties.class.getName(), false); Object[] type = attributes == null ? null : (Object[]) attributes.getFirst("value"); if (type == null || type.length == 0) { return new String[] { ConfigurationPropertiesBindingPostProcessorRegistrar.class .getName() }; } return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() }; } /** * {@link ImportBeanDefinitionRegistrar} for configuration properties support. */ public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { MultiValueMap attributes = metadata .getAllAnnotationAttributes( EnableConfigurationProperties.class.getName(), false); List > types = collectClasses(attributes.get("value")); for (Class type : types) { String prefix = extractPrefix(type); String name = (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName()); if (!registry.containsBeanDefinition(name)) { registerBeanDefinition(registry, type, name); } } } private String extractPrefix(Class type) { ConfigurationProperties annotation = AnnotationUtils.findAnnotation(type, ConfigurationProperties.class); if (annotation != null) { return annotation.prefix(); } return ""; } private List > collectClasses(List
可以看到prefix会被设置到targetname中 而targetname将会被包装秤Releaxedname
private IterablegetRelaxedTargetNames() { return (this.target != null && StringUtils.hasLength(this.targetName) ? new RelaxedNames(this.targetName) : null);}
而relaxedname如下
/** * Create a new {@link RelaxedNames} instance. * @param name the source name. For the maximum number of variations specify the name * using dashed notation (e.g. {@literal my-property-name} */public RelaxedNames(String name) { this.name = (name == null ? "" : name); initialize(RelaxedNames.this.name, this.values);} @Overridepublic Iteratoriterator() { return this.values.iterator();} private void initialize(String name, Set values) { if (values.contains(name)) { return; } for (Variation variation : Variation.values()) { for (Manipulation manipulation : Manipulation.values()) { String result = name; result = manipulation.apply(result); result = variation.apply(result); values.add(result); initialize(result, values); } }}
将会包装成多个【大小写 驼峰等等】望文生义
总之通过各种异形的名称来在属性文件中获取到设置的值
下面和以前Spring3一样通过setPropertyValue来设置对应的值。
其中有一个random特别引起了开发者的欢迎。
比如可以如下定义
my: secret: password: ${random.value} intValue: ${random.int} intValueRange: ${random.int[1,99]} longValue: ${random.long} longValueRange: ${random.long[111111111111,999999999999]} uuid: ${random.uuid}
来查看
/** * {@link PropertySource} that returns a random value for any property that starts with * {@literal "random."}. Where the "unqualified property name" is the portion of the * requested property name beyond the "random." prefix, this {@link PropertySource} * returns: *
* {@code OPEN value (,max) CLOSE} where the {@code OPEN,CLOSE} are any character and * {@code value,max} are integers. If {@code max} is provided then {@code value} is the * minimum value and {@code max} is the maximum (exclusive). * * @author Dave Syer * @author Matt Benson */public class RandomValuePropertySource extends PropertySource
非常有意思
至于实现profile的关键
spring 解析对应properties通过 PropertySourcesPropertyResolver
protectedT getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource propertySource : this.propertySources) { if (logger.isTraceEnabled()) { logger.trace("Searching for key '" + key + "' in PropertySource '" + propertySource.getName() + "'"); } Object value = propertySource.getProperty(key); if (value != null) { if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } logKeyFound(key, propertySource, value); return convertValueIfNecessary(value, targetValueType); } } } if (logger.isDebugEnabled()) { logger.debug("Could not find key '" + key + "' in any property source"); } return null;}
可以看出propertySources的顺序非常影响对应注入的参数【当查找到第一个符合条件的结果后就返回了】
那么我们最经常用的在Jvm参数中 -Dspring.active.profile=dev
关于JVM参数中-D
-D<name>=<value> set a system property 设置系统属性。
这样相当于在environment中设置了spring.active.profile
因此部分小伙伴碰到在properties定义属性为
user.name发现无法生效【这是因为在系统中已经存在比properties优先级更高的系统环境变量】
如何确认propertySource的优先级顺序呢?
SpringBoot在启动时或默认配置如下
/** * Add, remove or re-order any {@link PropertySource}s in this application's * environment. * @param environment this application's environment * @param args arguments passed to the {@code run} method * @see #configureEnvironment(ConfigurableEnvironment, String[]) */protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } }}
可以看到我们可以设置默认defaultProperties 如果存在将会设置到最后
但是对于java调用参数【如果存在】来说默认会将其放入第一位
对于标准环境来说Spring默认也会调用如下方法
/** * Create a new {@code Environment} instance, calling back to * {@link #customizePropertySources(MutablePropertySources)} during construction to * allow subclasses to contribute or manipulate {@link PropertySource} instances as * appropriate. * @see #customizePropertySources(MutablePropertySources) */public AbstractEnvironment() { customizePropertySources(this.propertySources); if (logger.isDebugEnabled()) { logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources); }}/** * Customize the set of property sources with those appropriate for any standard * Java environment: *
Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}. * @see AbstractEnvironment#customizePropertySources(MutablePropertySources) * @see #getSystemProperties() * @see #getSystemEnvironment() */@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
因此将会放入对应的propertySource【在做Environment的构造函数时】因此在创建完成时
SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
分别出现在properties前两位【直到放入commandArgument】
对于servlet容器
/** * Customize the set of property sources with those contributed by superclasses as * well as those appropriate for standard servlet-based environments: *
Properties present in {@value #SERVLET_CONFIG_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #SERVLET_CONTEXT_PROPERTY_SOURCE_NAME}, and * properties found in either of the above take precedence over those found in * {@value #JNDI_PROPERTY_SOURCE_NAME}. *
Properties in any of the above will take precedence over system properties and * environment variables contributed by the {@link StandardEnvironment} superclass. *
The {@code Servlet}-related property sources are added as * {@link StubPropertySource stubs} at this stage, and will be * {@linkplain #initPropertySources(ServletContext, ServletConfig) fully initialized} * once the actual {@link ServletContext} object becomes available. * @see StandardEnvironment#customizePropertySources * @see org.springframework.core.env.AbstractEnvironment#customizePropertySources * @see ServletConfigPropertySource * @see ServletContextPropertySource * @see org.springframework.jndi.JndiPropertySource * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources * @see #initPropertySources(ServletContext, ServletConfig) */@Overrideprotected 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);}
SERVLET_CONTEXT_PROPERTY_SOURCE_NAME 也会出现在properties的前列【即web.xml中配置选项】
因此可以得出结论
commandLineArgs>servletContextInitParams>servletContextInitParams>jndiProperties>systemProperties>systemEnvironment>properties>defaultProperties
至于小伙伴认为为啥会defaultProperties排在最后
private void addConfigurationProperties( ConfigurationPropertySources configurationSources) { MutablePropertySources existingSources = this.environment .getPropertySources(); if (existingSources.contains(DEFAULT_PROPERTIES)) { existingSources.addBefore(DEFAULT_PROPERTIES, configurationSources); } else { existingSources.addLast(configurationSources); }}
转载地址:http://xybnb.baihongyu.com/