Skip to content

Commit 6c370ed

Browse files
committed
Recommendation for consistent @Profile declarations on overloaded @bean methods
Issue: SPR-15266 (cherry picked from commit 5d3249f)
1 parent a2b3561 commit 6c370ed

File tree

4 files changed

+82
-27
lines changed

4 files changed

+82
-27
lines changed

Diff for: spring-context/src/main/java/org/springframework/context/annotation/Conditional.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.context.annotation;
1818

19+
import java.lang.annotation.Documented;
1920
import java.lang.annotation.ElementType;
2021
import java.lang.annotation.Retention;
2122
import java.lang.annotation.RetentionPolicy;
@@ -55,8 +56,9 @@
5556
* @since 4.0
5657
* @see Condition
5758
*/
58-
@Retention(RetentionPolicy.RUNTIME)
5959
@Target({ElementType.TYPE, ElementType.METHOD})
60+
@Retention(RetentionPolicy.RUNTIME)
61+
@Documented
6062
public @interface Conditional {
6163

6264
/**

Diff for: spring-context/src/main/java/org/springframework/context/annotation/Configuration.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
* indicate they should be processed only if a given profile or profiles are <em>active</em>:
232232
*
233233
* <pre class="code">
234-
* &#064;Profile("embedded")
234+
* &#064;Profile("development")
235235
* &#064;Configuration
236236
* public class EmbeddedDatabaseConfig {
237237
*
@@ -251,6 +251,22 @@
251251
* }
252252
* }</pre>
253253
*
254+
* Alternatively, you may also declare profile conditions at the {@code @Bean} method level,
255+
* e.g. for alternative bean variants within the same configuration class:
256+
*
257+
* <pre class="code">
258+
* &#064;Configuration
259+
* public class ProfileDatabaseConfig {
260+
*
261+
* &#064;Bean("dataSource")
262+
* &#064;Profile("development")
263+
* public DataSource embeddedDatabase() { ... }
264+
*
265+
* &#064;Bean("dataSource")
266+
* &#064;Profile("production")
267+
* public DataSource productionDatabase() { ... }
268+
* }</pre>
269+
*
254270
* See the {@link Profile @Profile} and {@link org.springframework.core.env.Environment}
255271
* javadocs for further details.
256272
*

Diff for: spring-context/src/main/java/org/springframework/context/annotation/Profile.java

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,12 +57,24 @@
5757
*
5858
* <p>If a given profile is prefixed with the NOT operator ({@code !}), the annotated
5959
* component will be registered if the profile is <em>not</em> active &mdash; for example,
60-
* given {@code @Profile({"p1", "!p2"})}, registration will occur if profile 'p1' is active or
61-
* if profile 'p2' is <em>not</em> active.
60+
* given {@code @Profile({"p1", "!p2"})}, registration will occur if profile 'p1' is active
61+
* or if profile 'p2' is <em>not</em> active.
6262
*
6363
* <p>If the {@code @Profile} annotation is omitted, registration will occur regardless
6464
* of which (if any) profiles are active.
6565
*
66+
* <p><b>NOTE:</b> With {@code @Profile} on {@code @Bean} methods, a special scenario may
67+
* apply: In the case of overloaded {@code @Bean} methods of the same Java method name
68+
* (analogous to constructor overloading), an {@code @Profile} condition needs to be
69+
* consistently declared on all overloaded methods. If the conditions are inconsistent,
70+
* only the condition on the first declaration among the overloaded methods will matter.
71+
* {@code @Profile} can therefore not be used to select an overloaded method with a
72+
* particular argument signature over another; resolution between all factory methods
73+
* for the same bean follows Spring's constructor resolution algorithm at creation time.
74+
* <b>Use distinct Java method names pointing to the same {@link @Bean#name bean name}
75+
* if you'd like to define alternative beans with different profile conditions</b>;
76+
* see {@code ProfileDatabaseConfig} in {@link Configuration @Configuration}'s javadoc.
77+
*
6678
* <p>When defining Spring beans via XML, the {@code "profile"} attribute of the
6779
* {@code <beans>} element may be used. See the documentation in the
6880
* {@code spring-beans} XSD (version 3.1 or greater) for details.
@@ -78,8 +90,8 @@
7890
* @see Conditional
7991
* @see org.springframework.test.context.ActiveProfiles
8092
*/
81-
@Retention(RetentionPolicy.RUNTIME)
8293
@Target({ElementType.TYPE, ElementType.METHOD})
94+
@Retention(RetentionPolicy.RUNTIME)
8395
@Documented
8496
@Conditional(ProfileCondition.class)
8597
public @interface Profile {

Diff for: src/asciidoc/core-beans.adoc

+45-20
Original file line numberDiff line numberDiff line change
@@ -7402,6 +7402,9 @@ jdbc.password=
74027402
}
74037403
----
74047404

7405+
7406+
7407+
74057408
[[beans-environment]]
74067409
== Environment abstraction
74077410

@@ -7423,6 +7426,8 @@ on. The role of the `Environment` object with relation to properties is to provi
74237426
user with a convenient service interface for configuring property sources and resolving
74247427
properties from them.
74257428

7429+
7430+
74267431
[[beans-definition-profiles]]
74277432
=== Bean definition profiles
74287433

@@ -7497,7 +7502,7 @@ can rewrite the `dataSource` configuration as follows:
74977502
[subs="verbatim,quotes"]
74987503
----
74997504
@Configuration
7500-
**@Profile("dev")**
7505+
**@Profile("development")**
75017506
public class StandaloneDataConfig {
75027507
75037508
@Bean
@@ -7549,56 +7554,76 @@ of creating a custom _composed annotation_. The following example defines a cust
75497554
}
75507555
----
75517556

7557+
[TIP]
7558+
====
7559+
If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and
7560+
`@Import` annotations associated with that class will be bypassed unless one or more of
7561+
the specified profiles are active. If a `@Component` or `@Configuration` class is marked
7562+
with `@Profile({"p1", "p2"})`, that class will not be registered/processed unless
7563+
profiles 'p1' and/or 'p2' have been activated. If a given profile is prefixed with the
7564+
NOT operator (`!`), the annotated element will be registered if the profile is **not**
7565+
active. For example, given `@Profile({"p1", "!p2"})`, registration will occur if profile
7566+
'p1' is active or if profile 'p2' is not active.
7567+
====
7568+
75527569
`@Profile` can also be declared at the method level to include only one particular bean
7553-
of a configuration class:
7570+
of a configuration class, e.g. for alternative variants of a particular bean:
75547571

75557572
[source,java,indent=0]
75567573
[subs="verbatim,quotes"]
75577574
----
75587575
@Configuration
75597576
public class AppConfig {
75607577
7561-
@Bean
7562-
**@Profile("dev")**
7563-
public DataSource devDataSource() {
7578+
@Bean("dataSource")
7579+
**@Profile("development")**
7580+
public DataSource standaloneDataSource() {
75647581
return new EmbeddedDatabaseBuilder()
75657582
.setType(EmbeddedDatabaseType.HSQL)
75667583
.addScript("classpath:com/bank/config/sql/schema.sql")
75677584
.addScript("classpath:com/bank/config/sql/test-data.sql")
75687585
.build();
75697586
}
75707587
7571-
@Bean
7588+
@Bean("dataSource")
75727589
**@Profile("production")**
7573-
public DataSource productionDataSource() throws Exception {
7590+
public DataSource jndiDataSource() throws Exception {
75747591
Context ctx = new InitialContext();
75757592
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
75767593
}
75777594
}
75787595
----
75797596

7580-
[TIP]
7597+
[NOTE]
75817598
====
7582-
If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and
7583-
`@Import` annotations associated with that class will be bypassed unless one or more of
7584-
the specified profiles are active. If a `@Component` or `@Configuration` class is marked
7585-
with `@Profile({"p1", "p2"})`, that class will not be registered/processed unless
7586-
profiles 'p1' and/or 'p2' have been activated. If a given profile is prefixed with the
7587-
NOT operator (`!`), the annotated element will be registered if the profile is **not**
7588-
active. For example, given `@Profile({"p1", "!p2"})`, registration will occur if profile
7589-
'p1' is active or if profile 'p2' is not active.
7599+
With `@Profile` on `@Bean` methods, a special scenario may apply: In the case of
7600+
overloaded `@Bean` methods of the same Java method name (analogous to constructor
7601+
overloading), an `@Profile` condition needs to be consistently declared on all
7602+
overloaded methods. If the conditions are inconsistent, only the condition on the
7603+
first declaration among the overloaded methods will matter. `@Profile` can therefore
7604+
not be used to select an overloaded method with a particular argument signature over
7605+
another; resolution between all factory methods for the same bean follows Spring's
7606+
constructor resolution algorithm at creation time.
7607+
7608+
If you would like to define alternative beans with different profile conditions,
7609+
use distinct Java method names pointing to the same bean name via the `@Bean` name
7610+
attribute, as indicated in the example above. If the argument signatures are all
7611+
the same (e.g. all of the variants have no-arg factory methods), this is the only
7612+
way to represent such an arrangement in a valid Java class in the first place
7613+
(since there can only be one method of a particular name and argument signature).
75907614
====
75917615

7616+
75927617
[[beans-definition-profiles-xml]]
7593-
=== XML bean definition profiles
7618+
==== XML bean definition profiles
75947619

75957620
The XML counterpart is the `profile` attribute of the `<beans>` element. Our sample
75967621
configuration above can be rewritten in two XML files as follows:
75977622

75987623
[source,xml,indent=0]
75997624
[subs="verbatim,quotes"]
76007625
----
7601-
<beans profile="dev"
7626+
<beans profile="development"
76027627
xmlns="http://www.springframework.org/schema/beans"
76037628
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
76047629
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
@@ -7637,7 +7662,7 @@ It is also possible to avoid that split and nest `<beans/>` elements within the
76377662
76387663
<!-- other bean definitions -->
76397664
7640-
<beans profile="dev">
7665+
<beans profile="development">
76417666
<jdbc:embedded-database id="dataSource">
76427667
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
76437668
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
@@ -7671,7 +7696,7 @@ it programmatically against the `Environment` API which is available via an
76717696
[subs="verbatim,quotes"]
76727697
----
76737698
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
7674-
ctx.getEnvironment().setActiveProfiles("dev");
7699+
ctx.getEnvironment().setActiveProfiles("development");
76757700
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
76767701
ctx.refresh();
76777702
----

0 commit comments

Comments
 (0)