# 3.5.1　注入外部的值

在 Spring 中，处理外部值的最简单方式就是声明属性源并通过 Spring 的 Environment 来检索属性。例如，以下程序展现了一个基本的 Spring 配置类，它使用外部的属性来装配 BlankDisc bean。

```java
package com.soundsystem;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")
public class ExpressiveConfig {
  
  @Autowired
  Environment env;
  
  @Bean
  public BlankDisc disc() {
    return new BlankDisc(
      env.getProperty("disc.title"),
      env.getProperty("disc.artist")
    );
  }
}
```

在本例中，@PropertySource 引用了类路径中一个名为 app.properties 的文件。它大致会如下所示：

```
disc.title=Sgt. Peppers Lonely Hearts Club
disc.artisc=The Beatles
```

这个属性文件会加载到 Spring 的 Environment 中，稍后可以从这里检索属性。同时，在 disc() 方法中，会创建一个新的 BlankDisc，它的构造器参数是从属性文件中获取的，而这是通过调用 getProperty() 实现的。

深入学习 Spring 的 Environment 当我们去了解 Environment 的时候会发现，上述程序所示的 getProperty() 方法并不是获取属性值的唯一方 法，getProperty() 方法有四个重载的变种形式：

* `String getProperty(String key)`
* `String getProperty(String key, String defualtValue)`
* `T getProperty(String key, Class<T>  type)`
* `T getProperty(String key, Class<T> type, T defaultValue)`

前两种形式的 getProperty() 方法都会返回 String 类型的值。我们已经在上述程序中看到了如何使用第一种 getProperty()方法。但是，你可以稍微对 @Bean 方法进行一下修改，这样在指定属性不存在的时候，会使用一个默认值：

```java
@Bean
public BlankDisc disc() {
  env.getProperty("disc.title", "Rattle and Hum"),
  env.getProperty("disc.artist", "U2")
}
```

剩下的两种 getProperty() 方法与前面的两种非常类似，但是它们不会将所有的值都视为 String 类型。例如，假设你想要获取的值所代表的含义是连接池中所维持的连接数量。如果我们从属性文件中得到 的是一个 String 类型的值，那么在使用之前还需要将其转换为 Integer 类型。但是，如果使用重载形式的 getProperty() 的话，就能非常便利地解决这个问题：

```java
int connectionCount = env.getProperty("db.connection.count", Integer.class, 30);
```

Environment 还提供了几个与属性相关的方法，如果你在使用 getProperty() 方法的时候没有指定默认值，并且这个属性没有定义的话，获取到的值是 null。如果你希望这个属性必须要定义，那么可以使用 getRequiredProperty() 方法，如下所示：

```java
@Bean
public BlankDisc disc() {
  env.getRequiredProperty("disc.title");
  env.getRequiredProperty("disc.artist");
}
```

在这里，如果 disc.title 或 disc.artist 属性没有定义的话，将会抛出 IllegalStateException 异常。

如果想检查一下某个属性是否存在的话，那么可以调用 Environment 的 containsProperty() 方法：

```java
boolean titleExists = env.containsProperty("disc.title");
```

最后，如果想将属性解析为类的话，可以使用 getPropertyAsClass() 方法：

```java
Class<CompactDisc> cdClass = env.getPropertyAdClass("disc.class", CompactDisc.class);
```

除了属性相关的功能以外，Environment 还提供了一些方法来检查哪些 profile 处于激活状态：

* String\[] getActiveProfiles()：返回激活 profile 名称的数组；
* String\[] getDefaultProfiles()：返回默认 profile 名称的数组；
* boolean acceptsProfiles(String... profiles)：如果 environment 支持给定 profile 的话，就返回true。

直接从 Environment 中检索属性是非常方便的，尤其是在 Java 配置中装配 bean 的时候。但是，Spring 也提供了通过占位符装配属性的方法，这些占位符的值会来源于一个属性源。

**解析属性占位符**

Spring 一直支持将属性定义到外部的属性的文件中，并使用占位符值将其插入到 Spring bean 中。在 Spring 装配中，占位符的形式为使用 `${ ... }` 包装的属性名称。作为样例，我们可以在 XML 中按照如下的 方式解析 BlankDisc 构造器参数：

```markup
<bean id="sgtPeppers" class="soundsystem.BlankDisc"
      c:_title="${disc.titlte}"
      c:_artist="${disc.artist}" />
```

可以看到，title 构造器参数所给定的值是从一个属性中解析得到的，这个属性的名称为 disc.title。artist 参数装配的是名为 disc.artist 的属性值。按照这种方式，XML 配置没有使用任何硬编码的值，它的值是从配置文件以外的一个源中解析得到的。（我们稍后会讨论这些属性是如何解析的。）

如果我们依赖于组件扫描和自动装配来创建和初始化应用组件的话，那么就没有指定占位符的配置文件或类了。在这种情况下，我们可以使用 @Value 注解，它的使用方式与 @Autowired 注解非常相似。比如，在 BlankDisc 类中，构造器可以改成如下所示：

```java
public BlankDisc(
  @Value("${disc.title}") String title,
  @Value("${disc.artist}") String artist) {
    this.title = title;
    this.artist = artist;
}
```

为了使用占位符，我们必须要配置一 个 PropertyPlaceholderConfigurer bean 或 PropertySourcesPlaceholderConfigurer bean。从 Spring 3.1 开始，推荐使用 PropertySourcesPlaceholderConfigurer，因为它能够基于 Spring Environment 及其属性源来解析占位符。

如下的 @Bean 方法在 Java 中配置了 PropertySourcesPlaceholderConfigurer：

```java
@Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
}
```

如果你想使用 XML 配置的话，Spring context 命名空间中的 `<context:propertyplaceholder>` 元素将会为你生成 PropertySourcesPlaceholderConfigurer bean：

{% code title="" %}

```markup
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/beans/spring-context.xsd" >
  
  <context:property-placeholder />
          
</beans>
```

{% endcode %}

解析外部属性能够将值的处理推迟到运行时，但是它的关注点在于根据名称解析来自于 Spring Environment 和属性源的属性。而 Spring 表达式语言提供了一种更通用的方式在运行时计算所要注入的值。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v4/di-3-zhang-gao-ji-zhuang-pei/untitled-1/untitled.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
