#ComponentScan in application class breaks #WebMvcTest and #SpringBootTest - spring-boot

I am creating tests with #WebMvcTest annotation and found that if I have a #ComponentScan annotation in the application class it will break the expected behavior of the tests.
According to the WebMvcTest javadoc:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans)."
The problem is that with #ComponentScan it is instantiating beans annotated with #Service. If instead of #ComponentScan I specify the scan base packages in the #SpringBootApplication annotation everything works as expected.
Another problem happens when I specify the controller class in the #WebMvcTest annotation. When there is a #ComponentScan annotation in the application class it will load all controllers instead of loading only the specified one.
Is this a bug in Spring Boot?
I want to use #ComponentScan because of the excludeFilters attribute which is not available in the #SpringBootApplication annotation.
A workaround I have found is to create a separate class with #Configuration annotation and move the #ComponentScan there.

Found the reason for this odd behavior.
This is the declaration of the #SpringBootApplication annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public #interface SpringBootApplication {
As you can see the #ComponentScan annotation inside the #SpringBootApplication has the excludedFilters attribute specified.
When I added the #ComponentScan annotation directly in my application class I did not specify the default excludedFilters and that is why the behavior was different.

Related

Autowiring configuration class spring

I'm wondering whether it's possible to autowire a class annotated with #Configuration into any annotated class (#Component, #SpringBootTest, etc)? I know it's possible between #Configuration classes, but I can't find a straight answer on if it's possible outside #Configuration classes (both test and non-test classes). Sample code to illustrate what I mean below:
#Configuration
public class Config {
//Anything in here
}
//any non-Configuration annotation
public class Autowired {
#Autowire
Config config; //like this
}
If you are looking at the source code of #Configuration annotation you can see it's annotated itself with #Component, making annotated classes with it also Spring components, so making them available for injection.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Configuration {
Yes. you can autowire configuration class as #configuration annotation contains #component

Can Spring Boot #Import annotation be used without #Configuration tag

#Import annotation in Spring is used in order to group configurations.
I know that the standard syntax for this annotation looks like this:
#Configuration
#Import({ Manager.class, Programmer.class })
class WorkerConfiguration {
}
But I am wondering is it possible to use #Import annotation to import a group of those annotations outside of a configuration file(maybe in the main file).
Example:
#SpringBootApplication
#Import({ Manager.class, Programmer.class })
public class App{
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Import Javadoc :
Indicates one or more component classes to import — typically
#Configuration classes.
#Import is often used in the context of a class annotated with #Configuration class to include some declared beans in #Configuration in another one. But it also works with composite annotations that contains among other annotations the #Configuration one.
And in Spring Boot it turns out that several annotations include #Configuration:
For example #SpringBootApplication that you ask to is also composed (among other things) of a #Configuration annotation :
Indicates a configuration class that declares one or more #Bean
methods and also triggers auto-configuration and component scanning.
This is a convenience annotation that is equivalent to declaring
#Configuration, #EnableAutoConfiguration and #ComponentScan.
So yes what you want to do is valid.

#EnableAutoConfiguration annotation with class prameter is not initializing properties object

i have following #CongfigurationProperties class
//#Component
#ConfigurationProperties
#PropertySource("classpath:typesofcharge.properties")
public class ChargeProperties {
private HashMap<String,String> charge=new HashMap<>();
public HashMap<String,String> getCharge()
{
return this.charge;
}
}
And this is my Configuration file
#SpringBootApplication
#ComponentScan({"com.vehiclemanagement.config,com.vehiclemanagement.client,"
+ "com.vehiclemanagement.controller,"
+ "com.vehiclemanagement.exception,"
+ "com.vehiclemanagement.model,"
+ "com.vehiclemanagement.service"})
#EnableConfigurationProperties(ChargeProperties.class)
public class VehicleManagementConfig {
public static void main(String[] args) {
SpringApplication.run(VehicleManagementConfig.class, args);
}
}
If i use #Component annotation in ChargeProperties and remove ChargeProperties.class annotation in Configuration class the charge HashMap is initialized properly
If i remove #Component and pass ChargeProperties.class as argument like this
#EnableConfigurationProperties(ChargeProperties.class) like how document says the charge HashMap is empty when i run
I am using spring boot 2.0.2 release .But i am following latest docs. Can anyone explain why this are not working as document suggest
content of property file is as follows
UPDATE the content of property files are as shown
#DO NOT MODIFY THIS FILE
charge.peak=Double_rate;
charge.lateNight=duration_based_charge;
charge.earlyMorning=special_offers;
When specifying ChargeProperies.class on the #EnableConfigurationProperties annotation it will be registered as a bean through the EnableConfigurationPropertiesImportSelector class inside #EnableConfigurationProperties.
So in the example, if you have only annotated the ChargeProperties class with #ConfigurationProperties it will create a chargeProperties bean with an empty charge HashMap because it defaulted back to application.properties as the source.
A custom source can be specified by using #PropertySource.
#PropertySource annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
As per documentation above, to use #PropertySource to load the custom source, one has to use the #Configuration annotation.
#Configuration
#PropertySource("classpath:typesofcharge.properties")
Under the hood a #Configuration class is a #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
So to your question. By specifying a custom #PropertySource without #Configuration, spring did not load the properties in the #PropertySource annotation and defaulted back to the application.properties.
If we use #PropertySource we have to use component otherwise the properties will not be read
since we added the #ComponentScan We don't have to mention #EnableConfiguationProperties annotation at all The propety class object can be autowired as Bean

Spring-boot application-test.properties

I am trying to unit test the spring-boot application using junit. I have placed the application-test.properties under src/test/resources. I have a ApplicationConfiguration Class which reads the application.properties.
My test class looks like this
#RunWith(SpringRunner.class)
#SpringBootTest(classes=ApplicationConfiguration.class)
#TestPropertySource(locations = "classpath:application-test.properties")
#ActiveProfiles("test")
public class TestBuilders {
#Autowired
private ApplicationConfiguration properties;
When I try to read the properties, it is always null.
My ApplicationConfiguration Class looks something like this
#Configuration
#ConfigurationProperties
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource(value="file:config.properties", ignoreResourceNotFound =
true)})
public class ApplicationConfiguration{
private xxxxx;
private yyyyy;
I tried all possible ways that I found on google.. No luck. Please help!
Thanks in Advance.
The issue is you don't have #EnableConfigurationProperties on your test class.
When you load the application it start from main class(one which has #SpringBootApplication) where you might have #EnableConfigurationProperties and hence it works when you start the application.
Whereas when you are running the Test with only ApplicationConfiguration class as you have specified here
#SpringBootTest(classes=ApplicationConfiguration.class)
Spring doesn't know that it has to Enable Configuration Properties and hence the fields are not injecting and hence null. But spring is reading your application-test.properties file. This can be confirmed by just injecting the value directly in your test class
#Value("${xxxxx}")
private String xxxxx;
Here the value is injected. But to inject into a class with ConfigurationProperties you need to enable it using #EnableConfigurationProperties
Put #EnableConfigurationProperties on your test class and everythhing works fine.

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

Resources