文章目录

  • 1、Enviroment
  • 2、@Profile
  • 3、PropertySource
  • 4、@PropertySource
  • 5、@Configuration
  • 6、Resource
  • Resource 主要实现类:
  • EncodedResource
  • 资源文件加载



软件系统在生命周期的不同阶段(比如

开发调试

测试

生产等),应该有着不同的行为模式,软件系统从其运行环境中获取信息,来识别其所处的阶段,然后呈现不同的行为逻辑。

配置 很重要,不同的阶段,利益攸关者不同,配置 的需求也不一样。

1、Enviroment

所谓的环境,其本质上是一组信息,对于软件系统来说,这类信息由外到内单向传递,用来影响或改变软件的行为模式。

环境 相关的最重要、最核心的两个概念是:信息源使用者,前者表明信息是从何而来,后者表明信息是为谁所用。在软件系统中,使用者 必然是程序,对于 Spring 应用来说,就是 spring bean。无论是自定义的软件,还是基于框架(比如 spring)的软件,在设计 环境信息 相关的模块(后面称其为 配置模块 )时,都必然要围绕着这两个核心概念展开。

配置模块 的核心工作是解析和适配,获取信息源的信息,将其解析为 配置对象,并建立与 功能对象 之间的关系。

virualenv environment system environment 区别 environment和atmosphere的区别_xml


Spring 应用的运行环境由 Environment 对象来表示,在设计上,环境 分为两个层面:profilesproperties

profile 是 spring bean 的逻辑分组,当 bean 所在的 profile 状态为 active 时才会被注册到容器(ioc container)中,可以选择使用 xml 或者 注解 的方式将 spring beans 分配到不同的 profile 中。

在 Spring boot 应用中,可以通过 spring.profiles.active=test,game,sunday 属性来配置激活的 profile,没有被 @Profile 注解的 bean,属于 default profile

2、@Profile

@Profile 注解可以应用在下列场合:

  1. 应用在 @Component@Configuration 注释的 Java Class 上;
  2. 作为 meta-annotation ,构建新的 Annotation
  3. 应用在 @Bean 注解的方法上;

如果在 @Configuration 类上使用 @Profile,那么将适用于其所有的 @Bean 方法和 @Import 注解,除非单独为它们添加特定的 @Profile

profile 的名称可以为字符串或者是 profile 表达式,例如 p1 & p2@Profile({"p1", "p2"}) 表示,当 p1p2 至少有一个处于激活状态时,其标识的 bean 才会被注册到环境中。

xml 文件中,通过 <beans profile="p1,p2"> 的方式来定义 profile。

3、PropertySource

接口 ConfigurableEnvironment 提供了设置 profile 和操作 property sources 的方法,允许 配置方 来配置环境信息。

应用环境可以关联多个 PropertySource,这些 PropertySource 可以增删改:

1、添加新的 PropertySource

ConfigurableEnvironment environment = new StandardEnvironment();
 MutablePropertySources propertySources = environment.getPropertySources();
 Map<String, String> myMap = new HashMap<>();
 myMap.put("xyz", "myValue");
 propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));

2、删除默认的系统属性的 PropertySource

MutablePropertySources propertySources = environment.getPropertySources();
 propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)

3、测试阶段,模拟系统环境:

MutablePropertySources propertySources = environment.getPropertySources();
 MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
 propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);

对于 Spring 应用来说,要保证 ApplicationContext 的 refresh 方法调用前完成所有 PropertySource 相关的操作。

virualenv environment system environment 区别 environment和atmosphere的区别_Environment_02


Spring 提供了多种类型的 PropertySource 实现,可以按需选择。

4、@PropertySource

@PropertySource 注解提供了一种 声明式的 添加 PropertySource 的机制,例如:

@Configuration
 @PropertySource("classpath:/com/myco/app.properties")
 public class AppConfig {
     @Autowired
     Environment env;

     @Bean
     public TestBean testBean() {
         TestBean testBean = new TestBean();
         testBean.setName(env.getProperty("testbean.name"));
         return testBean;
     }
 }

可以通过 @PropertySource 把配置文件中的属性设置到 Environment 对象中。

@PropertySource 路径中的 ${...} 占位符信息,比如 @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties"),会从已经配置的属性信息中自动解析,如果 my.placeholder 在系统属性(System properties),或者环境变量(environment variables)中定义了,那么相应的占位符会被正确解析,如果没有定义,则使用 : 后面的默认值,这里是 default/path

当一个属性在多个 PropertySource 中定义时,最后一个被解析的 PropertySource 中的值有效。

例如有两份配置文件 application.properties 分别在用户目录和 classpath 中:

@Configuration
 @PropertySource("classpath:/conf/application.properties")
 public class ConfigA { }

 @Configuration
 @PropertySource("file:${user.dir}/application.properties")
 public class ConfigB { }

那么,到底哪个文件中的参数生效,取决于两个配置类的加载顺序。

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 ctx.register(ConfigA.class);
 ctx.register(ConfigB.class);
 ctx.refresh();

上例中,ConfigB 中的配置生效。

5、@Configuration

在 Spring 应用中,使用 @Configuration 来建立 配置信息bean 之间的关系,两者分别对应了 环境业务逻辑

1、被 @Configuration 注解的类,可以通过 AnnotationConfigApplicationContext 类中的 register 方法来注册:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 ctx.register(AppConfig.class);
 ctx.refresh();
 MyBean myBean = ctx.getBean(MyBean.class);

2、也可以通过 xml 文件来配置:

<beans>
    <context:annotation-config/>
    <bean class="com.acme.AppConfig"/>
 </beans>

可以 @Configuration + @ImportResource(locations={"classpath:beans.xml"}) 的形式来导入 xml 中配置的 bean

3、应用 Environment 中的信息:

@Configuration
 public class AppConfig {
     @Autowired Environment env;
     @Bean
     public MyBean myBean() {
         MyBean myBean = new MyBean();
         myBean.setName(env.getProperty("bean.name"));
         return myBean;
     }
 }

4、配置属性源:PropertySource

@Configuration
 @PropertySource("classpath:/com/acme/app.properties")
 public class AppConfig {
     @Inject Environment env;

     @Bean
     public MyBean myBean() {
         return new MyBean(env.getProperty("bean.name"));
     }
 }

5、注入属性:

@Configuration
 @PropertySource("classpath:/com/acme/app.properties")
 public class AppConfig {
     @Value("${bean.name}") String beanName;

     @Bean
     public MyBean myBean() {
         return new MyBean(beanName);
     }
 }

6、使用 @Impoert 引用其他配置类:

@Configuration
 public class DatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return DataSource
     }
 }

 @Configuration
 @Import(DatabaseConfig.class)
 public class AppConfig {

     private final DatabaseConfig dataConfig;

     public AppConfig(DatabaseConfig dataConfig) {
         this.dataConfig = dataConfig;
     }

     @Bean
     public MyBean myBean() {
         // reference the dataSource() bean method
         return new MyBean(dataConfig.dataSource());
     }
 }

在调用 new AnnotationConfigApplicationContext(AppConfig.class) 初始化 AppConfig.class 时, DatabaseConfig.class 也会被初始化到 Context 中。

7、与 @Profile 注解结合使用:

@Profile("development")
 @Configuration
 public class EmbeddedDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return embedded DataSource
     }
 }

 @Profile("production")
 @Configuration
 public class ProductionDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return production DataSource
     }
 }

8、使用 @ImportResource 导入资源:

@Configuration
 @ImportResource("classpath:/com/acme/database-config.xml")
 public class AppConfig {

     @Inject DataSource dataSource; // from XML

     @Bean
     public MyBean myBean() {
         // inject the XML-defined dataSource bean
         return new MyBean(this.dataSource);
     }
 }

9、嵌嵌套的 @Configuration 类:

@Configuration
 public class AppConfig {

     @Inject DataSource dataSource;

     @Bean
     public MyBean myBean() {
         return new MyBean(dataSource);
     }

     @Configuration
     static class DatabaseConfig {
         @Bean
         DataSource dataSource() {
             return new EmbeddedDatabaseBuilder().build();
         }
     }
 }

10、懒加载模式: @Lazy

默认情况下,@Bean 方法会在容器初始化阶段立即实例化,@Lazy 注解标识的 @Bean,可以延迟实例化。

6、Resource

JDK 提供的访问资源的类(如 java.net.URL, File 等),并不能很好地满足各种底层资源的访问需求,比如缺少从 类路径 或者 WEB 容器上下文 中获取资源的操作类。

Spring 设计了一个 Resource 接口,它为应用提供了更强大的访问底层资源的能力。

public interface Resource extends InputStreamSource {
    boolean exists();
    boolean isOpen();
    URL getURL() throws IOException;
    File getFile() throws IOException;
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();
    String getDescription();
}

Resource 主要实现类:

  • ClassPathResource:类路径下的资源,资源以相对于类路径的方式表示;
  • FileSystemResource:文件系统资源,资源以文件系统路径的方式表示,如 D:/conf/bean.xml
  • ServletContextResource:为访问 Web 容器上下文中的资源而设计的类,负责以相对于 Web 应用根目录的路径加载资源,支持以流和 URL 的方式访问,在 War 解包的情况下,也可以通过 File 的方式访问,该类还可以直接从 Jar 包中访问资源;
  • URLResource:Url 封装了 java.net.URL,它使用户能够访问任何可以通过 URL 表示的资源,如文件系统的资源、HTTP 资源、FTP 资源等;

Spring 可以将配置信息放置在任何地方(如数据库、LDAP 中),只要可以通过 Resource 接口返回配置信息就可以了。

virualenv environment system environment 区别 environment和atmosphere的区别_Configuration_03

String filePath = "D:/home/spring-demo/WebRoot/WEB-INF/classes/info.txt"

Resource fileResource = new FileSystemResource(filePath);
Resource classPathResource = new ClassPathResource("info.txt");// 通过 System.getProperty("java.class.path") 可获取 class path 信息;
Resource servletContextResource = new ServletContextResource(servletContext, "/WEB-INF/classes/info.txt")

可以通过 createRelative(String relativePath) 在对应 Resource 资源的相对路径上创建文件。

对于远程服务器(Web 服务器或 FTP 服务器)的文件资源,用户可以 方便地通过 UrlResource 进行访问。

EncodedResource

资源加载时默认采用系统编码读取资源内容,如果资源文件采用特殊的编码格式,那么可以通过 EncodedResource 对资源进行编码,以保证资源内容操作的正确性。

Resource resource = new ClassPathResource("info.txt");
EncodedResource encodeResource = new EncodedResource(resource, "UTF-8");

资源文件加载

Spring 提供了一个强大加载资源的机制,可以在不显示使用 Resource 实现类的情况下,仅通过资源地址的特殊标识(资源地址表达式)就可以加载相应的资源。

地址前缀

示例

资源类型

classpath

classpath: info.txt

从类路径中加载资源,资源文件可以在标准的文件系统中,也可以在 jar 或 zip 等包中

file

file:D:/home/spring-demo/WebRoot/WEB-INF/classes/info.txt

使用 UrlResource 从文件目录中装载资源,可以采用相对路径,也可以采用绝对路径

http://

http://www.abc.com/resource/info.txt

从 web 服务器中加载资源

ftp://

ftp://www.abc.com/resource/info.txt

从 web 服务器中加载资源

classpath*:conf/*.xml :会扫描 classpath 下所有的 jar 中出现的指定路径的文件;而 classpath: 只会在第一个加载的包的路径下查找。

Ant 风格资源地址支持 3 中匹配符:

  • classpath:conf/t?st.xml,匹配文件名中的一个字符;
  • file:D:/conf/*.xml* 匹配文件名中任意个字符;
  • classpath:com/**/test.xml** 匹配多层路径;

ResourceLoader 可以根据资源地址的名称来加载资源,不过,资源地址仅支持带资源类型前缀的表达式,不支持 Ant 风格的资源路径表达式。

public interface ResourceLoader {
    Resource getResource(String location);
}

ApplicationContext 实现了 ResourceLoader 接口,因此可以通过 ApplicationContext 对象来获取资源:

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");

当路径信息中没有资源前缀时,会根据 ApplicationContext 的实现(比如 ClassPathXmlApplicationContext,FileSystemXmlApplicationContext)来推断。

任何实现了 ResourceLoaderAware 接口的 Bean,都会被注入一个 ResourceLoader 实例,这个实例就是当前的 ApplicationContext 实例。当然也可以通过注解的形式来注入一个 ResourceLoader 属性。

ResourcePatternResolver 扩展了 ResourceLoader 接口,定义了新的接口方法: getResources ,该方法支持带资源路径前缀及 Ant 风格的资源路径表达式。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resources[] = resolver.getResources("classpath*:conf/*.xml");