Following the question Understanding Spring #Autowired usage I wanted to create a complete knowledge base for the other option of spring wiring, the #Configuration class.
Let's assume I have a spring XML file that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="another-application-context.xml"/>
<bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
<constructor-arg value="${some.interesting.property}" />
</bean>
<bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
<constructor-arg ref="someBean"/>
<constructor-arg ref="beanFromSomewhereElse"/>
</bean>
</beans>
How can I use #Configuration instead? Does it have any affect on the code itself?
Migrating XML to #Configuration
It is possible to migrate the xml to a #Configuration in a few steps:
Create a #Configuration annotated class:
#Configuration
public class MyApplicationContext {
}
For each <bean> tag create a method annotated with #Bean:
#Configuration
public class MyApplicationContext {
#Bean(name = "someBean")
public SomeClass getSomeClass() {
return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
}
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass() {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
}
}
In order to import beanFromSomewhereElse we need to import it's definition. It can be defined in an XML and the we'll use #ImportResource:
#ImportResource("another-application-context.xml")
#Configuration
public class MyApplicationContext {
...
}
If the bean is defined in another #Configuration class we can use the #Import annotation:
#Import(OtherConfiguration.class)
#Configuration
public class MyApplicationContext {
...
}
After we imported other XMLs or #Configuration classes, we can use the beans they declare in our context by declaring a private member to the #Configuration class as follows:
#Autowired
#Qualifier(value = "beanFromSomewhereElse")
private final StrangeBean beanFromSomewhereElse;
Or use it directly as parameter in the method which defines the bean that depends on this beanFromSomewhereElse using #Qualifier as follows:
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass(#Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
}
Importing properties is very similar to importing bean from another xml or #Configuration class. Instead of using #Qualifier we'll use #Value with properties as follows:
#Autowired
#Value("${some.interesting.property}")
private final String someInterestingProperty;
This can be used with SpEL expressions as well.
In order to allow spring to treat such classes as beans containers we need to mark this in our main xml by putting this tag in the context:
<context:annotation-config/>
You can now import #Configuration classes exactly the same as you would create a simple bean:
<bean class="some.package.MyApplicationContext"/>
There are ways to avoid spring XMLs altogether but they are not in the scope of this answer. You can find out one of these options in my blog post on which I'm basing my answer.
The advantages and disadvantages of using this method
Basically I find this method of declaring beans much more comfortable than using XMLs due to a few advantages I see:
Typos - #Configuration classes are compiled and typos just won't allow compilations
Fail fast (compile time) - If you forget to inject a bean you'll fail on compile time and not on run-time as with XMLs
Easier to navigate in IDE - between constructors of beans to understand the dependency tree.
Possible to easily debug configuration startup
The disadvantages are not many as I see them but there are a few which I could think of:
Abuse - Code is easier to abuse than XMLs
With XMLs you can define dependencies based on classes that are not available during compile time but are provided during run-time. With #Configuration classes you must have the classes available at compile time. Usually that's not an issue, but there are cases it may be.
Bottom line: It is perfectly fine to combine XMLs, #Configuration and annotations in your application context. Spring doesn't care about the method a bean was declared with.
Related
I would like to ask if there is possible in spring to annotate class with #Service with more than 1 value, something like this:
#Service({"ServiceName1","ServiceName2"})
public class ClassName {
}
The reason is i want to get same class when i am calling applicationContext.getBean("ServiceName1"); or applicationContext.getBean("ServiceName2");
Thanks in advance for answers.
You can just declare your bean in a configuration class instead, and specify multiple names in the Bean annotation:
#Bean(name = { "ServiceName1", "ServiceName2" })
public ClassName myService() {
return new ClassName();
}
But if you're getting beans by name, from the application context, you've probably missed the whole point of dependency injection.
I think if we use #Component/ #Service annotations we can create only one bean instance for a class. If we need to create multiple beans for a single class we need to go with xml approach. Define the beans for the single Class in applicationContext.xml
<bean id="ServiceName1" class="<ClassName Path>">
</bean>
<bean id="ServiceName2" class="<ClassName Path>">
</bean>
Does Spring Boot have a way to inject a dependency with the class name and constructor properties provided in the config file?
For example, I have two version of a common interface, IFileStore, FileStoreA and FileStoreB. I want to be able to define which of these I should use in the application.yml file.
I know I can do something like this:
#Value("${fileStore.class}")
private String fileStoreClassName;
#Bean
public IFileStore fileStore() {
switch(fileStoreClassName) {
case "FileStoreA":
return new FileStoreA();
case "FileStoreB":
return new FileStoreB();
}
}
This however feels really hacky. I'd also have to manually extract and supply any required parameters to them.
My ideal would be that it's able to determine which to use based on the class name, and also provide any parameters the specific one needs, so if I add a third FileStore, it'd auto-magically work and I'd just have to use that for the class name instead.
If you really only need a single bean, then create a conditional configuration
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreA")
public class FileStoreAConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreA(...);
}
}
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreB")
public class FileStoreBConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreB(...);
}
}
It's actually easier than that, as the annotation can be used on a method instead, rather than having separate configuration classes.
See the ConditionalOnProperty Javadoc
You can use Spring Profiles (#Profile annotation) in order to configure the same #Bean but with different implementations.
For example, you can make a production configuration like this:
#Configuration
#Profile("production")
public class ProductionConfiguration {
// ...
}
So, for your example, you can configure how many profiles you require and then you can specify the property in any of the usual ways, for example, you could include it in your application.properties.
For further details, you can read Spring Boot features - Profiles
Are you perhaps looking for XML-based configuration?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="fileStore" class="com.example.FileStoreA">
<property name="parameter1" value="Hello World!"/>
</bean>
</beans>
I have a configuration class which uses the #Configuration annotation and also extends the RepositoryRestMvcConfiguration.
as part of the extended class, there are overridable methods that allow configuration of the bean recipes. one of which is configuring the conversion services available to the spring component.
I would like to inject some beans into a list that is iterated over and added as a conversion service through this overrided method, My configuration java class is defined below:
#Configuration
#EnableJpaRepositories(basePackages = "com.example.model.repositories")
public class DataConfig extends RepositoryRestMvcConfiguration {
List<Converter<?,?>> converters;
//get
//set
#Override
protected void configureConversionService(ConfigurableConversionService conversionService){
for(Converter converter : converter){
conversionService.addConverter(converter);
}
}
}
The following defines my converters that i wish to inject in the app-context.xml file
<beans>
<bean id="fooToBarConverter" class="com.example.model.converters.FooToBarConverter" />
<bean id="barToFooConverter" class="com.example.model.converters.BarToFooConverter" />
<util:list id="myConverters" value-type="org.springframework.core.convert.converter.Converter">
<ref bean="barToFooConverter"/>
<ref bean="fooToBarConverter" />
</util:list>
</beans>
Is there a better way of providing these converters through spring configuration or do i need to explicitly list them as output of a function contained within my configuration class like:
#Bean
public List<Converter<?,?> myConverters(){
Arrays.asList(new FooToBarConverter(), new BarToFooConverter());
}
Your help is highly appreciated.
P.S. since you are so good at spring, would you mind having a look at my spring-data-rest-mvc related question? please and thank you.
By default, any #Autowired (or #Resource) annotated Collection (or List, Set, etc) of a certain type will contain all beans of that type discovered in the context. You could add an #Autowired in your setter and let Spring injects your controller for you.
If you need a more fine-grained control over which converters should be configured and which one should not, maybe you should configure the ConversionService altogether instead.
Is it possible to override imported resources using Spring annotation configuration?
The configuration class:
#Configuration
#ImportResource({"classpath:applicationContext.xml"})
public class CoreConfiguration {
#Resource(name = "classA")
private ClassA classA;
#Bean(name = "nameIWantToOverride")
private ClassB classB() {
return new ClassB("different setting");
}
}
The applicationContext.xml includes:
<bean name="classA" class="a.b.c.ClassA">
<property name="nameIWantToOverride" ref="classB" />
</bean>
If classA has a classB field but I want it to use the ClassB I define in my configuration class, is that possible? I tried switching the order but that didn't help. It seems XML takes precedence as when I run a simple test of instantiating the config, it never reaches the classB method. If I change the name so it doesn't match the bean in the xml file, then it does reach the classB method.
I've seen where it can work the other way: Can spring framework override Annotation-based configuration with XML-based configuration? but what about this direction? Since this is the newer way of doing things, I would think it you'd be able to do this.
What can I do to resolve this?
Edit: Updated with XML. Assume classA has multiple fields but I just want to replace the one.
You cannot override spring xml configuration using annotation.
Spring XML configuration always takes precedence to annotation configuration
Maybe, because of my wrong English, I couldn't understand the benefit of using #Autowired annotation.
According to the tutorial we can simplify the first(I.) case to second case(II.) by means of #Autowired.
My question is, what is the meaning of the #Autowired ? Because it doesnt tell any more, since without using #Autowired the compiler can figure out that "EmpDao emDao" and "EmpManager" are closely related according the declaration.
code cited from here
I.
<bean id="empDao" class="EmpDao" />
<bean id="empManager" class="EmpManager">
<property name="empDao" ref="empDao" />
</bean>
public class EmpManager {
private EmpDao empDao;
public EmpDao getEmpDao() {
return empDao;
}
public void setEmpDao(EmpDao empDao) {
this.empDao = empDao;
}
...
}
II.
<context:annotation-config />
<bean id="empManager" class="autowiredexample.EmpManager" />
<bean id="empDao" class="autowiredexample.EmpDao" />
import org.springframework.beans.factory.annotation.Autowired;
public class EmpManager {
#Autowired
private EmpDao empDao;
}
#Autowired is spring-specific. #Inject is the standard equivallent. It is an annotation that tells the context (spring, or in the case of #Inject - any DI framework) to try to set an object into that field.
The compiler has nothing to do with this - it is the DI framework (spring) that instantiates your objects at runtime, and then sets their dependencies at the points you have specified - either via XML or via an annotation.
I agree it is a possible scenario for a DI framework to try to inject dependencies into all fields, even if they are not annotated. (And if you want to exclude a particular field, to annotate it). But they chose the other strategy (configuration-over-convention). By the way:
if using xml config and choose some form of autowiring, the dependencies of the bean will be automatically autowired without the need to specify anything
you can specify per-context autowiring settings.
When the server bootstraps itself. It finds
<context:annotation-config />
in the application context and then goes through the classes defined in the contexts. If there are any beans that are autowired, it injects that into the class by referring the context file.
Basically, it promotes convention over configuration. That's what most frameworks do these days to reduce the development time.
the #Autowired Spring annotation tells Spring to for a bean named 'empDao' and inject it into the EmpManager class, without you having to add the empDao bean as a property in your spring config file.
#Autowired tells Spring to find a bean of the declared type and wire in that bean, rather than requiring an explicit lookup by bean name. It can, under certain circumstances, make configuring applications easier if you only have one implementation of your types in a given Spring context.