how to create Custom AutoConfiguration at build time with spring native - spring

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 ?

Related

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

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);
}
}

Spring Boot Injection issue using several Profiles

I'm having an issue using Profiles in Spring, basically, we use them to stub some parts of our microservice, which uses both a connection to DB and a connection to another webservice.
Previously I used one stub profile for both the DB and external webservice :
#Configuration
#EnableAutoConfiguration
#Profile("stub")
#ComponentScan(
basePackages = {"com.consumption.backend"},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.consumption.backend.*.persistence.*"),
#ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.consumption.backend.databackendapi.*")
})
public class StubConfiguration {
#Bean
public DataApi consumptionApi() { return new DataStubApi(); }
#Bean
public RefDayDao refDayDao() { return new RefDayInMemoryDao(); }
#Bean
public RefTypeHourDao refTypeHourDao() { return new RefTypeHourInMemoryDao(); }
}
and this works fine, however I would like to separate this stub into two stubs, one for the DB and one for the external webservices to ensure greater flexibility in our tests.
Stub for the DAO:
#Configuration
#EnableAutoConfiguration
#Profile("stubconfv3")
#ComponentScan(
basePackages = {"com.consumption.backend"},
excludeFilters = #ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.consumption.backend.*.persistence.*")
)
public class StubConfV3Configuration {
#Bean
public RefDayDao refDayDao() { return new RefDayInMemoryDao(); }
#Bean
public RefTypeHourDao refTypeHourDao() { return new RefTypeHourInMemoryDao(); }
}
Stub for the external webservice :
#Configuration
#EnableAutoConfiguration
#Profile("stubdatabackend")
#ComponentScan(
basePackages = {"com.consumption.backend"},
excludeFilters = #ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.consumption.backend.databackendapi.*")
)
public class StubDataBackendConfiguration {
#Bean
public DataApi consumptionApi() { return new DataStubApi(); }
}
The stub for the DAO seems to work fine, however I seem to have an issue with the external webservice API not excluding the implementation properly:
If I launch my application with stub profile everything works fine
If I launch my application with stubconfv3 and stubdatabackend I get an issue with injection, as two classes are found :
Parameter 0 of constructor in
com.consumption.backend.service.DataService required a single bean,
but 2 were found:
dataBackendApi: defined in file [C:\code\consumption-backend\databackend-api\target\classes\com\consumption\backend\databackendapi\DataBackendApi.class]
consumptionApi: defined by method 'consumptionApi' in class path resource
[com/consumption/backend/application/configuration/StubDataBackendConfiguration.class]
Action:
Consider marking one of the beans as #Primary, updating the
consumer to accept multiple beans, or using #Qualifier to identify the
bean that should be consumed
Either the exclusion is not working or there is some tricky thing as it seems to find the .class file in target instead of finding it at runtime in a class loader
Most likely, I do some stupid mistake and am not seeing it ...
EDIT : I noticed if I misstype stubconfv3 profile in StubConfV3Configuration, the injection issue doesn't appear anymore, so I guess you can't combine exclusionFilters in #ComponentScan in two different classes...
The first class will do a componentscan on everything but what she excludes, and the second will do the same, so packages excluded by the second class with still be scanned by the first class.
You could try to annotate your DataBackendApi with
#Profile("!stubdatabackend")
to include it when that profile is not used and exclude it otherwise

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.*")

#TestPropertySource is not loading properties

I'm writing integration test for my spring boot application but when I try to override some properties using #TestPropertySource, it's loading the property file defined in the context xml but it's not overriding the properties defined in the annotation.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {DefaultApp.class, MessageITCase.Config.class})
#WebAppConfiguration
#TestPropertySource(properties = {"spring.profiles.active=hornetq", "test.url=http://www.test.com/",
"test.api.key=343krqmekrfdaskfnajk"})
public class MessageITCase {
#Value("${test.url}")
private String testUrl;
#Value("${test.api.key}")
private String testApiKey;
#Test
public void testUrl() throws Exception {
System.out.println("Loaded test url:" + testUrl);
}
#Configuration
#ImportResource("classpath:/META-INF/spring/test-context.xml")
public static class Config {
}
}
I tested this feature with Spring Boot 1.4
Line below works pretty well
#TestPropertySource(properties = { "key=value", "eureka.client.enabled=false" })
Nevertheless new #SpringBootTest annotation is working as well
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "key=value", "eureka.client.enabled=false" }
)
public class NewBootTest {
#Value("${key}")
public String key;
#Test
public void test() {
System.out.println("great " + key);
}
}
I had a similar problem. I fixed it by updating the Spring Context beans.xml to use
org.springframework.context.support.PropertySourcesPlaceholderConfigurer instead of org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.
From the JavaDoc of PropertyPlaceholderConfigurer
Deprecated.
as of 5.2; use org.springframework.context.support.PropertySourcesPlaceholderConfigurer instead which is more flexible through taking advantage of the Environment and PropertySource mechanisms.

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