Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow @Configuration classes to self-@ComponentScan [SPR-8808] #13450

Closed
spring-projects-issues opened this issue Oct 31, 2011 · 5 comments
Closed

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Oct 31, 2011

Tomasz Nurkiewicz opened SPR-8808 and commented

I am using AnnotationConfigWebApplicationContext and XML-free testing support with @ContextConfiguration(classes=...). When pointed @Configuration class contains @ComponentScan with a package containing that class:

package com.example.foo;

@Configuration
@ComponentScan(basePackages = "com.example.foo")
public class Config { }

I get unexpected exception:

org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'config' for bean class [com.example.foo.Config] conflicts with existing, non-compatible bean definition of same name and class [com.example.foo.Config]

While I understand why it happens, IMHO Spring should handle this case and simply ignore @Configuration found in scanned package if it has already been provided in AnnotationConfigWebApplicationContext.

Test case attached. On branch master you'll find a test case failing, branch fix fixes the problem by simply @Config to another package.


Affects: 3.1 RC1

Attachments:

Issue Links:

  • #13521 Default @ComponentScan's exclude filter to exclude @Configuration annotated classes ("is duplicated by")
  • #12956 Allow recursive use of @ComponentScan

Referenced from: commits ca4eafd, 6991cd9

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 3, 2011

Chris Beams commented

You can simply add

@ComponentScan(excludeFilters = @Filter(Configuration.class))

To avoid this. We are considering making this arrangement the default for excludeFilters as a number of users have already run into it.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 3, 2011

Chris Beams commented

Updated issue type and title to reflect previous comment.

Considering the following implementation to fix:

$ git diff org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
diff --git org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java org.springframework.context/src/main/java/org/springframework/conte
index 2b0b7d0..86c6eb4 100644
--- org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
+++ org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
@@ -121,7 +121,7 @@ public @interface ComponentScan {
         * Specifies which types are not eligible for component scanning.
         * @see #resourcePattern()
         */
-       Filter[] excludeFilters() default {};
+       Filter[] excludeFilters() default { @Filter(Configuration.class) };
 
 
        /**
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 8, 2011

Tomasz Nurkiewicz commented

Thanks for your answer and suggested solution. I was aware of @Filter(Configuration.class) workaround. However I can still think of some problematic situations:

Imagine I add @Configuration com.example.foo.SecondaryConfig class. I expect it to be picked up because it is located in com.example.foo (see Config definition above). However excluding all @Configuration classes by default would exclude it as well. Of course I can reference all @Configuration classes explicitly (e.g. via AnnotationConfigWebApplicationContext#register) but it defeats the purpose of component scanning. Test case included (testcase3.zip, exclude-filters branch).

So in this particular case I would again recommend excluding only explicitly referenced @Configuration classes in AnnotationConfig*ApplicationContext rather than all of them as the least unexpected behaviour. Also changing the default excludeFilters introduces minor backward incompatibility.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 28, 2011

Chris Beams commented

Complete, just in time for RC2:

commit 51eb9fe9bdec0d9761dffbc0c021c51a3a60ba32
Author: Chris Beams <cbeams@vmware.com>
Date:   Mon Nov 28 21:35:38 2011 +0000

    Allow @Configuration classes to self-@ComponentScan
    
    Prior to this change, a @Configuration classes that @ComponentScan
    themselves would result in a ConflictingBeanDefinitionException.
    
    For example:
    
        package com.foo.config;
    
        @Configuration
        @ComponentScan("com.foo");
        public class AppConfig {
            // ...
        }
    
    This resulted in a ConflictingBeanDefinitionException that users have
    typically worked around in the following fashion:
    
        package com.foo.config;
    
        @Configuration
        @ComponentScan(basePackages="com.foo",
            excludeFilters=@Filter(value=ANNOTATION_TYPE, type=Configuration.class);
        public class AppConfig {
            // ...
        }
    
    This is obviously more verbose and cumbersome than would be desirable,
    and furthermore potentially too constraining as it prohibits the ability
    to include other legitimate @Configuration classes via scanning.
    
    The exception was being thrown because of a logic problem in
    ClassPathBeanDefinitionScanner.  The bean definition for AppConfig gets
    registered once by the user (e.g. when constructing an
    AnnotationConfigApplicationContext), then again when performing the
    component scan for 'com.foo'. Prior to this change,
    ClassPathBeanDefinitionScanner's #isCompatible returned false if the new
    bean definition was anything other than an AnnotatedBeanDefinition.  The
    intention of this check is really to see whether the new bean definition
    is a *scanned* bean definition, i.e. the result of a component-scanning
    operation.  If so, then it becomes safe to assume that the original bean
    definition is the one that should be kept, as it is the one explicitly
    registered by the user.
    
    Therefore, the fix is as simple as narrowing the instanceof check from
    AnnotatedBeanDefinition to its ScannedGenericBeanDefinition subtype.
    
    Note that this commit partially reverts changes introduced in SPR-8307
    that explicitly caught ConflictingBeanDefinitionExceptions when
    processing recursive @ComponentScan definitions, and rethrew as a
    "CircularComponentScanException.  With the changes in this commit,
    such CBDEs will no longer occur, obviating the need for this check and
    for this custom exception type altogether.
    
    Issue: SPR-8808, SPR-8307
    
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 13, 2014

Max Alexejev commented

Are there any reasons why this one wan rolled back eventually?

In 4.0.6.RELEASE, it is again
Filter[] excludeFilters() default {};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.