Spring IoC контейнер: абстракция Environment, профили определения компонентов, использование @Profile

Аннотация @Profile позволяет указать, что компонент имеет право на регистрацию, если активен один или несколько указанных профилей. Используя наш предыдущий пример, мы можем переписать конфигурацию источника данных следующим образом:

Java

@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();
    }
}

Kotlin

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

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

Java

@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");
    }
}

Kotlin

@Configuration
@Profile("production")
class JndiDataConfig {

    @Bean(destroyMethod = "")
    fun dataSource(): DataSource {
        val ctx = InitialContext()
        return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
    }
}

С помощью методов @Bean вы обычно выбираете программный поиск JNDI, используя либо помощники Spring JndiTemplate/JndiLocatorDelegate, либо прямое использование JNDI InitialContext, но не вариант JndiObjectFactoryBean, который заставит вас объявить возвращаемый тип как тип FactoryBean.

Строка профиля может содержать простое имя профиля (например, production) или выражение профиля. Выражение профиля позволяет выразить более сложную логику профиля (например, production & us-east). В выражениях профиля поддерживаются следующие операторы:

  • ! : Логическое "НЕ" профиля
  • & : Логическое "И" профилей.
  • | : Логическое "ИЛИ" профилей.

Нельзя смешивать & и | операторы без скобок. Например, production & us-east | eu-central не является допустимым выражением. Оно должно быть выражено как production & (us-east | eu-central).

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

Java

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

Kotlin

@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Profile("production")
annotation class Production

Если класс @Configuration помечен @Profile, все методы @Bean и аннотации @Import, связанные с этим классом, пропускаются, если не активен один или несколько указанных профилей. Если класс @Component или @Configuration помечен @Profile({"p1", "p2"}), этот класс не регистрируется и не обрабатывается, если не были активированы профили 'p1' или 'p2'. Если перед заданным профилем стоит префикс оператора НЕ (!), Аннотированный элемент регистрируется, только если профиль не активен. Например, для данного @Profile({"p1", "!p2"}) регистрация произойдет, если профиль 'p1' активен или если профиль 'p2' неактивен.

@Profile также можно объявить на уровне метода, чтобы включить только один конкретный bean-компонент класса конфигурации (например, для альтернативных вариантов конкретного bean-компонента), как показано в следующем примере:

Java

@Configuration
public class AppConfig {

    @Bean("dataSource")
    @Profile("development") // Метод standaloneDataSource доступен только в профиле 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") // Метод jndiDataSource доступен только в профиле production.
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

Kotlin

@Configuration
class AppConfig {

    @Bean("dataSource")
    @Profile("development") // Метод standaloneDataSource доступен только в профиле development.
    fun standaloneDataSource(): DataSource {
        return 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") // Метод jndiDataSource доступен только в профиле production.
    fun jndiDataSource() =
        InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource
}

С @Profile в методах @Bean может применяться особый сценарий: в случае перегруженных методов @Bean с тем же именем метода Java (аналогично перегрузке конструктора) условие @Profile должно быть последовательно объявлено для всех перегруженных методов. Если условия несовместимы, имеет значение только условие первого объявления среди перегруженных методов. Следовательно, @Profile нельзя использовать для выбора перегруженного метода с одной сигнатурой аргумента по сравнению с другим. Разрешение между всеми фабричными методами для одного и того же bean-компонента следует алгоритму разрешения конструктора Spring во время создания.

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


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки