Skip to content

Commit e8dead2

Browse files
committed
@EnableTransactionManagement and co get detected on superclasses as well
Issue: SPR-10864
1 parent b0b40da commit e8dead2

File tree

4 files changed

+39
-28
lines changed

4 files changed

+39
-28
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ final class ConfigurationClass {
6161
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
6262
new LinkedHashMap<String, Class<? extends BeanDefinitionReader>>();
6363

64-
private final Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars =
65-
new LinkedHashSet<ImportBeanDefinitionRegistrar>();
64+
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
65+
new LinkedHashMap<ImportBeanDefinitionRegistrar, AnnotationMetadata>();
66+
6667

6768
/**
6869
* Create a new {@link ConfigurationClass} with the given name.
@@ -175,12 +176,12 @@ public void addImportedResource(String importedResource, Class<? extends BeanDef
175176
this.importedResources.put(importedResource, readerClass);
176177
}
177178

178-
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar) {
179-
this.importBeanDefinitionRegistrars.add(registrar);
179+
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
180+
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
180181
}
181182

182-
public Set<ImportBeanDefinitionRegistrar> getImportBeanDefinitionRegistrars() {
183-
return Collections.unmodifiableSet(this.importBeanDefinitionRegistrars);
183+
public Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> getImportBeanDefinitionRegistrars() {
184+
return this.importBeanDefinitionRegistrars;
184185
}
185186

186187
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configC
136136
loadBeanDefinitionsForBeanMethod(beanMethod);
137137
}
138138
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
139-
loadBeanDefinitionsFromRegistrars(configClass.getMetadata(), configClass.getImportBeanDefinitionRegistrars());
139+
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
140140
}
141141

142142
private void removeBeanDefinition(ConfigurationClass configClass) {
@@ -314,11 +314,9 @@ private void loadBeanDefinitionsFromImportedResources(
314314
}
315315
}
316316

317-
private void loadBeanDefinitionsFromRegistrars(AnnotationMetadata importingClassMetadata,
318-
Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars) {
319-
320-
for (ImportBeanDefinitionRegistrar registrar : importBeanDefinitionRegistrars) {
321-
registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
317+
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
318+
for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) {
319+
entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
322320
}
323321
}
324322

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,7 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi
223223

224224
// process any @PropertySource annotations
225225
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
226-
sourceClass.getMetadata(), PropertySources.class,
227-
org.springframework.context.annotation.PropertySource.class)) {
226+
sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {
228227
processPropertySource(propertySource);
229228
}
230229

@@ -246,7 +245,7 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi
246245
}
247246

248247
// process any @Import annotations
249-
processImports(configClass, getImports(sourceClass), true);
248+
processImports(configClass, sourceClass, getImports(sourceClass), true);
250249

251250
// process any @ImportResource annotations
252251
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
@@ -378,7 +377,7 @@ private void processDeferredImportSelectors() {
378377
try {
379378
ConfigurationClass configClass = deferredImport.getConfigurationClass();
380379
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
381-
processImports(configClass, asSourceClasses(imports), false);
380+
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
382381
}
383382
catch (Exception ex) {
384383
throw new BeanDefinitionStoreException("Failed to load bean class: ", ex);
@@ -387,20 +386,19 @@ private void processDeferredImportSelectors() {
387386
this.deferredImportSelectors.clear();
388387
}
389388

390-
private void processImports(ConfigurationClass configClass, Collection<SourceClass> sourceClasses, boolean checkForCircularImports)
391-
throws IOException {
389+
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
390+
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
392391

393-
if (sourceClasses.isEmpty()) {
392+
if (importCandidates.isEmpty()) {
394393
return;
395394
}
396395
if (checkForCircularImports && this.importStack.contains(configClass)) {
397396
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
398397
}
399398
else {
400399
this.importStack.push(configClass);
401-
AnnotationMetadata importingClassMetadata = configClass.getMetadata();
402400
try {
403-
for (SourceClass candidate : sourceClasses) {
401+
for (SourceClass candidate : importCandidates) {
404402
if (candidate.isAssignable(ImportSelector.class)) {
405403
// the candidate class is an ImportSelector -> delegate to it to determine imports
406404
Class<?> candidateClass = candidate.loadClass();
@@ -410,21 +408,21 @@ private void processImports(ConfigurationClass configClass, Collection<SourceCla
410408
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
411409
}
412410
else {
413-
String[] importClassNames = selector.selectImports(importingClassMetadata);
411+
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
414412
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
415-
processImports(configClass, importSourceClasses, false);
413+
processImports(configClass, currentSourceClass, importSourceClasses, false);
416414
}
417415
}
418416
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
419417
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
420418
Class<?> candidateClass = candidate.loadClass();
421419
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
422420
invokeAwareMethods(registrar);
423-
configClass.addImportBeanDefinitionRegistrar(registrar);
421+
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
424422
}
425423
else {
426424
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
427-
this.importStack.registerImport(importingClassMetadata, candidate.getMetadata().getClassName());
425+
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
428426
processConfigurationClass(candidate.asConfigClass(configClass));
429427
}
430428
}
@@ -485,8 +483,7 @@ public List<PropertySource<?>> getPropertySources() {
485483
return propertySources;
486484
}
487485

488-
private PropertySource<?> collatePropertySources(String name,
489-
List<PropertySource<?>> propertySources) {
486+
private PropertySource<?> collatePropertySources(String name, List<PropertySource<?>> propertySources) {
490487
if (propertySources.size() == 1) {
491488
return propertySources.get(0);
492489
}
@@ -558,7 +555,6 @@ public SourceClass asSourceClass(String className) throws IOException, ClassNotF
558555
interface ImportRegistry {
559556

560557
AnnotationMetadata getImportingClassFor(String importedClass);
561-
562558
}
563559

564560

spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ public void transactionProxyIsCreated() {
5252
assertThat("Stereotype annotation not visible", services.containsKey("testBean"), is(true));
5353
}
5454

55+
@Test
56+
public void transactionProxyIsCreatedWithEnableOnSuperclass() {
57+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
58+
ctx.register(InheritedEnableTxConfig.class, TxManagerConfig.class);
59+
ctx.refresh();
60+
TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class);
61+
assertThat("testBean is not a proxy", AopUtils.isAopProxy(bean), is(true));
62+
Map<?,?> services = ctx.getBeansWithAnnotation(Service.class);
63+
assertThat("Stereotype annotation not visible", services.containsKey("testBean"), is(true));
64+
}
65+
5566
@Test
5667
public void txManagerIsResolvedOnInvocationOfTransactionalMethod() {
5768
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -97,6 +108,11 @@ static class EnableTxConfig {
97108
}
98109

99110

111+
@Configuration
112+
static class InheritedEnableTxConfig extends EnableTxConfig {
113+
}
114+
115+
100116
@Configuration
101117
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
102118
static class EnableAspectJTxConfig {

0 commit comments

Comments
 (0)