Prevent AutoConfiguration of all included Spring Cloud dependencies - spring-boot

Excluding several AutoConfiguration classes in Spring Boot is easy. Just exclude it from the application:
#SpringBootApplication(exclude = {
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration.class,
org.springframework.cloud.openfeign.FeignAutoConfiguration.class,
})
But how can all AutoConfiguration from the package org.springframework.cloud can be excluded without adding all Spring Clouds AutoConfiguration classes to the exclude? That would be dozens if not hundred entries. And it would not be future proof as with every new Spring Cloud version new AutoConfiguration classes come along.
Using #ComponentScan would only work if #SpringBootApplication respective #EnableAutoConfiguration is not used. But that would remove all the convenient auto configuration capabilities, too.
Is there a way to achieve that programmatically? And how?
Removing Spring Clouds dependencies from the application is unfortunately not a feasible solution.

Why you want to exclude all classes from auto configuration ?
Most of the beans in Autoconfigurations are #conditional , they will not be created if you are providing your own.
Anyway you can look into AutoConfigurationImportSelector . As of now Spring boot allow only class and name for #EnableAutoConfigurtion exclude . if you want to write customize "AutoConfigurationImportSelector" , you can subclass and filter exclude by package after writing your own extended interface for EnableAutoConfiguration to write package method.
As I mentioned this will be the last thing I would like to do . just check #conditional dependency for auto configuration and provide your own beans is better solution.

Related

Exclude Single Bean using #SpringBootApplication

I want to exclude a single Spring bean that's included in the packages scanned by Spring's component scan. I'd like to do this by using some kind of exclude parameter of the #SpringBootApplication annotation. All of Spring Boot's documentation seems to explain how to exclude "auto configuration classes" like LiquibaseAutoConfiguration.class. I don't want to exclude an auto configuration class - I'd just like to exclude a single bean. I'm using Spring Boot 2.7.4. Does anyone know how to do this?

Spring boot auto configuration with dependency and without #ComponentScan

Spring boot provides #ComponentScan to find packages to be scanned.
I am building a library which has #RestControllers inside with package com.mylib.controller. There are other classes as well with stereotype annotations in different packages.
So, if some one is developing SpringBoot Application with com.myapp base package.
He uses my library in his application. He need to mention #ComponentScan("com.mylib") to discover stereotype components of library.
Is there any way to scan components without including library package in #ComponentScan?
As spring-boot-starter-actuator expose its endpoints just with dependency, without defining #ComponentScan. OR any default package which is scanned regardless of application base package.
You could create a Spring Boot Starter in the same style as the Spring Provided Starters. They are essentially a jar'd library with a a spring.factories file pointing to the #Configuration class to load with some other annotations on there to provide overriding/bean back off (#ConditionalOnMissingBean) and generally provide their own #ConfigurationProperties.
Stéphane Nicoll provided an excellent demo of how to build one.
https://github.com/snicoll-demos/hello-service-auto-configuration
It is also documented in the Spring Boot documentation. https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
The library approach would also work but I see no benefit in not making it a starter. Additionally for any library/starter I'd recommend dropping the #ComponentScan and just defining the beans in a #Configuration. This will work for sterotypes like #RestController etc. will function as normal if you create an #Bean out of it in a configuration.
Spring boot starter are special artifacts designed by Spring and used by Spring.
You can check that in the source code that contains mainly a
spring.provides file :
provides: spring-boot-actuator,micrometer-core
I don't know the exact way to process in the same way as Spring Boot Starter but as probably acceptable workaround, you could define in your jar a #Configuration class that specifies #ComponentScan("com.mylib").
#Configuration
#ComponentScan("com.mylib")
public class MyLibConfig {
//...
}
In this way, clients of the jar need "only" to import the #Configuration class :
#Import(MyLibConfig.class)
#Configuration
public class ClientConfig{
//...
}

Why AutoConfigurationPackages not consider #ComponentScan?

Spring Boot's support for Spring data configuration is generally by org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport, and this class use the follow code to determine what packages to scan for repositories:
AutoConfigurationPackages.get(this.beanFactory)
So, basically Spring Data with Spring Boot only scan the package which contains the #EnableAutoConfiguration or #ImportAutoConfiguration, but not consider the #ComponentScan, Is this correct ?
The #ComponentScan annotation is a core Spring Framework feature to search for classes annotated with #Component. Since Spring Data repositories are interfaces (and not annotated), the #ComponentScan annotation won't pick them up.
If you are using Spring Data outside of Spring Boot, you can scan for Spring Data repositories using #EnableJpaRepositories with the basePackages attribute set.
When it comes to Spring Boot, there's usually no need to use either #ComponentScan or #EnableJpaRepositories. If you structure your code as suggested, both components and repositories will be picked up.
To get back to your original question about AbstractRepositoryConfigurationSourceSupport. If you look at the source of #SpringBootApplication you'll see it's annotated with #ComponentScan (to find #Components) and #AutoConfigurationPackage (via #EnableAutoConfiguration). The #AutoConfigurationPackage sets up AutoConfigurationPackages with the value that's later retrieved.
If you want to override the packages the Spring Data searches for repositories (for example in tests) you'll need to use #EnableJpaRepositories to completely override auto-configuration. I usually don't do this, but instead use #DataJpaTest and pick up my main configuration.

How to integration test auto configuration for a custom Spring Boot style starter library?

I am writing a library to provide some functionality that is shared between multiple different Spring Boot applications that I work with.
I would like to do something similar to the auto-configuration that is provided by the many Spring Boot starter libraries exist. That, or some other simple declarative way to integrate my library with the ApplicationContext of the apps using it.
I have found some resources explaining how auto configuration works. I can figure out the above problem.
However, I have not been able to find any good examples of how I can test as part of my library's test suite that it suitably integrates with a Spring Boot application. Ideally, I would start up a simple Spring Boot app written in the library's test directly just for the sake of testing, add the right annotation to it, and be able to assert that the correct beans are then configured.
I have tried creating a TestApplication class that does that and writing integration tests using the SpringBootTest annotation but the TestApplication was never started before my test started.
What can I do to start up a simple app like that solely for the purpose of testing my library? My tests are written with Spock and Spock-Spring in case that changes things versus other test frameworks.
I was able to make it work with the following test class:
#SpringBootTest
#ContextConfiguration(classes = TestApplication)
class DummyIntegrationSpec extends Specification {
#Autowired
DummyService dummyService
void 'dummy service should exist'() {
expect:
dummyService.getMessage() == DummyConfiguration.MESSAGE
}
}
and this TestApplication class at src/test/groovy/com/example/project/TestApplication.groovy
#SpringBootApplication(scanBasePackages = 'com.example.project.config')
#EnableAutoConfiguration
class TestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TestApplication)
}
static void main(String[] args) {
SpringApplication.run(TestApplication, args)
}
}
The two key changes I had to make in order for the TestApplication to start and load the correct context when I moved my TestApplication class from src/main to src/test were:
the TestApplication class needed to be added to the ContextConfiguration annotation
the package that my library's Java config files live in needed to be added to the SpringBootApplication scanBasePackages field
The library auto-configuration does follow a similar structure to the one mentioned in the link tom provided.
Your auto-configuration should be automatically picked while your main spring application/test is starting and all beans will be registered in your context. They will be available for auto-wiring and follow your conditions and init order.
As a summary, make sure you have an auto-configuration annotated by #Configuration class with an #Import that imports your #Configuration annotated configuration classes (inside of them you define beans with methods annotated with #Bean). Also make sure you created a spring.factories file that include your auto-configuration class and that you removed the spring boot maven plugin (for the packaging to be right).
Also, make sure your auto-configuration project is NOT annotated by things like #SpringBootApplication, #EnableAutoConfiguration, #ComponentScan or other spring boot annotations that need to be only in the main spring boot projects (There should be one of them in each stack).
Please also see the article below:
Spring boot is based on a lot of pre-made auto-configuration parent projects. You should already be familiar with spring boot starter projects.
You can easily create your own starter project by doing the following easy steps:
Create some #Configuration classes to define default beans. You should use external properties as much as possible to allow customization and try to use auto-configuration helper annotations like #AutoConfigureBefore, #AutoConfigureAfter, #ConditionalOnBean, #ConditionalOnMissingBean etc. You can find more detailed information on each annotation in the official documentation Condition annotations
Place an auto-configuration file/files that aggregates all of the #Configuration classes.
Create a file named spring.factories and place it in src/main/resources/META-INF.
In spring.factories, set org.springframework.boot.autoconfigure.EnableAutoConfiguration property with comma separated values of your #Configuration classes:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
Using this method you can create your own auto-configuration classes that will be picked by spring-boot. Spring-boot automatically scan all maven/gradle dependencies for a spring.factories file, if it finds one, it adds all #Configuration classes specified in it to its auto-configuration process.
Make sure your auto-configuration starter project does not contain spring boot maven plugin because it will package the project as an executable JAR and won't be loaded by the classpath as intended - spring boot will not be able to find your spring.factories and won't load your configuration

spring-boot without #SpringBootApplication

I am attempting to migrate a spring, non-boot, app to a boot app. The current one builds a war file. Following these instructions, I am walking through the steps to migrate.
I am finding that the #SpringBootApplication annotation forces a lot of things to fail. For instance, it tries to auto config security when I really need the existing xml security config to remain as is. I found that I can override #EnableAutoConfiguration and exclude configuration classes (.i.e. SecurityAutoConfiguration.class). But I am finding it is doing this a great deal for the items I already have on my classpath. I decided it would be better to remove #SpringBootApplication and replace it with just #Configuration, #ComponentScan and #ImportResource to load my original context xml. The class extends SpringBootServletInitializer so that I can register my custom servlets and filters.
What I have found, it now no longer knows to load the application.yml or bootstrap.yml. What triggers auto configuration of these files? Do I fall back to loading with the traditional properties placeholder configurers? I want to avoid this as the next step is to hook it up to spring cloud config to centralize the management of the application configuration.
#SpringBootApplication is a alternative for #Configuration, #EnableAutoConfiguration and #ComponentScan.
Probably you want use #Configuration + #ComponentScan. If you want load xml configuration you can use: #ImportResource annotation.
If you want use autoconfiguration, but you can disable a few auto configurations, eg:
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
Details:
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-configuration-classes.html
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html

Resources