How to get #Configuration files in Spring to run - spring

I'm writing a micro service using dependency injection, and I have a Java class annotated with #Configuration which creates all my beans. However, my autowired fields are not detecting the beans. How do I get my #Configuration class to run before the application starts?
I tried annotating the classes with the Autowired fields as #ContextConfiguration(classes = Config.class), but this didn't work.
My spring configuration file:
#Configuration
public class Config {
#Bean
public AmazonDynamoDB amazonDynamoDB() {
return ...
}
#Bean
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
return ...
}
}
I expect the Configuration file to be run and the beans injected, but the beans are not being detected.
There's no main method, since I'm writing this in a service which is created using dependency injection in another service. I'm not sure where I'd tell my application to use my Config file.

probably place a #EnableConfigurationProperties( {Config.class}) above your #SpringBootApplication main class.

Related

How to disable #Configuration initialization in WebFluxTest?

I would like to write tests for reactive controller using #WebFluxTest annotation, mocking all dependencies.
#WebFluxTest(controllers = MyController.class)
public class MyControllerTest {
#MockBean
SomeService service;
#Autowired
WebTestClient webClient;
//some tests
}
From what I understand, the WebFluxTest annotation shall apply only configuration relevant to WebFlux tests (i.e. #Controller, #ControllerAdvice, etc.), but not another beans.
My spring boot app contains a number of #Configuration classes that configure a number of beans (annotated as #Bean). Some of those configurations have also dependencies (autowired by constructor).
#Configuration
#RequiredArgsConstructor
public class MyConfig {
private final AnotherConfig anotherConfig;
#Bean
//...
}
When I run my web flux tests, I can see the context initialization contains an attempt to initialize the MyConfig (and it fails because of the missing dependency which comes from 3rd party auto-configured lib). How can I configure the test to skip initialization of all of these?
I am able to exclude the problematic configuration class only by excluding auto configuration of the whole app.
#WebFluxTest(controllers = MyController.class, excludeAutoConfiguration = {MyApplication.class})
public class MyControllerTest { ... }
where MyApplication is the spring boot app autoscanning those configuration classes.
But how can I achieve to skip initialization of MyConfig only? Or even better, how can I achieve to only include a list of configurations to be initialized?
Add
#ActiveProfiles("YOUR_ENV_OTHER_THAN_TEST")
below or above #Configuration
For multiple environments..
#ActiveProfiles(profiles ={env1, env2,env3})

when using functional bean registration, is there a way to inform Spring that ClassA is responsible for creating an instance of BeanA?

I've switched a portion of a Spring app to use functional bean registrations. The motivation for the switch is due to requiring multiple instances of some beans under certain conditions. It also turns out to be much more concise (which won't be at all apparent with the simplistic examples below).
The code used to look like this (simple example):
#Configuration
public class ConfigA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
#Service
public class Service1 {
#Autowired BeanA beanA;
...
}
#Service
public class Service2 {
#Autowired BeanA beanA;
...
}
I've switched the configuration class to look like this:
#Configuration
public class ConfigA implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
context.registerBean("beanA", BeanA.class, () -> new BeanA());
}
}
The issue I'm now encountering is that Spring is complaining about autowired beans not being found. With the original code, Spring could determine that a BeanA bean was declared via ConfigA and would create that bean before initializing the services.
With the new code, I guess there is no way for Spring to determine where the BeanA bean(s) are being declared, and so it tries to init the services before the BeanA is initialized (which causes the app to not start).
I was hoping that Spring would prioritize #Configuration classes over #Service or #Controller classes, but that doesn't seem to be the case.
I could annotate all the services with #DependsOn("configA"), but there are many services that autowire BeanA (some in other code bases), so the #DependsOn option isn't really realistic.
Question: When using functional bean registration, is there a way to inform Spring that ConfigA is responsible for creating an instance of BeanA?
In order to use the functional style of bean registration and enable autowiring mechanism in other beans you can do the following:
Remove #Configuration annotation from your ConfigA class;
Create directory named META-INF under main/java/resources and create a file named spring.factories under the newly created directory;
Fill the newly created file with the line org.springframework.context.ApplicationContextInitializer=(package-name-to-configA-class).ConfigA
Now Spring should be able to successfully autowire bean named BeanA where requested.

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

What is the difference between #Configuration and #Component in Spring?

#ComponentScan creates beans using both #Configuration and #Component. Both these annotations work fine when swapped. What is the difference then?
#Configuration Indicates that a class declares one or more #Bean
methods and may be processed by the Spring container to generate bean
definitions and service requests for those beans at runtime
#Component Indicates that an annotated class is a "component". Such
classes are considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning.
#Configuration is meta-annotated with #Component, therefore
#Configuration classes are candidates for component scanning
You can see more here:
http://docs.spring.io/spring-framework/docs/4.0.4.RELEASE/javadoc-api/org/springframework/context/annotation/Configuration.html
A #Configuration is also a #Component, but a #Component cannot act like a #Configuration.
Actually answer is not complete, is it true that:
#Component Indicates that an annotated class is a "component". Such
classes are considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning.
But you do can create i.e MyConfiguration.java class then stereotype with #Component and add #Beans declaration to it. In this way it will looks as a configuration, main difference is that when annotated class with #Configuration #Bean annotated methods are proxy using CGLIB which made in code calls after the first one to return bean from context instead of execute method again and create another instance as happens when using #Component with #Bean
There is a very subtle difference between them. Let me provide a very quick outlook to this.
Consider the below scenario:
#Configuration
public class MyConfig {
#Bean
public ServiceA aService(){
return new ServiceA();
}
#Bean
public ServiceB bService(){
return new ServiceB(aService());
}
}
Note that ServiceB bean has a dependecy on ServiceA and this is not autowired. Instead, the way it's written implies that a new instance is created, which is not actually created by Spring. You, the programmer, did it with the new keyword instead.
So, if we do use #Configuration, then it uses CGLIB proxying, and in this situation it creates a singleton bean managed by the Spring context. If you invoke it multiple times, it returns the same bean that was created by Spring - sort of autowiring effect.
Whereas if you use #Component, it won't do this proxying and will simply return a new instance every time the method is invoked, instead of providing the Spring managed instance. (Remember that a Spring bean is something that is managed by the Spring container, and, as a developer, it's your job is to pull them in, e.g. with #Autowired.
The same #Component effect can be achieved with #Configuration(proxyEnabled= false) (This is also referred to as bean light mode processing). So, in light mode, you would end up doing something like this:
#Configuration(proxyEnabled = false) // Lite mode, same effect as #Component
public class MyConfig {
#Bean
public ServiceA aService() {
return new ServiceA();
}
#Autowired
#Bean
public ServiceB bService(ServiceA aServiceBean){
return new ServiceB(aServiceBean);
}
}
Refer here for a more elaborate explanation
Hope that helps! Happy Coding!
#Configuration - It is like beans.xml but Java-based bean configuration. It means class annotated with this annotation is the place where beans are configured and will be a candidate for auto-detection. In this class, methods are annotated with #Bean which return an object of the class.
Example:
#Configuration
public class ConfigClass {
#Bean
public UserClass getObject() {
return new UserClass();
}
}
#Component - You cannot autowire (#Autowired) any class if it is not marked with #Component. It means when you want to autowire any class using annotation that class should be annotated with #Component.
Example:
#Component
public class A { .... }
public class B {
#Autowired
A a;
.....
.....
}
Spring Document for reference:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
#Component is imported by default with #Configuration. controllers, service, and repostory are children components (along with Configuration). They are also candidate for auto-detection.
I am extending on #reus's answer.
#Configuration Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
If you look at the #Configuration class, you will see that it is meta-annotated with #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
#Bean is enables us to define the dependency in any way we like, this is why the #Bean annotation goes above a methods and we manually create a bean object and return it from that method. #Component enables us to define a dependency quickly, that is why #Component goes above classes. We only inject it wherever we need.
Collectively these 3 points says that- to quickly define a bean, we can annotate the class with #Component. To define a bean as we like (support custom requirements), we can write the bean definition using #Bean inside a #Configuration annotated class.
Apart from the differences highlighted by reos.
The reason why #Configuration cannot be replaced by #Component is as below:
The difference is in how the inter bean dependency is handled.
Refer the link for a detailed explanation with example:
Difference between Configuration and Component

Modify the bean created in main application context during Integration test

In my springboot application I am performing Integration tests using the following class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AccountLoadApplication.class,
loader = SpringApplicationContextLoader.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest {
AccountLoadApplication.class is a spring boot main class and the actual application has a bean defined like below:
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), new SFTPRemoteFileService());
}
Also I have a Test Configuration class like below
#Configuration
public class AtddTestConfig {
#Bean
public ResourceLoader mvsFileResourceLoader() {
ResourceLoader recapMvsFileResourceLoader =
new RemoteFileResourceLoader(remoteHostProperties(), new FakeSFTPRemoteFileService());
return recapMvsFileResourceLoader;
}
My Idea is that I want to override the bean created in the main application using the new bean defined in the test Configuration file.
But during integration tests the main application bean is considered instead of the bean defined in the test application context?
Is There any other way to achieve what i am trying to achieve ?
Additional Info:
Here are the beans defined in my Application configuration class
#Bean
public RemoteFileService remoteFileService() {
return new SFTPRemoteFileService();
}
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), remoteFileService());
}
Here are the beans defined in my Test configuration class
#Bean
#Profile("local")
#Primary
public RemoteFileService remoteFileService() {
return new FakeSFTPRemoteFileService();
}
Still the production bean is only created instead of this primary bean.
Use #Profile annotation to enable testing bean only in test context
Use #Primary annotation on testing bean, so that spring would use test bean instead of production one.
Here is my Github repository with working example using this mechanism.
Maybe when you add your test configuration as parameter for #ContextConfiguration it resolves problem, e.g.
#ContextConfiguration(classes = {AccountLoadApplication.class, AtddTestConfig.class},
loader = SpringApplicationContextLoader.class)
Along with the other changes suggested by #luboskrnac, you have to declare #ActiveProfiles; otherwise, your local profile is simply ignored.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("local")
#SpringApplicationConfiguration(AccountLoadApplication.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest { /* ... */ }
Note that the above assumes that your AtddTestConfig class gets picked up via component scanning by your AccountLoadApplication class.

Resources