Справочное руководство Spring5: среда

Spring Boot Java Spring

Эта статья участвовала в третьем этапе курса «High Yield Update» тренировочного лагеря для создателей Nuggets. Подробнее см.:Dig Li Project | Идет третий этап тренировочного лагеря создателя, «написание» личного влияния.

Интерфейс Spring Environment выполняет две ключевые роли: 1. Профиль, 2. Свойства. Вы можете посмотреть определение этого интерфейса:

public interface Environment extends PropertyResolver {

	/**
	 * Return the set of profiles explicitly made active for this environment. Profiles
	 * are used for creating logical groupings of bean definitions to be registered
	 * conditionally, for example based on deployment environment. Profiles can be
	 * activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 * "spring.profiles.active"} as a system property or by calling
	 * {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
	 * <p>If no profiles have explicitly been specified as active, then any
	 * {@linkplain #getDefaultProfiles() default profiles} will automatically be activated.
	 * @see #getDefaultProfiles
	 * @see ConfigurableEnvironment#setActiveProfiles
	 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 */
	String[] getActiveProfiles();

	/**
	 * Return the set of profiles to be active by default when no active profiles have
	 * been set explicitly.
	 * @see #getActiveProfiles
	 * @see ConfigurableEnvironment#setDefaultProfiles
	 * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
	 */
	String[] getDefaultProfiles();

	/**
	 * Return whether one or more of the given profiles is active or, in the case of no
	 * explicit active profiles, whether one or more of the given profiles is included in
	 * the set of default profiles. If a profile begins with '!' the logic is inverted,
	 * i.e. the method will return {@code true} if the given profile is <em>not</em> active.
	 * For example, {@code env.acceptsProfiles("p1", "!p2")} will return {@code true} if
	 * profile 'p1' is active or 'p2' is not active.
	 * @throws IllegalArgumentException if called with zero arguments
	 * or if any profile is {@code null}, empty, or whitespace only
	 * @see #getActiveProfiles
	 * @see #getDefaultProfiles
	 * @see #acceptsProfiles(Profiles)
	 * @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)}
	 */
	@Deprecated
	boolean acceptsProfiles(String... profiles);

	/**
	 * Return whether the {@linkplain #getActiveProfiles() active profiles}
	 * match the given {@link Profiles} predicate.
	 */
	boolean acceptsProfiles(Profiles profiles);

}

Он наследует PropertyResolver:

public interface PropertyResolver {

	/**
	 * Return whether the given property key is available for resolution,
	 * i.e. if the value for the given key is not {@code null}.
	 */
	boolean containsProperty(String key);

	/**
	 * Return the property value associated with the given key,
	 * or {@code null} if the key cannot be resolved.
	 * @param key the property name to resolve
	 * @see #getProperty(String, String)
	 * @see #getProperty(String, Class)
	 * @see #getRequiredProperty(String)
	 */
	@Nullable
	String getProperty(String key);

	/**
	 * Return the property value associated with the given key, or
	 * {@code defaultValue} if the key cannot be resolved.
	 * @param key the property name to resolve
	 * @param defaultValue the default value to return if no value is found
	 * @see #getRequiredProperty(String)
	 * @see #getProperty(String, Class)
	 */
	String getProperty(String key, String defaultValue);

	/**
	 * Return the property value associated with the given key,
	 * or {@code null} if the key cannot be resolved.
	 * @param key the property name to resolve
	 * @param targetType the expected type of the property value
	 * @see #getRequiredProperty(String, Class)
	 */
	@Nullable
	<T> T getProperty(String key, Class<T> targetType);

	/**
	 * Return the property value associated with the given key,
	 * or {@code defaultValue} if the key cannot be resolved.
	 * @param key the property name to resolve
	 * @param targetType the expected type of the property value
	 * @param defaultValue the default value to return if no value is found
	 * @see #getRequiredProperty(String, Class)
	 */
	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

	/**
	 * Return the property value associated with the given key (never {@code null}).
	 * @throws IllegalStateException if the key cannot be resolved
	 * @see #getRequiredProperty(String, Class)
	 */
	String getRequiredProperty(String key) throws IllegalStateException;

	/**
	 * Return the property value associated with the given key, converted to the given
	 * targetType (never {@code null}).
	 * @throws IllegalStateException if the given key cannot be resolved
	 */
	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

	/**
	 * Resolve ${...} placeholders in the given text, replacing them with corresponding
	 * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
	 * no default value are ignored and passed through unchanged.
	 * @param text the String to resolve
	 * @return the resolved String (never {@code null})
	 * @throws IllegalArgumentException if given text is {@code null}
	 * @see #resolveRequiredPlaceholders
	 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
	 */
	String resolvePlaceholders(String text);

	/**
	 * Resolve ${...} placeholders in the given text, replacing them with corresponding
	 * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
	 * no default value will cause an IllegalArgumentException to be thrown.
	 * @return the resolved String (never {@code null})
	 * @throws IllegalArgumentException if given text is {@code null}
	 * or if any placeholders are unresolvable
	 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
	 */
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

Профиль — это логическая группа bean-компонентов, которые регистрируются в контейнере только в том случае, если данный профиль активен.

Свойства в основном используются для чтения определений свойств из различных источников: файлов свойств, свойств системы JVM, переменных системной среды, JNDI, параметров контекста сервлета, объектов специальных свойств, объектов сопоставления и т. д.

Profiles

В процессе разработки нам может понадобиться определить разные конфигурации в разных средах, например:

  • Работайте с источниками данных в памяти при разработке вместо того, чтобы искать те же источники данных из JNDI в отделе контроля качества или в рабочей среде.
  • Инфраструктура мониторинга регистрируется только при развертывании приложения в рабочей среде.
  • Пользовательские реализации bean-компонентов регистрируются для развертываний клиента A и клиента B.

Если у нас есть два источника данных, один используется в тестовой среде, а другой — в онлайн-среде, мы можем указать разные среды через профили. Следующее:

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

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build();
    }
}
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

Выражения в @Profile могут быть простыми строками или вспомогательными операторами, такими как:

  • !Логическое НЕ
  • & логическое И
  • |логический или

@Profile можно использовать в качестве мета-аннотации для создания пользовательских составных аннотаций. В следующем примере определяется пользовательская аннотация @Production, которую можно использовать в качестве замены для @Profile("production"):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @Interface Production {
}

@Profile также можно использовать на уровне метода для включения специального компонента или класса конфигурации. Следующее:

@Configuration
public class AppConfig {

    @Bean("dataSource")
    @Profile("development") 
    public DataSource standaloneDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }

    @Bean("dataSource")
    @Profile("production") 
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

Профили используются в XML

Атрибут профиля можно использовать в XML следующим образом:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

Активировать профиль

Мы определили Профили выше, но как их активировать?

Активировать профиль можно несколькими способами, но проще всего программно через API среды, предоставляемый контекстом приложения. В следующем примере показано, как это сделать:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.getEnvironment().setActiveProfiles("development");
        ctx.register(AppConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
        ctx.refresh();
    }

Кроме того, профили можно активировать декларативно с помощью свойства spring.profiles.active, которое можно указать с помощью системных переменных среды, системных свойств jvm, параметров контекста сервлета в файле web.xml или даже в виде записи в JNDI. Следующее:

-Dspring.profiles.active="profile1,profile2"

Вы также можете активировать несколько профилей одновременно.

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

Профиль по умолчанию

Профиль по умолчанию означает, что профиль активирован по умолчанию следующим образом:

@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}

Если профиль не активирован, то будет создан источник данных, который вы можете рассматривать как способ создания bean-компонентов по умолчанию. Если активированы другие профили, профиль по умолчанию использоваться не будет.

Вы можете изменить имя профиля по умолчанию в среде с помощью SetDefaultProfiles() или декларативно с помощью свойства spring.profiles.default.

PropertySource

Вот пример использования PropertySource:

    public static void main(String[] args) {
        ApplicationContext ctx = new GenericApplicationContext();
        Environment env = ctx.getEnvironment();
        boolean containsMyProperty = env.containsProperty("my-property");
        System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
    }

Здесь Spring запрашивает, определено ли свойство my-property, а StandardEnvironment здесь определяет два набора объектов PropertySource для запроса:

	/** System environment property source name: {@value}. */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value}. */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

Один представляет собой набор системных свойств JVM (System.GetProperties()), а другой — набор системных переменных среды (System.getEnv()).

Для обычной StandardServletEnvironment приоритет запроса свойства следующий:

  • Параметры ServletConfig (если применимо — например, для контекста DispatcherServlet)
  • Параметр ServletContext (элемент параметра контекста web.xml)
  • Переменная среды JNDI (Java:COMP/Env/entry)
  • Системные свойства JVM (аргумент командной строки -d)
  • Системная среда JVM (переменные среды операционной системы)

Используйте @PropertySource

Аннотация @PropertySource предоставляет удобный и декларативный механизм добавления PropertySource в Spring.

Следующий класс @Configuration использует @PropertySource для вызова testBean.getName() для возврата myTestBean:

@Configuration
@PropertySource("classpath:app.properties")
public class PropertiesConfig {


    @Autowired
    Environment env;

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

Любые заполнители $…, присутствующие в расположении ресурса @Propertysource, будут сопоставлены с набором источников свойств, которые были зарегистрированы в среде, как показано в следующем примере:

@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")

Если предположить, что my.placeholder существует в одном из зарегистрированных источников свойств (например, в системном свойстве или переменной среды), заполнитель преобразуется в соответствующее значение. Если нет, по умолчанию используется default/path. Если значение по умолчанию не указано и атрибут не может быть разрешен, будет выдано исключение IllegalArgumentException.

Примеры в этом разделе могут относиться кEnvironment

Дополнительные руководства см.блог флайдина