From 6c370ed28d93519698fbb4b1f68742acff42d628 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Apr 2017 14:52:06 +0200 Subject: [PATCH] Recommendation for consistent @Profile declarations on overloaded @Bean methods Issue: SPR-15266 (cherry picked from commit 5d3249f) --- .../context/annotation/Conditional.java | 6 +- .../context/annotation/Configuration.java | 18 ++++- .../context/annotation/Profile.java | 20 ++++-- src/asciidoc/core-beans.adoc | 65 +++++++++++++------ 4 files changed, 82 insertions(+), 27 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java index f47aecf1741..578a6e2a6c5 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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. @@ -16,6 +16,7 @@ package org.springframework.context.annotation; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -55,8 +56,9 @@ import java.lang.annotation.Target; * @since 4.0 * @see Condition */ -@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented public @interface Conditional { /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java index 04627fb749b..49019333114 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java @@ -231,7 +231,7 @@ import org.springframework.stereotype.Component; * indicate they should be processed only if a given profile or profiles are active: * *
- * @Profile("embedded")
+ * @Profile("development")
  * @Configuration
  * public class EmbeddedDatabaseConfig {
  *
@@ -251,6 +251,22 @@ import org.springframework.stereotype.Component;
  *     }
  * }
* + * Alternatively, you may also declare profile conditions at the {@code @Bean} method level, + * e.g. for alternative bean variants within the same configuration class: + * + *
+ * @Configuration
+ * public class ProfileDatabaseConfig {
+ *
+ *     @Bean("dataSource")
+ *     @Profile("development")
+ *     public DataSource embeddedDatabase() { ... }
+ *
+ *     @Bean("dataSource")
+ *     @Profile("production")
+ *     public DataSource productionDatabase() { ... }
+ * }
+ * * See the {@link Profile @Profile} and {@link org.springframework.core.env.Environment} * javadocs for further details. * diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Profile.java b/spring-context/src/main/java/org/springframework/context/annotation/Profile.java index 36f609798cc..0838238d4a5 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Profile.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Profile.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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. @@ -57,12 +57,24 @@ import org.springframework.core.env.ConfigurableEnvironment; * *

If a given profile is prefixed with the NOT operator ({@code !}), the annotated * component will be registered if the profile is not active — for example, - * given {@code @Profile({"p1", "!p2"})}, registration will occur if profile 'p1' is active or - * if profile 'p2' is not active. + * given {@code @Profile({"p1", "!p2"})}, registration will occur if profile 'p1' is active + * or if profile 'p2' is not active. * *

If the {@code @Profile} annotation is omitted, registration will occur regardless * of which (if any) profiles are active. * + *

NOTE: With {@code @Profile} on {@code @Bean} methods, a special scenario may + * apply: In the case of overloaded {@code @Bean} methods of the same Java method name + * (analogous to constructor overloading), an {@code @Profile} condition needs to be + * consistently declared on all overloaded methods. If the conditions are inconsistent, + * only the condition on the first declaration among the overloaded methods will matter. + * {@code @Profile} can therefore not be used to select an overloaded method with a + * particular argument signature over another; resolution between all factory methods + * for the same bean follows Spring's constructor resolution algorithm at creation time. + * Use distinct Java method names pointing to the same {@link @Bean#name bean name} + * if you'd like to define alternative beans with different profile conditions; + * see {@code ProfileDatabaseConfig} in {@link Configuration @Configuration}'s javadoc. + * *

When defining Spring beans via XML, the {@code "profile"} attribute of the * {@code } element may be used. See the documentation in the * {@code spring-beans} XSD (version 3.1 or greater) for details. @@ -78,8 +90,8 @@ import org.springframework.core.env.ConfigurableEnvironment; * @see Conditional * @see org.springframework.test.context.ActiveProfiles */ -@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ProfileCondition.class) public @interface Profile { diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index a4472c1c8ea..aa8a32aa661 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -7402,6 +7402,9 @@ jdbc.password= } ---- + + + [[beans-environment]] == Environment abstraction @@ -7423,6 +7426,8 @@ on. The role of the `Environment` object with relation to properties is to provi user with a convenient service interface for configuring property sources and resolving properties from them. + + [[beans-definition-profiles]] === Bean definition profiles @@ -7497,7 +7502,7 @@ can rewrite the `dataSource` configuration as follows: [subs="verbatim,quotes"] ---- @Configuration - **@Profile("dev")** + **@Profile("development")** public class StandaloneDataConfig { @Bean @@ -7549,8 +7554,20 @@ of creating a custom _composed annotation_. The following example defines a cust } ---- +[TIP] +==== +If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and +`@Import` annotations associated with that class will be bypassed unless one or more of +the specified profiles are active. If a `@Component` or `@Configuration` class is marked +with `@Profile({"p1", "p2"})`, that class will not be registered/processed unless +profiles 'p1' and/or 'p2' have been activated. If a given profile is prefixed with the +NOT operator (`!`), the annotated element will be registered if the profile is **not** +active. For example, given `@Profile({"p1", "!p2"})`, registration will occur if profile +'p1' is active or if profile 'p2' is not active. +==== + `@Profile` can also be declared at the method level to include only one particular bean -of a configuration class: +of a configuration class, e.g. for alternative variants of a particular bean: [source,java,indent=0] [subs="verbatim,quotes"] @@ -7558,9 +7575,9 @@ of a configuration class: @Configuration public class AppConfig { - @Bean - **@Profile("dev")** - public DataSource devDataSource() { + @Bean("dataSource") + **@Profile("development")** + public DataSource standaloneDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") @@ -7568,29 +7585,37 @@ of a configuration class: .build(); } - @Bean + @Bean("dataSource") **@Profile("production")** - public DataSource productionDataSource() throws Exception { + public DataSource jndiDataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } } ---- -[TIP] +[NOTE] ==== -If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and -`@Import` annotations associated with that class will be bypassed unless one or more of -the specified profiles are active. If a `@Component` or `@Configuration` class is marked -with `@Profile({"p1", "p2"})`, that class will not be registered/processed unless -profiles 'p1' and/or 'p2' have been activated. If a given profile is prefixed with the -NOT operator (`!`), the annotated element will be registered if the profile is **not** -active. For example, given `@Profile({"p1", "!p2"})`, registration will occur if profile -'p1' is active or if profile 'p2' is not active. +With `@Profile` on `@Bean` methods, a special scenario may apply: In the case of +overloaded `@Bean` methods of the same Java method name (analogous to constructor +overloading), an `@Profile` condition needs to be consistently declared on all +overloaded methods. If the conditions are inconsistent, only the condition on the +first declaration among the overloaded methods will matter. `@Profile` can therefore +not be used to select an overloaded method with a particular argument signature over +another; resolution between all factory methods for the same bean follows Spring's +constructor resolution algorithm at creation time. + +If you would like to define alternative beans with different profile conditions, +use distinct Java method names pointing to the same bean name via the `@Bean` name +attribute, as indicated in the example above. If the argument signatures are all +the same (e.g. all of the variants have no-arg factory methods), this is the only +way to represent such an arrangement in a valid Java class in the first place +(since there can only be one method of a particular name and argument signature). ==== + [[beans-definition-profiles-xml]] -=== XML bean definition profiles +==== XML bean definition profiles The XML counterpart is the `profile` attribute of the `` element. Our sample configuration above can be rewritten in two XML files as follows: @@ -7598,7 +7623,7 @@ configuration above can be rewritten in two XML files as follows: [source,xml,indent=0] [subs="verbatim,quotes"] ---- - ` elements within the - + @@ -7671,7 +7696,7 @@ it programmatically against the `Environment` API which is available via an [subs="verbatim,quotes"] ---- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.getEnvironment().setActiveProfiles("dev"); + ctx.getEnvironment().setActiveProfiles("development"); ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); ctx.refresh(); ----