Skip to content

Commit 69a762e

Browse files
committed
resolved:
+ Provide @primary annotation (SPR-5590) + Provide @lazy annotation (SPR-5591) + Test @bean initMethod/destroyMethod functionality (SPR-5592) + Test @bean dependsOn functionality (SPR-5593)
1 parent 5bcdf81 commit 69a762e

File tree

9 files changed

+360
-46
lines changed

9 files changed

+360
-46
lines changed

org.springframework.context/src/main/java/org/springframework/context/annotation/Bean.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,19 @@
7777
/**
7878
* The optional name of a method to call on the bean instance during initialization.
7979
* Not commonly used, given that the method may be called programmatically directly
80-
* within the Bean method.
80+
* within the body of a Bean-annotated method.
8181
*/
8282
String initMethod() default "";
8383

8484
/**
8585
* The optional name of a method to call on the bean instance during upon closing
8686
* the application context, for example a {@literal close()}
87-
* method on a {@literal DataSource}.
87+
* method on a {@literal DataSource}. The method must have no arguments, but may
88+
* throw any exception.
89+
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
90+
* factory which is always the case for singletons, but not guaranteed
91+
* for any other scope.
92+
* see {@link org.springframework.context.ConfigurableApplicationContext#close()}
8893
*/
8994
String destroyMethod() default "";
9095

@@ -93,6 +98,8 @@
9398
* created by the container before this bean. Used infrequently in cases where a bean
9499
* does not explicitly depend on another through properties or constructor arguments,
95100
* but rather depends on the side effects of another bean's initialization.
101+
* <p>Note: This attribute will not be inherited by child bean definitions,
102+
* hence it needs to be specified per concrete bean definition.
96103
*/
97104
String[] dependsOn() default {};
98105

org.springframework.context/src/main/java/org/springframework/context/annotation/BeanMethod.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ public int getModifiers() {
8585
* @see #getRequiredAnnotation(Class)
8686
*/
8787
@SuppressWarnings("unchecked")
88-
public <T extends Annotation> T getAnnotation(Class<T> annoType) {
88+
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
8989
for (Annotation anno : annotations)
9090
if (anno.annotationType().equals(annoType))
91-
return (T) anno;
91+
return (A) anno;
9292

9393
return null;
9494
}
@@ -101,7 +101,9 @@ public <T extends Annotation> T getAnnotation(Class<T> annoType) {
101101
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
102102
T anno = getAnnotation(annoType);
103103

104-
Assert.notNull(anno, format("annotation %s not found on %s", annoType.getSimpleName(), this));
104+
if(anno == null)
105+
throw new IllegalStateException(
106+
format("required annotation %s is not present on %s", annoType.getSimpleName(), this));
105107

106108
return anno;
107109
}

org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@
5555
* @see Bean
5656
* @see Lazy
5757
* @see Value
58-
* @see org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
58+
* @see ConfigurationClassPostProcessor;
5959
*/
60-
@Component
6160
@Target( { ElementType.TYPE })
6261
@Retention(RetentionPolicy.RUNTIME)
6362
@Inherited
6463
@Documented
64+
@Component
6565
public @interface Configuration {
6666

6767
}

org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static java.lang.String.*;
2020

21+
import java.lang.annotation.Annotation;
2122
import java.lang.reflect.Modifier;
2223
import java.util.HashSet;
2324
import java.util.Set;
@@ -43,7 +44,7 @@ final class ConfigurationClass extends ModelClass {
4344

4445
private String beanName;
4546
private int modifiers;
46-
private Configuration configurationAnnotation;
47+
private HashSet<Annotation> annotations = new HashSet<Annotation>();
4748
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
4849
private ConfigurationClass declaringClass;
4950

@@ -63,14 +64,38 @@ public void setModifiers(int modifiers) {
6364
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
6465
this.modifiers = modifiers;
6566
}
67+
68+
public void addAnnotation(Annotation annotation) {
69+
this.annotations.add(annotation);
70+
}
71+
72+
/**
73+
* @return the annotation on this class matching <var>annoType</var> or
74+
* {@literal null} if not present.
75+
* @see #getRequiredAnnotation(Class)
76+
*/
77+
@SuppressWarnings("unchecked")
78+
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
79+
for (Annotation annotation : annotations)
80+
if(annotation.annotationType().equals(annoType))
81+
return (A) annotation;
6682

67-
public Configuration getConfigurationAnnotation() {
68-
return this.configurationAnnotation;
83+
return null;
6984
}
7085

71-
public void setConfigurationAnnotation(Configuration configAnno) {
72-
Assert.notNull(configAnno, "configuration annotation must be non-null");
73-
this.configurationAnnotation = configAnno;
86+
/**
87+
* @return the annotation on this class matching <var>annoType</var>
88+
* @throws {@link IllegalStateException} if not present
89+
* @see #getAnnotation(Class)
90+
*/
91+
public <A extends Annotation> A getRequiredAnnotation(Class<A> annoType) {
92+
A anno = getAnnotation(annoType);
93+
94+
if(anno == null)
95+
throw new IllegalStateException(
96+
format("required annotation %s is not present on %s", annoType.getSimpleName(), this));
97+
98+
return anno;
7499
}
75100

76101
public Set<BeanMethod> getBeanMethods() {
@@ -93,7 +118,7 @@ public void setDeclaringClass(ConfigurationClass configurationClass) {
93118

94119
public void validate(ProblemReporter problemReporter) {
95120
// configuration classes must be annotated with @Configuration
96-
if (configurationAnnotation == null)
121+
if (getAnnotation(Configuration.class) == null)
97122
problemReporter.error(new NonAnnotatedConfigurationProblem());
98123

99124
// a configuration class may not be final (CGLIB limitation)
@@ -113,9 +138,12 @@ public String toString() {
113138
public int hashCode() {
114139
final int prime = 31;
115140
int result = super.hashCode();
116-
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
117-
result = prime * result + ((beanName == null) ? 0 : beanName.hashCode());
118-
result = prime * result + ((configurationAnnotation == null) ? 0 : configurationAnnotation.hashCode());
141+
result = prime * result
142+
+ ((annotations == null) ? 0 : annotations.hashCode());
143+
result = prime * result
144+
+ ((beanName == null) ? 0 : beanName.hashCode());
145+
result = prime * result
146+
+ ((declaringClass == null) ? 0 : declaringClass.hashCode());
119147
result = prime * result + ((methods == null) ? 0 : methods.hashCode());
120148
result = prime * result + modifiers;
121149
return result;
@@ -130,20 +158,20 @@ public boolean equals(Object obj) {
130158
if (getClass() != obj.getClass())
131159
return false;
132160
ConfigurationClass other = (ConfigurationClass) obj;
133-
if (declaringClass == null) {
134-
if (other.declaringClass != null)
161+
if (annotations == null) {
162+
if (other.annotations != null)
135163
return false;
136-
} else if (!declaringClass.equals(other.declaringClass))
164+
} else if (!annotations.equals(other.annotations))
137165
return false;
138166
if (beanName == null) {
139167
if (other.beanName != null)
140168
return false;
141169
} else if (!beanName.equals(other.beanName))
142170
return false;
143-
if (configurationAnnotation == null) {
144-
if (other.configurationAnnotation != null)
171+
if (declaringClass == null) {
172+
if (other.declaringClass != null)
145173
return false;
146-
} else if (!configurationAnnotation.equals(other.configurationAnnotation))
174+
} else if (!declaringClass.equals(other.declaringClass))
147175
return false;
148176
if (methods == null) {
149177
if (other.methods != null)

org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassVisitor.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.springframework.context.annotation.AsmUtils.*;
2121
import static org.springframework.util.ClassUtils.*;
2222

23+
import java.lang.annotation.Annotation;
2324
import java.util.HashMap;
2425
import java.util.Stack;
2526

@@ -105,26 +106,27 @@ private void visitSuperType(String superTypeDesc) {
105106

106107
/**
107108
* Visits a class level annotation on a {@link Configuration @Configuration} class.
108-
* Accounts for all possible class-level annotations that are respected by JavaConfig
109-
* including AspectJ's {@code @Aspect} annotation.
110-
* <p>
111-
* Upon encountering such an annotation, update the {@link #configClass} model object
112-
* appropriately, and then return an {@link AnnotationVisitor} implementation that can
113-
* populate the annotation appropriately with data.
109+
*
110+
* <p>Upon encountering such an annotation, updates the {@link #configClass} model
111+
* object appropriately, and then returns an {@link AnnotationVisitor} implementation
112+
* that can populate the annotation appropriately with its attribute data as parsed
113+
* by ASM.
114114
*
115115
* @see MutableAnnotation
116+
* @see Configuration
117+
* @see Lazy
118+
* @see Import
116119
*/
117120
@Override
118121
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
119122
String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
123+
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoTypeName, classLoader);
120124

121-
if (Configuration.class.getName().equals(annoTypeName)) {
122-
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class, classLoader);
123-
configClass.setConfigurationAnnotation(mutableConfiguration);
124-
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
125-
}
125+
if (annoClass == null)
126+
// annotation was unable to be loaded -> probably Spring IDE unable to load a user-defined annotation
127+
return super.visitAnnotation(annoTypeDesc, visible);
126128

127-
if (Import.class.getName().equals(annoTypeName)) {
129+
if (Import.class.equals(annoClass)) {
128130
ImportStack importStack = ImportStackHolder.getImportStack();
129131

130132
if (!importStack.contains(configClass)) {
@@ -135,7 +137,9 @@ public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
135137
problemReporter.error(new CircularImportProblem(configClass, importStack));
136138
}
137139

138-
return super.visitAnnotation(annoTypeDesc, visible);
140+
Annotation mutableAnnotation = createMutableAnnotation(annoClass, classLoader);
141+
configClass.addAnnotation(mutableAnnotation);
142+
return new MutableAnnotationVisitor(mutableAnnotation, classLoader);
139143
}
140144

141145
/**
@@ -187,7 +191,7 @@ public void visitInnerClass(String name, String outerName, String innerName, int
187191
innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
188192

189193
// is the inner class a @Configuration class? If so, add it to the list
190-
if (innerConfigClass.getConfigurationAnnotation() != null)
194+
if (innerConfigClass.getAnnotation(Configuration.class) != null)
191195
innerClasses.put(name, innerConfigClass);
192196
}
193197

org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModelBeanDefinitionReader.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,16 @@ private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
155155
}
156156
}
157157

158-
// TODO: re-enable for Lazy support
159-
// // is this bean marked as primary for disambiguation?
160-
// if (bean.primary() == Primary.TRUE)
161-
// beanDef.setPrimary(true);
162-
//
163-
// // is this bean lazily instantiated?
164-
// if ((bean.lazy() == Lazy.TRUE)
165-
// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE)))
166-
// beanDef.setLazyInit(true);
158+
if (method.getAnnotation(Primary.class) != null)
159+
beanDef.setPrimary(true);
160+
161+
// is this bean to be instantiated lazily?
162+
Lazy defaultLazy = configClass.getAnnotation(Lazy.class);
163+
if (defaultLazy != null)
164+
beanDef.setLazyInit(defaultLazy.value());
165+
Lazy lazy = method.getAnnotation(Lazy.class);
166+
if (lazy != null)
167+
beanDef.setLazyInit(lazy.value());
167168

168169
// does this bean have a custom init-method specified?
169170
String initMethodName = bean.initMethod();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2002-2009 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
26+
/**
27+
* Indicates whether a bean is to be lazily initialized.
28+
*
29+
* <p>May be used on any class directly or indirectly annotated with
30+
* {@link org.springframework.stereotype.Component} or on methods annotated with
31+
* {@link Bean}.
32+
*
33+
* <p>If this annotation is not present on a Component or Bean definition, eager
34+
* initialization will occur. If present and set to {@literal true}, the
35+
* Bean/Component will not be initialized until referenced by another bean or
36+
* explicitly retrieved from the enclosing
37+
* {@link org.springframework.beans.factory.BeanFactory}. If present and set to
38+
* {@literal false}, the bean will be instantiated on startup by bean factories
39+
* that perform eager initialization of singletons.
40+
*
41+
* <p>If Lazy is present on a {@link Configuration} class, this indicates that all
42+
* {@link Bean} methods within that {@literal Configuration} should be lazily
43+
* initialized. If Lazy is present and false on a Bean method within a
44+
* Lazy-annotated Configuration class, this indicates overriding the 'default
45+
* lazy' behavior and that the bean should be eagerly initialized.
46+
*
47+
* @author Chris Beams
48+
* @since 3.0
49+
* @see Primary
50+
* @see Bean
51+
* @see Configuration
52+
* @see org.springframework.stereotype.Component
53+
*/
54+
@Target({ ElementType.TYPE, ElementType.METHOD })
55+
@Retention(RetentionPolicy.RUNTIME)
56+
@Documented
57+
public @interface Lazy {
58+
59+
/**
60+
* Whether lazy initialization should occur.
61+
*/
62+
boolean value() default true;
63+
64+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2002-2009 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
26+
/**
27+
* Indicates that a bean should be given preference when multiple candidates
28+
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
29+
* bean exists among the candidates, it will be the autowired value.
30+
*
31+
* <p>May be used on any class directly or indirectly annotated with
32+
* {@link org.springframework.stereotype.Component} or on methods annotated
33+
* with {@link Bean}.
34+
*
35+
* <p>Using {@link Primary} at the class level has no effect unless component-scanning
36+
* is being used. If a {@link Primary}-annotated class is declared via XML,
37+
* {@link Primary} annotation metadata is ignored, and
38+
* {@literal <bean primary="true|false"/>} is respected instead.
39+
*
40+
* @author Chris Beams
41+
* @since 3.0
42+
* @see Lazy
43+
* @see Bean
44+
* @see org.springframework.stereotype.Component
45+
*/
46+
@Target({ ElementType.TYPE, ElementType.METHOD })
47+
@Retention(RetentionPolicy.RUNTIME)
48+
@Documented
49+
public @interface Primary {
50+
51+
}

0 commit comments

Comments
 (0)