Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional ComponentScan on package

In a Spring Boot Application I have a package with Application class like

@SpringBootApplication
class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application);
        application.run(args);
    }

}

which automatically has ComponentScan set up from that class's package by default. Then I have several subpackages each containing several component and service beans (using annotations). But for the purpose of reuse of this application for different use cases, I need to enable/disable all the components in some of the subpackages, preferably by a property.

That is I have subpackages like

org.example.app.provider.provider1
org.example.app.provider.provider2

Now, based on some property I would like to enable (scan for) beans in one of the packages, e.g.

provider1.enabled=true

I thought I could make ConditionalOnProperty on Configuration class work like that, but the problem is, that the beans are picked up by the default @SpringBootApplication component scan (i.e. the subpackage Configuration class does not override the top level one)

So I thought I would exclude the packages, but this adds more work (and knowledge) when a new provider package is needed (need to know in advance to add an explicit exclude for that package).

Is there any other way how to do this I can't figure out?

like image 782
redhead Avatar asked Aug 26 '16 13:08

redhead


People also ask

What is @configuration @EnableAutoConfiguration and @ComponentScan?

@ComponentScan - to enable component scanning, all the packages and subpackages will be auto-scanned which are under the root package on which @SpringBootApplication is applied. @EnableAutoConfiguration - to enable auto-configuration of the. classes bases on the jars added in classpath.

Why ComponentScan is needed?

@ComponentScan tells Spring in which packages you have annotated classes which should be managed by Spring. Spring needs to know which packages contain spring beans, otherwise you would have to register each bean individually in(xml file). This is the use of @ComponentScan.

What is the difference between @component and ComponentScan?

@Component and @ComponentScan are for different purposes. @Component indicates that a class might be a candidate for creating a bean. It's like putting a hand up. @ComponentScan is searching packages for Components.

What is @ComponentScan?

The @ComponentScan annotation is used with the @Configuration annotation to tell Spring the packages to scan for annotated components. @ComponentScan also used to specify base packages and base package classes using thebasePackageClasses or basePackages attributes of @ComponentScan.


1 Answers

Load the provider components conditionally

A Spring Configuration annotated with a @ConditionalOnProperty would do just that:

@Configuration
@ComponentScan("org.example.app.provider.provider1")
@ConditionalOnProperty(name = "provider1.enabled", havingValue = "true")
public class Provider1Configuration {
}

@Configuration
@ComponentScan("org.example.app.provider.provider2")
@ConditionalOnProperty(name = "provider2.enabled", havingValue = "true")
public class Provider2Configuration {
}

Then exclude the components under org.example.app.provider.*

Now, all you need is to exclude the providers from Spring Boot Application (and let the CondtionalOnProperty do its work). You can either:

  • (1) move the provider packages so that they are not below the Spring Boot Application

For example, if the Spring Boot main is in org.example.app, keep the @Configuration in org.example.app but the providers in org.example.providers

  • (2) Or exclude the provider package from Spring Boot (assuming the @Configuration are in org.example.app for example):

SpringBootMain.java:

@SpringBootApplication
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "org.example.app.provider.*"))
class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application);
        application.run(args);
    }
}
like image 61
alexbt Avatar answered Oct 28 '22 00:10

alexbt