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

Can't component scan for @Component and exclude @Controller (a.k.a., excludeFilters should take precedence over includeFilters) [SPR-9795] #14428

Closed
spring-projects-issues opened this issue Sep 13, 2012 · 8 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Nick Williams opened SPR-9795 and commented

Like many, I wanted my services, repositories and the like managed by the application context and my controllers by the framework servlet. So, for my application context configuration class I used the following annotation:

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=true, includeFilters={
        @ComponentScan.Filter(Aspect.class)
}, excludeFilters={
        @ComponentScan.Filter(Controller.class)
})

And for my framework servlet configuration I used:

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=false, includeFilters={
        @ComponentScan.Filter(Controller.class)
})

However, my controllers were getting instantiated twice: once in the application context and once in the framework servlet. So I tried the following iterations of my application context config class annotation:

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=false, includeFilters={
        @ComponentScan.Filter(Aspect.class), @ComponentScan.Filter(Component.class),
        @ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class)
})
@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=false, includeFilters={
        @ComponentScan.Filter(Aspect.class), @ComponentScan.Filter(Component.class),
        @ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class)
}, excludeFilters={
        @ComponentScan.Filter(Controller.class)
})

Neither of those worked. Controllers were instantiated twice. I figured this is because the @Controller annotation is itself annotated with @Component. So, with that, THIS worked:

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=false, includeFilters={
        @ComponentScan.Filter(Aspect.class), @ComponentScan.Filter(Repository.class),
        @ComponentScan.Filter(Service.class)
})

However, that doesn't achieve what I want, because I want classes marked @Component to be instantiated, I just don't want classes marked @Controller to be instantiated.

In short, it seems to me that if I explicitly say "exclude classes matching these filters from component scanning," that should take precedence over all else. Especially important: if I say "use default filters" and then provide exclusions, those exclusions should NOT be ignored.

I do not know whether this same problem exists with XML configuration. Presumable it does, but I'm not in a position to test that at the moment.


Affects: 3.1.2

0 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

Thanks Nick, will take a look at this. In the meantime, feel free to submit a simple reproduction project.

@spring-projects-issues
Copy link
Collaborator Author

Nick Williams commented

By the way, since I reported this I have confirmed that this problem does not exist in the XML equivalent of the configuration above (forgive any misspellings, these were transcribed, not copied and pasted):

<context:component-scan base-package="com.package" use-default-filters="true">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" />
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context>
<context:component-scan base-package="com.package" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context>

So, the above works just as expected. The application context instantiates @Services, @Repositories, @Components and @Aspects, but it does NOT instantiate @Controllers. Then the servlet context instantiates @Controllers. This is the correct behavior (from my perspective, i.e., this was the INTENDED behavior). However, it differs from the @Configuration behavior. IMO, these behaviors should be identical.

This should work the same as my applicationContext.xml above (it does not):

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=true, includeFilters={
        @ComponentScan.Filter(Aspect.class)
}, excludeFilters={
        @ComponentScan.Filter(Controller.class)
})

And this should work the same as my servletContext.xml above (it does):

@ComponentScan(basePackageClasses={
        com.package.Marker.class
}, useDefaultFilters=false, includeFilters={
        @ComponentScan.Filter(Controller.class)
})

@spring-projects-issues
Copy link
Collaborator Author

Phil Webb commented

Hi Nick,

After a bit of digging I think this might not actually be a bug. From your notes it looks like your two @Configuration classes are both contained in the same package. What I think is happening is that your top configuration (the one the filters @Controllers) is scanning your base package for all @Components. Because @Configuration classes are meta-annotated with @Component the scanning actually finds and processes your second configuration, bringing in the @Controller classes.

Try adding an additional exclude filter to prevent the second configuration class from being found. If you don't want any @Configuration classes to be scanned you can use the following:

Application:

@ComponentScan(
  basePackageClasses={com.package.Marker.class}, 
  useDefaultFilters=true, 
  includeFilters={@Filter(Aspect.class)},
  excludeFilters={@Filter(Controller.class, Configuration.class)})

Servlet:

@ComponentScan(
  basePackageClasses={com.package.Marker.class},
  useDefaultFilters=false, 
  includeFilters={@Filter(Controller.class)
})

I have created a repro project to show the fix:
spring-attic/spring-framework-issues@a1519b6

Cheers,
Phil.

@spring-projects-issues
Copy link
Collaborator Author

Nick Williams commented

Phil, this is not correct. Here is my actual project structure for the concerned classes:

-> com
    -> companyname
        -> config
            -> ApplicationContext.java
            -> ServletContext.java
        -> product
            -> health
                -> Marker.java

The actual basePackageClasses value is com.companyname.product.health.Marker.class. This is not in the same package as the config classes. Therefore, this is not the cause of the issue.

@spring-projects-issues
Copy link
Collaborator Author

Nick Williams commented

(Sorry that the comment was improperly formatted before. I have resolved this.)

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 22, 2013

Phil Webb commented

Hi Nick,

Thanks for the update, I thought I was onto the root cause there!

I am having some trouble replicating the issue, could you take a look at #14428 in https://github.com/SpringSource/spring-framework-issues to see if you can see any obvious differences between that project and your code.

Cheers,
Phil.

@spring-projects-issues
Copy link
Collaborator Author

Nick Williams commented

OMG ... I'm so sorry I wasted your time, Phil (and my time). I just spent the last two hours slowly building a reproduction project and bit by bit I couldn't get it to reproduce, up until the point that I had replicated the entire project and still couldn't get it to reproduce. I couldn't figure out what the @$#! was going on, and then I realized...

The Marker.java was in the wrong package.

...

I can't tell you how many months I have looked at that folder structure and never realized it was in the wrong package. I KNEW it was supposed to be in .product, I KNEW putting it above .product (and, thus, above .config) could cause problems, and I KNEW that I had put it in .product. But I hadn't. Either that or somebody else on the team moved it. Either way, it was above .product and .config, thus causing so many problems that I hadn't even realized.

sigh

Sorry

@spring-projects-issues
Copy link
Collaborator Author

Phil Webb commented

No worries. We've all been there!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants