SSH之Spring传统方式接入YAML管理各环境配置方式(从Spring Boot移植)
2016-12-17 15:36
393 查看
1. 背景
我们的项目早期是采用Spring Roo进行构建的,但是当业务越来越复杂,后端人员越来越多的时候分工特别不方便;并且大家都不太习惯使用Spring Roo CLI进行项目开发,所以经过了一波去Roo化后回归到了SSH架构;后来由于项目的环境配置管理特别不方便,我们发现Spring Boot对配置文件的管理特别符合我们的需求以及我们后期要把项目进行微服务化发现Spring Cloud同样也是一个不错的选择,但项目业务体系比较庞大需要一步一步去做,要把项目进行调整过渡慢慢的适应于Spring Boot那一套进行开发;所以需要移植Spring Boot已经存在的和我们迫切需要的一些优秀特征。2. 说明
把配置文件从Properties换成YAML需要导入第三方YAML的核心库,Spring仅仅只是做为一个管理去使用它,但真正的YAML解析还是需要靠这个第三方库;具体Spring是如何使用YAML来进行管理配置文件的可以参考Spring第三方配置文档。3. 环境版本
Spring 4.2.6.RELEASE、Snakeyaml 1.174. SnakeYaml的MAVEN片段
<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.17</version> </dependency>
5. 从Spring Boot移植的依赖类(稍修过)
这里所涉及到四个类文件,在自己项目里面新建包名路径为“org.springframework.env”(最好直接使用我这个名称,不需要额外导包之类的),然后把下面四个类拷贝至新建的包中就好了。5.1 PropertySourceLoader
/* * Copyright 2012-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.env; import java.io.IOException; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.SpringFactoriesLoader; /** * Strategy interface located via {@link SpringFactoriesLoader} and used to load a * {@link PropertySource}. * * @author Dave Syer * @author Phillip Webb */ public interface PropertySourceLoader { /** * Returns the file extensions that the loader supports (excluding the '.'). * @return the file extensions */ String[] getFileExtensions(); /** * Load the resource into a property source. * @param name the name of the property source * @param resource the resource to load * @param profile the name of the profile to load or {@code null}. The profile can be * used to load multi-document files (such as YAML). Simple property formats should * {@code null} when asked to load a profile. * @return a property source or {@code null} * @throws IOException if the source cannot be loaded */ PropertySource<?> load(String name, Resource resource, String profile) throws IOException; }
5.2 ArrayDocumentMatcher
/* * Copyright 2012-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.env; import java.util.Collections; import java.util.Properties; import java.util.Set; import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; import org.springframework.util.StringUtils; /** * Matches a document containing a given key and where the value of that key is an array * containing one of the given values, or where one of the values matches one of the given * values (interpreted as regexes). * * @author Dave Syer */ public class ArrayDocumentMatcher implements DocumentMatcher { private final String key; private final String[] patterns; public ArrayDocumentMatcher(final String key, final String... patterns) { this.key = key; this.patterns = patterns; } @Override public MatchStatus matches(Properties properties) { if (!properties.containsKey(this.key)) { return MatchStatus.ABSTAIN; } Set<String> values = StringUtils .commaDelimitedListToSet(properties.getProperty(this.key)); if (values.isEmpty()) { values = Collections.singleton(""); } for (String pattern : this.patterns) { for (String value : values) { if (value.matches(pattern)) { return MatchStatus.FOUND; } } } return MatchStatus.NOT_FOUND; } }
5.3 SpringProfileDocumentMatcher
/* * Copyright 2012-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.env; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Properties; import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; import org.springframework.core.env.Environment; /** * {@link DocumentMatcher} backed by {@link Environment#getActiveProfiles()}. A YAML * document matches if it contains an element "spring.profiles" (a comma-separated list) * and one of the profiles is in the active list. * * @author Dave Syer */ public class SpringProfileDocumentMatcher implements DocumentMatcher { private static final String[] DEFAULT_PROFILES = new String[] { "^\\s*$" }; private String[] activeProfiles = new String[0]; public SpringProfileDocumentMatcher() { } public SpringProfileDocumentMatcher(String... profiles) { addActiveProfiles(profiles); } public void addActiveProfiles(String... profiles) { LinkedHashSet<String> set = new LinkedHashSet<String>( Arrays.asList(this.activeProfiles)); Collections.addAll(set, profiles); this.activeProfiles = set.toArray(new String[set.size()]); } @Override public MatchStatus matches(Properties properties) { String[] profiles = this.activeProfiles; if (profiles.length == 0) { profiles = DEFAULT_PROFILES; } return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties); } }
5.4 SpringYamlPropertiesFactoryBean
package org.springframework.env; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.YamlProcessor; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.util.ClassUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; import java.util.Map; import java.util.Properties; import java.util.regex.Pattern; /** * Spring Yaml配置信息处理类 * <p><b>参考SpringBoot内容进行移植</b></p> * <p>http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html</p> * <p>具体查看: 24.6.2 Exposing YAML as properties in the Spring Environment</p> * @author cary * @date 2016/11/2 */ public class SpringYamlPropertiesFactoryBean implements FactoryBean<Properties>, InitializingBean, EnvironmentAware { private boolean singleton = true; private Properties properties; private Resource resource; private Environment environment; @Override public Properties getObject() throws Exception { return this.properties; } @Override public Class<?> getObjectType() { return Properties.class; } @Override public boolean isSingleton() { return this.singleton; } @Override public void afterPropertiesSet() throws Exception { if (isSingleton()) { if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { this.properties = new Properties(); loadDefault(); for (int i = 0; i < environment.getActiveProfiles().length; i++) { String profile = environment.getActiveProfiles()[i]; Processor processor = new Processor(resource, profile); this.properties.putAll(processor.process()); } //让VM参数可以覆盖YAML this.properties.putAll(System.getProperties()); System.getProperties().putAll(this.properties); } } } protected void loadDefault(){ Processor processor = new Processor(resource, null); this.properties.putAll(processor.process()); } public void setResource(Resource resource){ this.resource = resource; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } /** * {@link YamlProcessor} to create a {@link Map} containing the property values. * Similar to {@link YamlPropertiesFactoryBean} but retains the order of entries. */ private static class Processor extends YamlProcessor { Processor(Resource resource, String profile) { if (profile == null) { setMatchDefault(true); setDocumentMatchers(new SpringProfileDocumentMatcher()); } else { setMatchDefault(false); setDocumentMatchers(new SpringProfileDocumentMatcher(profile)); } setResources(new Resource[] { resource }); } @Override protected Yaml createYaml() { return new Yaml(new StrictMapAppenderConstructor(), new Representer(), new DumperOptions(), new Resolver() { @Override public void addImplicitResolver(Tag tag, Pattern regexp, String first) { if (tag == Tag.TIMESTAMP) { return; } super.addImplicitResolver(tag, regexp, first); } }); } // public Map<String, Object> process() { // final Map<String, Object> result = new LinkedHashMap<String, Object>(); // process(new MatchCallback() { // @Override // public void process(Properties properties, Map<String, Object> map) { // result.putAll(getFlattenedMap(map)); // } // }); // return result; // } public Properties process() { final Properties result = new Properties(); process(new MatchCallback() { @Override public void process(Properties properties, Map<String, Object> map) { result.putAll(properties); } }); return result; } } }
6. 配置applicationContext.xml
<!-- 如果需要针对不同环境设置,VM参数设置参考: -Dspring.profiles.active=production --> <bean id="yamlPropertiesFactoryBean" class="org.springframework.env.SpringYamlPropertiesFactoryBean" p:resource="classpath:META-INF/spring/application.yml"/> <context:property-placeholder properties-ref="yamlPropertiesFactoryBean" />
7. 简易各环境配置示例
新建一个文件“application.yml”存放到项目的“resources/META-INF/spring”中(目录位置其实可以随意,只要在第6点中把位置配置对就好了),加入以下内容到文件中,启动的时候设置JVM参数“-Dspring.profiles.active=development”即可看到效果。application: name: '默认名称' --- spring: profiles: development application: name: '测试名称' --- spring: profiles: production application: name: '生产名称' ---
8. 最后注意
在项目中具体怎么去使用Spring配置属性的相关的代码我就不贴出来了(和原始的使用方式一样);如果项目启动JVM中没有设置“-Dspring.profiles.active”变量参数的就会使用默认的信息。相关文章推荐
- spring的事务管理(配置文件方式和注解方式实现、转账环境搭建)
- SSH与SSM学习之Spring23——Spring事务之注解配置方式管理事务
- SSH与SSM学习之Spring22——Spring事务之xml配置方式管理事务
- SSH整合时,基于注解的事务管理的配置方式
- 在eclipse下手动配置ssh的环境,Spring,Struts2,Hibernate
- 【SSH三大框架】Spring基础第一篇:搭建Spring环境、实例化Bean、管理Bean的作用域以及Bean的生命周期
- Spring管理 hibernate 事务配置的五种方式
- 使用配置方式进行ssh的整合以及管理员管理的案例(二)
- spring事物管理五种配置方式
- JAVA_WEB项目之使用Spring的xml配置方式在项目中管理Lucene检索框架
- 用SpringAOP方式配置数据库事务管理
- 详细配置说明||Spring 使用注解方式进行事务管理
- Spring管理 hibernate 事务配置的五种方式
- SSH深度历险(六) 深入浅出----- Spring事务配置的五种方式
- spring与hibernate整合配置基于Annotation注解方式管理实务
- spring 声明式事务管理的配置方式
- 8.4.6: Spring的AOP---基于XML配置文件的管理方式
- Spring管理 hibernate 事务配置的五种方式
- Spring 管理hibernate事物,xml配置,注解 两种实现方式
- 使用配置方式进行ssh的整合以及管理员管理的案例