1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.context.annotation;
18
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
31 import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
32 import org.springframework.beans.factory.annotation.Autowire;
33 import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
34 import org.springframework.beans.factory.config.BeanDefinition;
35 import org.springframework.beans.factory.config.BeanDefinitionHolder;
36 import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
37 import org.springframework.beans.factory.parsing.Location;
38 import org.springframework.beans.factory.parsing.Problem;
39 import org.springframework.beans.factory.parsing.ProblemReporter;
40 import org.springframework.beans.factory.parsing.SourceExtractor;
41 import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
42 import org.springframework.beans.factory.support.BeanDefinitionReader;
43 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
44 import org.springframework.beans.factory.support.BeanNameGenerator;
45 import org.springframework.beans.factory.support.RootBeanDefinition;
46 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
47 import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
48 import org.springframework.core.annotation.AnnotationAttributes;
49 import org.springframework.core.env.Environment;
50 import org.springframework.core.io.Resource;
51 import org.springframework.core.io.ResourceLoader;
52 import org.springframework.core.type.AnnotationMetadata;
53 import org.springframework.core.type.MethodMetadata;
54 import org.springframework.core.type.classreading.MetadataReaderFactory;
55 import org.springframework.util.StringUtils;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 class ConfigurationClassBeanDefinitionReader {
72
73 private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
74
75 private static final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
76
77 private final BeanDefinitionRegistry registry;
78
79 private final SourceExtractor sourceExtractor;
80
81 private final ProblemReporter problemReporter;
82
83 private final MetadataReaderFactory metadataReaderFactory;
84
85 private final ResourceLoader resourceLoader;
86
87 private final Environment environment;
88
89 private final BeanNameGenerator importBeanNameGenerator;
90
91 private final ImportRegistry importRegistry;
92
93 private final ConditionEvaluator conditionEvaluator;
94
95
96
97
98
99
100 ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
101 ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
102 ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator,
103 ImportRegistry importRegistry) {
104
105 this.registry = registry;
106 this.sourceExtractor = sourceExtractor;
107 this.problemReporter = problemReporter;
108 this.metadataReaderFactory = metadataReaderFactory;
109 this.resourceLoader = resourceLoader;
110 this.environment = environment;
111 this.importBeanNameGenerator = importBeanNameGenerator;
112 this.importRegistry = importRegistry;
113 this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
114 }
115
116
117
118
119
120
121 public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
122 TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
123 for (ConfigurationClass configClass : configurationModel) {
124 loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
125 }
126 }
127
128
129
130
131
132 private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
133 TrackedConditionEvaluator trackedConditionEvaluator) {
134
135 if (trackedConditionEvaluator.shouldSkip(configClass)) {
136 String beanName = configClass.getBeanName();
137 if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
138 this.registry.removeBeanDefinition(beanName);
139 }
140 this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
141 return;
142 }
143
144 if (configClass.isImported()) {
145 registerBeanDefinitionForImportedConfigurationClass(configClass);
146 }
147 for (BeanMethod beanMethod : configClass.getBeanMethods()) {
148 loadBeanDefinitionsForBeanMethod(beanMethod);
149 }
150 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
151 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
152 }
153
154
155
156
157 private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
158 AnnotationMetadata metadata = configClass.getMetadata();
159 AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
160
161 if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
162 ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
163 configBeanDef.setScope(scopeMetadata.getScopeName());
164 String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
165 AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
166 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
167 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
168 this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
169 configClass.setBeanName(configBeanName);
170 if (logger.isDebugEnabled()) {
171 logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
172 }
173 }
174 else {
175 this.problemReporter.error(
176 new InvalidConfigurationImportProblem(metadata.getClassName(), configClass.getResource(), metadata));
177 }
178 }
179
180
181
182
183
184 private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
185 ConfigurationClass configClass = beanMethod.getConfigurationClass();
186 MethodMetadata metadata = beanMethod.getMetadata();
187 String methodName = metadata.getMethodName();
188
189
190 if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
191 configClass.skippedBeanMethods.add(methodName);
192 return;
193 }
194 if (configClass.skippedBeanMethods.contains(methodName)) {
195 return;
196 }
197
198
199 AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
200 List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
201 String beanName = (names.size() > 0 ? names.remove(0) : methodName);
202
203
204 for (String alias : names) {
205 this.registry.registerAlias(beanName, alias);
206 }
207
208
209 if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
210 return;
211 }
212
213 ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
214 beanDef.setResource(configClass.getResource());
215 beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
216
217 if (metadata.isStatic()) {
218
219 beanDef.setBeanClassName(configClass.getMetadata().getClassName());
220 beanDef.setFactoryMethodName(methodName);
221 }
222 else {
223
224 beanDef.setFactoryBeanName(configClass.getBeanName());
225 beanDef.setUniqueFactoryMethodName(methodName);
226 }
227 beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
228 beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
229
230 AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
231
232 Autowire autowire = bean.getEnum("autowire");
233 if (autowire.isAutowire()) {
234 beanDef.setAutowireMode(autowire.value());
235 }
236
237 String initMethodName = bean.getString("initMethod");
238 if (StringUtils.hasText(initMethodName)) {
239 beanDef.setInitMethodName(initMethodName);
240 }
241
242 String destroyMethodName = bean.getString("destroyMethod");
243 if (StringUtils.hasText(destroyMethodName)) {
244 beanDef.setDestroyMethodName(destroyMethodName);
245 }
246
247
248 ScopedProxyMode proxyMode = ScopedProxyMode.NO;
249 AnnotationAttributes scope = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
250 if (scope != null) {
251 beanDef.setScope(scope.getString("value"));
252 proxyMode = scope.getEnum("proxyMode");
253 if (proxyMode == ScopedProxyMode.DEFAULT) {
254 proxyMode = ScopedProxyMode.NO;
255 }
256 }
257
258
259 BeanDefinition beanDefToRegister = beanDef;
260 if (proxyMode != ScopedProxyMode.NO) {
261 BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
262 new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
263 beanDefToRegister = new ConfigurationClassBeanDefinition(
264 (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
265 }
266
267 if (logger.isDebugEnabled()) {
268 logger.debug(String.format("Registering bean definition for @Bean method %s.%s()",
269 configClass.getMetadata().getClassName(), beanName));
270 }
271
272 this.registry.registerBeanDefinition(beanName, beanDefToRegister);
273 }
274
275 protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
276 if (!this.registry.containsBeanDefinition(beanName)) {
277 return false;
278 }
279 BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
280
281
282
283
284
285 if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
286 ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
287 return (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName()));
288 }
289
290
291
292 if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
293 return false;
294 }
295
296
297
298 if (logger.isInfoEnabled()) {
299 logger.info(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
300 "already exists. This top-level bean definition is considered as an override.",
301 beanMethod, beanName));
302 }
303 return true;
304 }
305
306 private void loadBeanDefinitionsFromImportedResources(
307 Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
308
309 Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<Class<?>, BeanDefinitionReader>();
310
311 for (Map.Entry<String, Class<? extends BeanDefinitionReader>> entry : importedResources.entrySet()) {
312 String resource = entry.getKey();
313 Class<? extends BeanDefinitionReader> readerClass = entry.getValue();
314
315
316 if (readerClass.equals(BeanDefinitionReader.class)) {
317 if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
318
319 readerClass = GroovyBeanDefinitionReader.class;
320 }
321 else {
322
323 readerClass = XmlBeanDefinitionReader.class;
324 }
325 }
326
327 BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
328 if (reader == null) {
329 try {
330
331 reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
332
333 if (reader instanceof AbstractBeanDefinitionReader) {
334 AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
335 abdr.setResourceLoader(this.resourceLoader);
336 abdr.setEnvironment(this.environment);
337 }
338 readerInstanceCache.put(readerClass, reader);
339 }
340 catch (Exception ex) {
341 throw new IllegalStateException(
342 "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
343 }
344 }
345
346
347 reader.loadBeanDefinitions(resource);
348 }
349 }
350
351 private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
352 for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) {
353 entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
354 }
355 }
356
357
358
359
360
361
362
363
364 @SuppressWarnings("serial")
365 private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
366
367 private final AnnotationMetadata annotationMetadata;
368
369 private final MethodMetadata factoryMethodMetadata;
370
371 public ConfigurationClassBeanDefinition(ConfigurationClass configClass, MethodMetadata beanMethodMetadata) {
372 this.annotationMetadata = configClass.getMetadata();
373 this.factoryMethodMetadata = beanMethodMetadata;
374 setLenientConstructorResolution(false);
375 }
376
377 public ConfigurationClassBeanDefinition(
378 RootBeanDefinition original, ConfigurationClass configClass, MethodMetadata beanMethodMetadata) {
379 super(original);
380 this.annotationMetadata = configClass.getMetadata();
381 this.factoryMethodMetadata = beanMethodMetadata;
382 }
383
384 private ConfigurationClassBeanDefinition(ConfigurationClassBeanDefinition original) {
385 super(original);
386 this.annotationMetadata = original.annotationMetadata;
387 this.factoryMethodMetadata = original.factoryMethodMetadata;
388 }
389
390 @Override
391 public AnnotationMetadata getMetadata() {
392 return this.annotationMetadata;
393 }
394
395 @Override
396 public MethodMetadata getFactoryMethodMetadata() {
397 return this.factoryMethodMetadata;
398 }
399
400 @Override
401 public boolean isFactoryMethod(Method candidate) {
402 return (super.isFactoryMethod(candidate) && BeanAnnotationHelper.isBeanAnnotated(candidate));
403 }
404
405 @Override
406 public ConfigurationClassBeanDefinition cloneBeanDefinition() {
407 return new ConfigurationClassBeanDefinition(this);
408 }
409 }
410
411
412
413
414
415
416 private static class InvalidConfigurationImportProblem extends Problem {
417
418 public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
419 super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
420 "nor does it declare any @Bean methods; it does not implement ImportSelector " +
421 "or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements " +
422 "or do not attempt to @Import it.", className), new Location(resource, metadata));
423 }
424 }
425
426
427
428
429
430
431 private class TrackedConditionEvaluator {
432
433 private final Map<ConfigurationClass, Boolean> skipped = new HashMap<ConfigurationClass, Boolean>();
434
435 public boolean shouldSkip(ConfigurationClass configClass) {
436 Boolean skip = this.skipped.get(configClass);
437 if (skip == null) {
438 if (configClass.isImported()) {
439 boolean allSkipped = true;
440 for (ConfigurationClass importedBy : configClass.getImportedBy()) {
441 if (!shouldSkip(importedBy)) {
442 allSkipped = false;
443 break;
444 }
445 }
446 if (allSkipped) {
447
448 skip = true;
449 }
450 }
451 if (skip == null) {
452 skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
453 }
454 this.skipped.put(configClass, skip);
455 }
456 return skip;
457 }
458 }
459
460 }