How can I exclude a specific #Configuration class from my Spring Boot application? - spring

Spring Boot 2.3.12 (I can't update to a newer version for reasons out of my control).
I have defined my main application class with specific scan base packages like this:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
}
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
What I'm trying to accomplish is both:
A) include a package outside the application's base package (hence the org.otherpackage.ComponentScanMarker.class reference in the #SpringBootApplication annotation)
and
B) exclude the HateoasConfiguration class completely*.
I've also tried this:
#SpringBootApplication
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
That results in HateoasConfiguration being loaded despite the excludeFilters.
Another option I tried:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = HateoasConfiguration.class
)
That results in an exception at startup with the message:
The following classes could not be excluded because they are not auto-configuration classes:
- org.springframework.hateoas.config.HateoasConfiguration
I can't get it to work, no matter what combination of annotation properties I try. Either HateoasConfiguration gets loaded despite the attempt to exclude it, or #Components in org.otherpackage don't get loaded. I've looked at a few different similar questions and answers, but none of them include the need for both goals.
How can I accomplish both needs, to include multiple base packages for component scanning, and exclude a specific #Configuration class that's on the classpath?
* This question really has nothing to do with Spring HATEOAS, it's just an example of a #Configuration class that is on the classpath but I want Spring Boot to ignore. Here are the annotations present on that class (source code here):
#Configuration(proxyBeanMethods = false)
#EnablePluginRegistries({ LinkDiscoverer.class })
public class HateoasConfiguration {

Have you tried this ?
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = { HateoasConfiguration.class }
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}

I've been looking for a solution for a similar problem myself. Dropping it here for future developers.
The underlying problem is that #SpringBootApplication performs a full component scan itself, so your own #ComponentScan annotation does not have the desired effect.
The solution for me was to eject #SpringBootApplication and just do what it does internally (with our desired modifications) as it's just a convenience shorthand.
For your case, try:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
})
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}

Related

how to create Custom AutoConfiguration at build time with spring native

I'm using spring-native to create a native-image, i've created a custom AutoConfiguration class as follow :
#AutoConfiguration
#ConditionalOnClass(value = ControllersConfigurer.class)
#Import(value = DefaultBeans.class)
#EnableTransactionManagement
public class CoreAutoConfiguration {
#PostConstruct
public void postConstruct() {
LogFactory.getLog(getClass())
.info("AutoConfiguration is enabled, the application is automatically configured with "
+ getClass().getSimpleName());
} }
#Configuration
#ComponentScan(basePackages = { "com" }, excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = BeanConfig.class) })
class DefaultBeans { }
at build time when i run the mvn package command it does not detect my custom class at beanDefinition stage when executing spring-aot-maven-plugin:0.12.0:generate (generate) goal,
Is there any additional configuration needed to detect it at build time ?

Spring boot - scan packages

I am starting to write an application in spring boot and below is how my package structure looks:
com.practice.spring.project.helloworld.HelloworldApplication.java
com.practice.spring.project.repository.EmployeeRepository.java
com.practice.spring.project.model.Employee.java
Below is how i had my application startup successfully,
#SpringBootApplication
#ComponentScan(basePackages = "com.practice.spring.project.DB", basePackageClasses = InitDatabase.class)
#EnableJpaRepositories(basePackages = "com.practice.spring.project.repository" , basePackageClasses = EmployeeRepository.class)
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
#Bean
public CommandLineRunner run(EmployeeRepository employeeRepository) throws Exception {
return (args) -> {
System.out.println("Calling it after the application context is all loaded up");
employeeRepository.save(new Employee("Ashwin", "Architect"));
};
}
}
My question is should I have to specify the base-packages & baseClasses for every class I add ? It would be tough if have 10 packages having 10 different classes.
Am sure there should be an easier way to scan and instantiate classes in different package.
Figured out a way - set the basePackages to com.practice.spring.project.*
#ComponentScan(basePackages = "com.practice.spring.project.*")

How to write #SpringBootTest for app with #ComponentScan and JPA repositories

I find it extremely hard to write #SpringBootTest if you use #ComponentScan and Jpa repositories. Can someone advice? This should be super-trivial stuff, but it's not documented anywhere.
#SpringBootApplication
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
basePackageClasses = {Main.class, Other.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
and one of discovered Configuration classes has:
#Configuration
#EnableJpaRepositories("jpa")
Now I want to create test, which will enable ideally just a subset of JPA repositories AND exactly nothing else, unless I tell so. Namely, no configuration from production source code. This seems to be close to impossible to express. This is where I was able to get:
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestIT.TestConfig.class)
public class TestIT {
#Configuration
#EnableJpaRepositories("jpa")
#AutoConfigureDataJpa
#AutoConfigurationPackage
public static class TestConfig {
//here will be beans for test.
}
so this configuration produces error
java.lang.IllegalArgumentException: Not a managed type ...my jpa repository class
which probably means, that jpa package isn't among autoconfigured packages. No idea how to add it if it's even possible.
OK, another approach. Some sources recommends this:
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestIT.TestConfig.class)
public class TestIT {
EnableJpaRepositories("jpa")
#EntityScan(basePackages = "jpa.entities")
//#TestPropertySource("classpath:application.properties")
#EnableTransactionManagement
public static class TestConfig {
//here will be beans for test.
}
but this one fails with caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
Any hints?
First of all, there is no specific need to add #ComponentScan in your main application startup file. #SpringBootApplication is sufficient.
Now regarding the test cases:
I use powermockito all my test cases usually look like this:-
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PrepareForTest({ ContextProvider.class, ConfigurationUtils.class, Utils.class })
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestIT {
IN the clause #PrepareForTest mention all the classes which u would mention with the annotation #Mock.
for e.g.
No need to mention any of the repo(DAO layer interfaces where we write our queries) interfaces. So ur repo declaration would be like:
private SoftwareRepo softwareRepo;
And u would use it in while execution is
softwareInventoryRepo = PowerMockito.mock(SoftwareRepo.class);
OK, after a lot of searching (in vain) and even more trying, I think I have answer.
To recap:
you have main class (thanks #Bhushan Shinde):
#SpringBootApplication(scanBasePackageClasses = {Main.class, Other.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
and some configuration:
#Configuration
#EnableJpaRepositories("jpa")
public class Config
So to use SpringBootTest and configure everything from scratch for test, ignoring production configurations, you go:
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = TestIT.TestConfig.class)
//#Import(TestIT.TestConfig.class)
public class TestIT {
#Configuration
#AutoConfigureDataJpa
#EnableJpaRepositories("jpa") //fake package names, obviously
#EntityScan(basePackages = "jpa.entities")
// #TestPropertySource("classpath:application.properties")
#EnableTransactionManagement
public static class TestConfig {
//test related beans & config.
}
//tests here.
}
maybe there is something extra here, but after day of googling & trying this is good enough for me.

How to reuse my configurations and exclude another one in SpringBoot Test?

How can I reuse my main configurations but exclude some..?
Like:
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class,
ConfigOne.class,
ConfigTwo.class,
ConfigThree.class //I want to exclude the ConfigThree in my tests and use my own ConfigThreeTest.class
).run(args);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyTests {
//test...
}
#Configuration
public class ConfigThreeTest {
//config...
}
In the sample above I want to exclude ConfigThree.class and use ConfigThreeTest.class
In your MyTests class, you can specify which class(es) to be used for the configuration.
#SpringBootTest(classes = {ConfigOne.class, ConfigTwo.class},webEnvironment = WebEnvironment.RANDOM_PORT)
Try using this
#SpringBootApplication(exclude = {demo.class})

Spring Boot: NoUniqueBeanDefinitionException between test and main

I have a SpringBoot main/Application.java class
#SpringBootApplication
#ComponentScan(value = "com.nfl.dm.shield", excludeFilters =
{
#ComponentScan.Filter(value = MemoryRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class Application {
final static Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
LOG.info("Booting application...");
SpringApplication.run(Application.class, args);
}
}
and a similar one for Test
#Configuration
#ComponentScan(basePackages = {"com.nfl.dm.shield"}, excludeFilters =
{
#ComponentScan.Filter(value = MySqlRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class ApplicationTestConfig {
}
The main code runs correctly. The test code throws NoUniqueBeanDefinitionException, appearing to not properly filter out the unwanted MySqlRepository component.
After over a day of trying many different ways to exclude the unwanted bean, the core issue turned out to be the fact that #ComponentScan was pulling in both Application and ApplicationTest, resulting in an additional scan for Application, resulting in the unwanted service being loaded.
The solution, add:
#ComponentScan.Filter(value = Application.class, type = FilterType.ASSIGNABLE_TYPE)
to the list in ApplicationTestConfig.java. So, when ApplicationTestConfig is loaded and triggers the component scan, it ignores Application (and all of Application's specific configurations).

Resources