How to exclude bean from loading? - java

I have a problem integrating external library with Spring. It contains a class annotated with #Configuration and it has a method annotated with #Bean. I don't want it to be instantiated (it's not needed and introduces dependency on a Spring Boot, which I don't use.
Unfortunately this #Configuration-annotated class is used elsewhere in the library (required by the class type, not interface type, so I need to instantiate exactly this class).
I exluded it's package from auto-scanning, I'm not importing it directly. Just constructing it by hand and registering in own configuration as a bean.
So, to make story short - I need to register a bean, but exclude it from annotation scnanning (to not process it's #Bean-annotated methods). Any way for doing this?

If you mean you have library in your maven, gradle, then you can exclude spring beans from being initialized. I found exclude #Component from #ComponentScan
#Configuration
#EnableSpringConfigured
#ComponentScan(
basePackages = {"com.example"},
excludeFilters = {
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = Foo.class)})
public class MySpringConfiguration {
}
And if you do not want to include transitive dependencies, thus you do not want your dependency to add other dependencies it uses, you have to exclude them from your dependncy (in maven, gradle).

Related

Spring AutoConfiguration manul import all components

I have the following autoconfigure class.
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#Import({LoggerController.class, LogService.class, LogConfig.class})
public class ProviderAutoConfiguration {
}
I need to import all the components, services, configurations using #import annotation, otherwise for example, when I remove LogService.class (LogController autowires LogService) from #Import parameters, it throws the following error
Consider defining a bean of type 'com.log.LogService' in your configuration.
Do I really have to include all of them manuelly as shown above, or is there any other way to automatically detect classes annotated Service, Component or Configuration?
!This is starter library, not a executable app!
The problem with your "library" is that it already needs a lot of moving parts, like Spring Data JPA, properly setup JPA. If that isn't available it will fail, or if you add #EnableJpaRepositories and/or things like #ComponentScan and #EntityScan it will (quite severely) interfere with the (auto)configuration of Spring Boot or the clients. Which is what you want to prevent.
Instead of all those annotations what you need to do is
Conditionally enable #EnableJpaRepositories or just include the dependency and let the auto-configuration kick in (I suggest the latter)
Don't add #ComponentScan, #EntityScan etc. instead use #AutoConfigurationPackage which is designed for starters.
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#AutoConfigurationPackage
public class ProviderAutoConfiguration {
}
NOTE: This assumes that the ProviderAutoConfiguration is in the same or a higher package then your other classes. If not specify the basePackages in the #AutoConfigurationPackage.
You can use #ComponentScan to specify the packages you want to scan to find configurations and/or components to load.
Assuming you are using Spring Boot, at least so it seems according to your tags, you should take a look at #SpringBootApplication.
#SpringBootApplication encapsulates #Configuration, #EnableAutoConfiguration, and #ComponentScan annotations with their default attributes. The default value for #ComponentScan means that all the sub packages on the package the #ComponentScan is used are scanned. That is why it is usually a good practice to include the main class in the base package of the project.
If your components, services, configurations are under com.log as direct class or in sub-package, you can use #ComponentScan(basePackages="com.log.*")
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#ComponentScan(basePackages="com.log.*")
public class ProviderAutoConfiguration {
}

bean name conflict while running the spring boot project

Background: I am working in a project named com.x.myproject. I have the dependency of two other packages com.x.document and com.x.library. both packages have the same class with name QueueHelper.
Now, In my project, I have to scan one other package com.x.security which internally scans com.x, something like that:
#SpringBootApplication
#ComponentScan(basePackages = {"com.x"})
#EnableCaching
public class Security {
.......
}
in com.x.myproject
#SpringBootApplication
#ComponentScan(basePackages = {"com.x.myproject","com.x.security"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.REGEX, pattern="com.x.document.*"),
#ComponentScan.Filter(type=FilterType.REGEX, pattern="com.x.library.*")})
public class MyProject{
.......
}
It all works fine when I use excludefilters in com.x.security but I want to use it in com.x.myproject
The exception which I got is
Annotation-specified bean name 'queueHelper' for bean class [com.x.library.utils.QueueHelper] conflicts with existing, non-compatible bean definition of same name and class [com.x.document.utils.QueueHelper]
Three answers come to my mind:
Give a different names to com.x.library.utils.QueueHelper and com.x.document.utils.QueueHelper via the #Component annotation. Spring will use the simple class name by default for naming the beans. You can annotate one with #Component('libraryQueueHelper') and the other with #Component('documentQueueHelper'). However, now you'll have to give a #Qualifier(<name>) in each place you're autowiring these beans.
Exclude them in your module, like you do in your question and then change their names using #Bean annotated methods within a #Configuration. When using in the third module, you'll need to use a #Qualifier to autowire the correct bean.
Rename the classes. This is the best solution in this case, but since you asked this question, I guess it's not viable.

Is it possible to disable an autoconfiguration class from another autoconfiguration class in spring boot?

I'm working on a data access library and would like to be able to include it as a dependency in other projects with minimal configuration (ideally just autowire a repo). In particular, this library sets itself up using an autoconfiguration class (enabled in spring.factories) and needs to disable other autoconfiguration classes to work (DataSourceAutoConfiguration and HibernateJpaAutoConfiguration).
Is it possible to do this outside of the dependent project?
To make configuration as simple as possible I'd like to avoid putting excludes in the dependent project's #SpringBootApplication annotation or its spring.autoconfigure.exclude property.
Update:
On my #Configuration I have tried adding the annotations:
#EnableAutoConfiguration(exclude={
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
this causes
IllegalStateException: Configuration problem: A circular #Import has
been detected
and
#ImportAutoConfiguration(exclude={
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
Which simply does nothing.
There's a very handy interface called AutoConfigurationImportFilter, which decides on which auto-configuration should be loaded. This is also how #ConditionalOnClass annotation works.
In your library, add the following class:
public class MyExclusionFilter
implements AutoConfigurationImportFilter {
private static final Set<String> SHOULD_SKIP = new HashSet<>(
Arrays.asList("org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration",
"org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration"));
#Override
public boolean[] match(String[] classNames, AutoConfigurationMetadata metadata) {
boolean[] matches = new boolean[classNames.length];
for(int i = 0; i< classNames.length; i++) {
matches[i] = !SHOULD_SKIP.contains(classNames[i]);
}
return matches;
}
}
This class needs to be registered in spring.factories to work. Add the following line in the library into META-INF/spring.factories:
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=com.mycompany.db.MyExclusionFilter
You don't need to make any changes to the dependent project. Just add the library as a dependency and the auto-configuration you specified won't be loaded for the whole application.
NOTE: You can add more than one import filter, only the auto-configuration classes not filtered in all import filters will get loaded.
For details, see the source code of org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter and org.springframework.boot.autoconfigure.condition.OnClassCondition.java classes.
you can exclude then via
#SpringBootApplication(exclude= {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}))
but you could exclude them in your #Configuration by adding
#EnableAutoConfiguration(exclude= {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})

Spring dependency injection - best design pattern of configuration

Right now i have a inherited project that is using annotation based spring dependency injection. So all classes are simply marked with #Component (or specific stereoTypes like #service, #Repository,#RestController, etc). This makes it a little hard to find where the dependency is located and i was thinking to change it so that each package has its own dependency configuration and then add each package to the #ComponentScan afterwards.
So for example if i had a package called com.mycoolpackage.login and mycoolpackage.networking
then i'd have a Spring configuration like this in first package:
#Configuration
public class LoginDIConfig {
#Bean
public LoginServiceImpl loginServiceImpl() {
return new LoginServiceImpl();
}
}
and in the second package i'd have the following:
#Configuration
public class NetworkDIConfig {
#Bean
public NetworkServiceImpl networkServiceImpl() {
return new NetworkServiceImpl();
}
}
and my#ComponentScan would look like this:
#ComponentScan(basePackages = {"com.mycoolpackage.login","com.mycoolpackage.network"})
So i have two questions about this approach.
How can i use a #Service annotation instead of bean here
Do you think this design is more easier as it tells you what your package dependencies are very easily instead of hunting them
down.
If you want to configure some been properties manually then you should go for above configuration else you should stick with exiting one.
This makes it a little hard to find where the dependency is located
#Autowire Or #Inject annotation will always lead you to dependency class.

Should I annotate configuration class as #Configuration for testing?

I spent some time resolving problem with missing org.joda.time.DateTime->java.util.Date converter in Spring Data (which should be enabled by default when Joda-Time is on a classpath). I have found a reason, but it generated a question about #Configuration annotation in Spring.
Standard application config using AbstractMongoConfiguration from spring-data-mongodb:
#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
A test which explicit uses AppConfig class (with Spock, but internally mechanisms provided by spring-test are used):
#ContextConfiguration(classes = AppConfig)
class JodaDocRepositorySpec extends Specification {
#Autowired
private JodaDocRepository jodaDocRepository
def "save document with DateTime"() {
given:
def jodaDoc = new JodaDoc(DateTime.now())
when:
def savedJodaDoc = jodaDocRepository.save(jodaDoc)
then:
savedJodaDoc.id
}
}
It works fine. But when #Configuration annotation in AppConfig is removed/commented:
//#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
the test fails with:
org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type org.joda.time.DateTime to type java.util.Date
AFAIK it is not needed to use #Configuration for the configuration class when it is explicit registered in the context (by classes in #ContextConfiguration or a register() method in AnnotationConfigWebApplicationContext). The classes are processed anyway and all declared beans are found. It is sometimes useful to not use #Configuration to prevent detecting by a component scan when there are 2 similar configuration classes in the same packages in a test context used by different tests.
Therefor I think it could a bug in Spring which causes to different internal beans processing in the context depending on an usage or not a #Configuration annotation. I compared Spring logs from these two cases and there are some differences, but I'm not able to determine what are they caused by in the Spring internal classes. Before a bug submission I would like to ask:
My question. Is there an explicable reason why Spring for the same configuration class (pointed explicit in #ContextConfiguration) uses (or not) converters for Joda-Time depending on an existence of a #Configuration annotation?
I created also a quickstart project reproducing the issue. spring-data-mongodb 1.3.3, spring 4.0.0, joda-time 2.3.
It's everything OK in this behaviour. AbstractMongoConfiguration is annotated by #Configuration, but in fact this annotation is not #Inherited, so you have to explicitly annotate your class.
When you remove #Configuration annotation then your AppConfig class is not a full configuration. It's processes as a lite configuration just because it contains methods annotated by #Bean - please refer to methods in org.springframework.context.annotation.ConfigurationClassUtils
isFullConfigurationCandidate()
isLiteConfigurationCandidate()
isFullConfigurationClass()
Finally only full (annotated by #Configuration) configuration classes are processes and enhanced by configuration post processors - look at ConfigurationClassPostProcessor.enhanceConfigurationClasses()

Categories

Resources