Spring : annotations for #Configuration class - java

I'm new to SpringBoot, and I don't quite understand for example this class:
#Configuration
#EnableConfigurationProperties(...)
#ComponentScan(basePackages = {...})
#Import(SomeClass.class)
public class MyConfig {
#Bean
public IFactory myFactory(IService myService){
return new myFactory(myService);
}
...
}
Why we are using #ComponentScan here ? The moment Spring detects this configuration it should include everything annotated with #Bean inside no ?
What does the #Import do here ? Why should be import a class rather than annotate it inside with #Bean ?
Is there any relation bewteen #Bean and the classes under #ComponentScan ?

#ComponentScan scans for other #Configuration/#Component/#Service/etc. annotated classes, under e.g. the basePackages you specify. It has nothing to do with the #Beans inside your configuration.
#Import explicitly imports another #Configuration, with all its #Bean definitions.
Having methods annotated with #Bean and #ComponentScan (searching for Java classes that are annotated with e.g. #Component) are two different ways of achieving the same thing: registering #Bean definitions/creating #Beans.

Related

What is the difference between #ComponentScans and #ComponentScan?

I see that we have #org.springframework.context.annotation.ComponentScans and #org.springframework.context.annotation.ComponentScan.
How do we use #ComponentScans() ?
How is #ComponentScans() different from #ComponentScan({"com.org.abc", "com.org.xyz"})
Spring can automatically scan a package for beans if component
scanning is enabled.
#ComponentScan configures which packages to scan for classes with
annotation configuration. We can specify the base package names
directly with one of the basePackages or value arguments (value is an
alias for basePackages)
#Configuration
#ComponentScan(basePackages = "com.baeldung.annotations")
class VehicleFactoryConfig {}
Also, we can point to classes in the base packages with the
basePackageClasses argument:
#Configuration
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
Both arguments are arrays so that we can provide multiple packages for
each.
If no argument is specified, the scanning happens from the same
package where the #ComponentScan annotated class is present.
#ComponentScan leverages the Java 8 repeating annotations feature,
which means we can mark a class with it multiple times:
#Configuration
#ComponentScan(basePackages = "com.baeldung.annotations")
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
Alternatively, we can use #ComponentScans to specify multiple
#ComponentScan configurations:
#Configuration
#ComponentScans({
#ComponentScan(basePackages = "com.baeldung.annotations"),
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
})
class VehicleFactoryConfig {}
You can found more Spring Bean Annotations
Take a look at documentation:
ComponentScans Container annotation that aggregates several
ComponentScan annotations.
ComponentScan Configures component scanning directives for use
with #Configuration classes.

What difference does #EnableConfigurationProperties make if a bean is already annotated with #ConfigurationProperties?

The Spring Boot documentation says that to use the #ConfigurationProperties annotation
You also need to list the properties classes to register in the
#EnableConfigurationProperties annotation, as shown in the following
example:
and gives this code:
#Configuration
#EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
But in the very next paragraph says:
Even if the preceding configuration creates a regular bean for
AcmeProperties, we recommend that #ConfigurationProperties only deal
with the environment and, in particular, does not inject other beans
from the context. Having said that, the #EnableConfigurationProperties
annotation is also automatically applied to your project so that any
existing bean annotated with #ConfigurationProperties is configured
from the Environment.
Suggesting that listing a #ConfigurationProperties bean under an #EnableConfigurationProperties annotation is not necessary.
So which is it? Experimentally, I've seen that if I annotate a bean with #ConfigurationProperties it gets properties injected to it as expected without needing to list it in #EnableConfigurationProperties, but if this is the case then why list anything that has a #ConfigurationProperties annotation under #EnableConfigurationProperties, as is shown in the documentation? Does it make any kind of difference?
As M. Deinum referred #EnableConfigurationProperties Is for enabling support of #ConfigurationProperties. If you take a look to the annotation Java Doc you can see:
Enable support for ConfigurationProperties annotated beans. ConfigurationProperties beans can be registered in the standard way (for example using Bean #Bean methods) or, for convenience, can be specified directly on this annotation. [...]
For example, let's say you have a class whose responsibility is to read and store information from your application.yml / application.properties that is required to make a connection to different databases. You annotate it with #ConfigurationProperties.
Then, you typically have a #Configuration annotated class that provides a DataSource #Bean to your application. You can use the #EnableConfigurationProperties to link it to the #ConfigurationProperties class and init your data sources accordingly.
Here is a small example:
application.yml
data-sources:
db1:
url: "jdbc:postgresql://localhost:5432}/db1"
username: test
password: test
db2:
url: "jdbc:postgresql://localhost:5432}/db2"
username: test
password: test
DataSourcesConfiguration
#ConfigurationProperties
public class DataSourcesConfiguration {
private Map<String, BasicDataSource> dataSources;
public void setDataSources(Map<String, BasicDataSource> dataSources) {
this.dataSources = dataSources;
}
Map<String, BasicDataSource > getDataSources() {
return dataSources;
}
}
DataSourceConnectionConfiguration
#Configuration
#EnableConfigurationProperties(DataSourcesConfiguration.class)
public class DatabaseConnectionConfiguration implements Provider<Connection> {
private DataSourcesConfiguration dataSourcesConfiguration;
public DatabaseConnectionConfiguration(DataSourcesConfiguration dataSourcesConfiguration) {
this.dataSourcesConfiguration = dataSourcesConfiguration;
}
#Bean
public DataSource dataSource() {
// Use dataSourcesConfiguration to create application data source. E.g., a AbstractRoutingDataSource..
}
}
It took me a while to reach to this post but would like to add here so that others may get benefited.
#ConfigurationProperties - Used to bind a class with an externalized property file. Very powerful and must be used to separate out bean classes with configuration entity class.
#Configuration - Creates a Spring bean of configuration stereotype.
#EnableConfigurationProperties - Creates a binding between a configuration entity class and Spring configuration stereotype so that after injection within a service properties can be retrieved easily.
If we look at the code below:
#Configuration #EnableConfigurationProperties #ConfigurationProperties(prefix="ar1")
public class ar1Settings { }
#Configuration tells Spring to treat this as a configuration class and register it as a Bean
#EnableConfigurationProperties tells Spring to treat this class as a consumer of application.yml/properties values
#ConfigurationProperties tells Spring what section this class represents.
My understanding is that if you don't need to specify the section of the property file, then #ConfigurationProperties can be omitted.
#EnableConfigurationProperties imports EnableConfigurationPropertiesRegistrar which enables support for #ConfigurationProperties annotated beans.
#ConfigurationProperties is an annotation for externalized configuration, it is to be applied to a bean configuration class or method annotated with #Bean eg
#ConfigurationProperties(prefix = "some-prefix")
public SomePrefix prefixBean() {
return new SomePrefix();
}
To load the properties and bind them to properties within the method or the class that match the prefix.
ps: some-prefix binds to SomePrefix because of spring's support for Relaxed binding.
Before springboot 2.2, You could do either of the following:
#Configuration
#ConfigurationProperties(prefix = "some-prefix")
public class ConfigProperties {
//...some code
}
or
#Configuration
#EnableConfigurationProperties(SomeClassToBeBounded.class)
public class ConfigProperties {
along with
#ConfigurationProperties(prefix = "some-prefix")
public class SomeClassToBeBounded{
//...some code
}
From springboot 2.2
You can do it in a much easier way:
#ConfigurationProperties(prefix = "some-prefix")
#ConfigurationPropertiesScan
public class ConfigProperties {
//...some code
}
With this, the classpath scanner enabled by #SpringBootApplication finds the ConfigProperties class, even though we didn't annotate this class with #Component.

How to exclude #Configuration class from component-scan in testContext.xml

I have a class AppConfig annotated with #Configuration which is having various bean definitions which includes beans which performs a third party hit from the application. now in my spring integration test cases, I do not want these beans to be initialized. that is where I created an another bean with name TestAppConfig annotated with #Configuration where I have mocked all those beans which performs third party hit. now in my testContext.xml I am adding an exclude filter to context:component-scan where I am specifying the AppConfig package to be excluded. but somehow this AppConfig is being initialized everytime. I have tried proper regex, but still things are not working. if anyone know the cause then please share.
After see the comment that you are using spring 3.2, you can see here for older version of spring to use #Profile
You can use #Profile annotation to determine which #Bean will be created or not.
Example:
Defined Beans are below
#Bean
#Profile("production")
public DataSource prodDataSource() {
...
return dataSource;
}
#Bean
#Profile("development")
public DataSource devDataSource() {
...
return dataSource;
}
For profile called "development"
In app.properties spring.profiles.active=development
prodDataSource will not be called. But devDataSource will be called
For profile called "production"
In app.properties spring.profiles.active=production
prodDataSource will not be called. But devDataSource will be called

Why is a bean declared in #SpringBootApplication class registered even though it is not a stereotyped class?

I have this main class in my project
#SpringBootApplication
#EnableOAuth2Sso
public class App
{
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
#Bean public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
}
As far as I know, component scan scans beans in stereotyped classes which are one of #Component, #Service, #Repository, #Controller if I am not wrong.
From spring docs
By default, classes annotated with #Component, #Repository, #Service,
#Controller, or a custom annotation that itself is annotated with
#Component are the only detected candidate components.
I cannot understand how the bean in this class is registered. As it is not a stereotyped class and no annotation is annotated with #Component it shouldn't be scanned in the first place but this code works perfectly. In fact for my use case having the bean in this class was the only way my problem was solved, but that is a different thing. Can anyone please explain this. Thanks !!
#SpringBootApplication is a meta annotation which looks like:
// Some details omitted
#SpringBootConfiguration
#EnableAutoConfiguration
public #interface SpringBootApplication { ... }
#SpringBootConfiguration is also a meta annotation:
// Other annotations
#Configuration
public #interface SpringBootConfiguration { ... }
And #Configuration is:
// Other annotations
#Component
public #interface Configuration { ... }
It works Since:
By default, classes annotated with #Component, #Repository, #Service,
#Controller, or a custom annotation that itself is annotated with
#Component are the only detected candidate components.
This is because #SpringBootApplication acts also as a #Configuration annotation.
#Configuration is used to create define beans as in xml spring configurarion files.
You can have a bean configuration class.
#Configuration
class MyConfiguration{
#bean MyBean myBean(){...};
}
o you can have a Spring configuration file
<beans>
<bean id="myBean" class="MyBean" />
</beans>
In your case you're unsing Spring configuration class as you're using #SpringBootApplication
You can see more here
http://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/SpringBootApplication.html
http://docs.spring.io/spring/docs/4.2.1.RELEASE/javadoc-api/org/springframework/context/annotation/Configuration.html

exclude #Component from #ComponentScan

I have a component that I want to exclude from a #ComponentScan in a particular #Configuration:
#Component("foo") class Foo {
...
}
Otherwise, it seems to clash with some other class in my project. I don't fully understand the collision, but if I comment out the #Component annotation, things work like I want them to. But other projects that rely on this library expect this class to be managed by Spring, so I want to skip it only in my project.
I tried using #ComponentScan.Filter:
#Configuration
#EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
but it doesn't appear to work. If I try using FilterType.ASSIGNABLE_TYPE, I get a strange error about being unable to load some seemingly random class:
Caused by: java.io.FileNotFoundException: class path resource [junit/framework/TestCase.class] cannot be opened because it does not exist
I also tried using type=FilterType.CUSTOM as following:
class ExcludeFooFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClass() == Foo.class;
}
}
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.CUSTOM, value=ExcludeFooFilter.class)})
public class MySpringConfiguration {}
But that doesn't seem to exclude the component from the scan like I want.
How do I exclude it?
The configuration seem alright, except that you should use excludeFilters instead of excludes:
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Using explicit types in scan filters is ugly for me. I believe more elegant approach is to create own marker annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface IgnoreDuringScan {
}
Mark component that should be excluded with it:
#Component("foo")
#IgnoreDuringScan
class Foo {
...
}
And exclude this annotation from your component scan:
#ComponentScan(excludeFilters = #Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
Another approach is to use new conditional annotations.
Since plain Spring 4 you can use #Conditional annotation:
#Component("foo")
#Conditional(FooCondition.class)
class Foo {
...
}
and define conditional logic for registering Foo component:
public class FooCondition implements Condition{
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// return [your conditional logic]
}
}
Conditional logic can be based on context, because you have access to bean factory. For Example when "Bar" component is not registered as bean:
return !context.getBeanFactory().containsBean(Bar.class.getSimpleName());
With Spring Boot (should be used for EVERY new Spring project), you can use these conditional annotations:
#ConditionalOnBean
#ConditionalOnClass
#ConditionalOnExpression
#ConditionalOnJava
#ConditionalOnMissingBean
#ConditionalOnMissingClass
#ConditionalOnNotWebApplication
#ConditionalOnProperty
#ConditionalOnResource
#ConditionalOnWebApplication
You can avoid Condition class creation this way. Refer to Spring Boot docs for more detail.
In case you need to define two or more excludeFilters criteria, you have to use the array.
For instances in this section of code I want to exclude all the classes in the org.xxx.yyy package and another specific class, MyClassToExclude
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
I had an issue when using #Configuration, #EnableAutoConfiguration and #ComponentScan while trying to exclude specific configuration classes, the thing is it didn't work!
Eventually I solved the problem by using #SpringBootApplication, which according to Spring documentation does the same functionality as the three above in one annotation.
Another Tip is to try first without refining your package scan (without the basePackages filter).
#SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
In case of excluding test component or test configuration, Spring Boot 1.4 introduced new testing annotations #TestComponent and #TestConfiguration.
I needed to exclude an auditing #Aspect #Component from the app context but only for a few test classes. I ended up using #Profile("audit") on the aspect class; including the profile for normal operations but excluding it (don't put it in #ActiveProfiles) on the specific test classes.
Your custom filer has wrong logic.
Because metadataReader.getClass() is always not equal to Foo.class, metadataReader.getClass() return "org.springframework.core.type.classreading.MetadataReader". It'd use className for comparation.
public class CustomFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClassMetadata().getClassName().equals(Foo.class.getName());
}
}
#ComponentScan.Filter is belong every #ComponentScan and only filter compononets from the specific scan path.
If you find it doesn't work, you could check whether there's another #ComponentScan or <context:component-scan base-package="xxx"/> in your code

Categories

Resources