Merlin's Beard
Merlin's Beard

Reputation: 63

Define Spring beans in library and exclude them from ComponentScan

I'm writing a library for two applications A and B. Application A defines beans with XML (without component scanning) and B uses annotations and component scanning. I want to let the library define some beans that both A and B can include. However, A and B should only include those beans when they are started in a specific Spring profile (let's say my_profile).

The TLDR question is: is there a way to do the above without referencing my_profile in the library and without having to explicitly exclude the library from B's component scan?

Currently, the beans in the library are defined as @Bean methods in a @Configuration class, like this:

@Configuration
open class LibraryConfig {
  @Bean
  open fun libraryBean() = ...
}

Now in application A, in XML, I can do this:

<beans profile="my_profile">
    <bean class="com.package.LibraryConfig" />
</beans>

And that works. In application B, that uses annotations, I would like to do something like this:

@Configuration
@Profile("my_profile")
@Import(LibraryConfig::class)
open class MyProfileConfig

But since LibraryConfig is also annotated with @Configuration, it will always be found by the component scanning. So this approach to make it conditional doesn't work.

Approaches I have considered:

  1. Simply include the profile in a @Profile on the LibraryConfig. I don't want to do this because the library should not be aware of the profiles of its consumers.
  2. Explicitly exclude the LibraryConfig from the @ComponentScan of project B. The drawback here is that we have projects C D and E too, that will not use my_profile. I want to be able to include the library there anyway without having to explicitly exclude a part of it.

So I'm looking for a way to create a 'configuration' class that can only be explicitly included. Or any other elegant way to do the above. Can it be done?

Upvotes: 1

Views: 1405

Answers (1)

Michiel
Michiel

Reputation: 3450

There might be some ways to exclude the library package from project A or B's component scan without explicitly stating that the package is to be excluded.

Use a different package root for your library
The most straightforward option is to use a different package root for the library than the project uses. Since the @SpringBootApplication annotation is meta-annotated with @ComponentScan, if @ComponentScan's attributes basePackageClasses or basePackages have not been specified, the package name of the annotated class is used as root package to use for component scanning (source):

Configures component scanning directives for use with @Configuration classes. Provides support parallel with Spring XML's context:component-scan element. Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.

For instance, if a @SpringBootApplication annotated root class is located in package com.example.somepackage:


package com.example.somepackage;

@SpringBootApplication
public class DemoApplication {
    ...
}

The base package root com.example.somepackage will be used as the root for component scanning. Using com.example.library instead of com.example.somepackage.library should exclude the library from being included in the component scanning.

Make use of ConditionalOn annotations in the Library
If the method above doesn't work and the configuration is still automatically included during component scanning, a slightly less invasive way when compared to using profiles is adding @ConditionalOnX annotations to your configuration class, such as:

ConditionalOnProperty

@Configuration
@ConditionalOnProperty(prefix = "yourlibrary", name = "enabled", havingValue="true")

The projects relying on the library will now need to include the property yourlibrary.enabled=true.

More info on the several @ConditionalOn annotations can be found here.

Upvotes: 1

Related Questions