Spring - Inject Dependency based on properties - java

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>

Related

Accessing properties file in Spring Expression Language

I created a simple web application with Thymeleaf using Spring Boot. I use the application.properties file as configuration. What I'd like to do is add new properties such as name and version to that file and access the values from Thymeleaf.
I have been able to achieve this by creating a new JavaConfiguration class and exposing a Spring Bean:
#Configuration
public class ApplicationConfiguration {
#Value("${name}")
private String name;
#Bean
public String name() {
return name;
}
}
I can then display it in a template using Thymeleaf like so:
<span th:text="${#name}"></span>
This seems overly verbose and complicated to me. What would be a more elegant way of achieving this?
If possible, I'd like to avoid using xml configuration.
You can get it via the Environment. E.g.:
${#environment.getProperty('name')}
It's very simple to do this in JavaConfig. Here's an example:
#Configuration
#PropertySource("classpath:my.properties")
public class JavaConfigClass{
#Value("${propertyName}")
String name;
#Bean //This is required to be able to access the property file parameters
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
Alternatively, this is the XML equivalent:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>my.properties</value>
</property>
</bean>
Finally, you can use the Environment variable, but it's a lot of extra code for no reason.

Understanding spring #Configuration class

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.

injecting beans into spring java config classes

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.

Spring autowire using annotations and a type defined in a properties file?

My goal is a framework where concrete types of beans can be easily changed by a properties file. I also prefer annotations to XML. Ideally I'd to use a combination of #Resource and SpEL like this:
#Resource(type="#{myProperties['enabled.subtype']}")
SomeInterface foo;
where I've loaded myProperties with a PropertiesFactoryBean or <util:properties> from a file that includes:
enabled.type = com.mycompany.SomeClassA; // which implements SomeInterface
This doesn't work because the argument of type must be a literal, i.e., no SpEL allowed. What's the best practice here?
Update: See my answer below.
This is exactly the use case for Spring Java Configuration.
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java
Or you can alternatively make a Factory.
Using: org.springframework.beans.factory.FactoryBean<SomeInterface>
The name of the bean that implements FactoryBean will be seen as a "SomeInterface" even though its not.
I think it is not possible, the solution I tend to adopt is to use a factory that creates the different objects depending on a configuration property (enabled.type in your example).
A second alternative could be to use injection by name:
#Resource(name="beanName")
And last, if you use Spring 3.1+ you can try to use profiles, and have different bean sets in different profiles, if that solves your problem.
Spring's Java Configuration and Bean Definition Profiles turn out to be exactly what I was looking for (thanks #Adam-Gent and #Guido-Garcia). The former seems necessary for the dynamic element, and the latter promotes a better practice.
Here's a solution with Java config and properties:
#Configuration
public class SomeClassConfig {
#Value("#{myProperties['enabled.subtype']}")
public Class enabledClass;
#Bean SomeInterface someBean()
throws InstantiationException, IllegalAccessException {
return (SomeInterface) enabledClass.newInstance();
}
}
Here's a slightly less dynamic solution with profiles.
#Configuration
#Profile("dev")
public class DevelopmentConfig {
#Bean SomeInterface someBean() {
return new DevSubtype();
}
}
#Configuration
#Profile("prod")
public class ProductionConfig {
#Bean SomeInterface someBean() {
return new ProdSubtype();
}
}
With profiles, the active profile(s) are declared using one of a variety of methods such as via system property, JVM property, web.xml, etc. For example, with a JVM property:
-Dspring.profiles.active="dev"

Is context:annotation-config an alternative to #AutoWired?

Is it correct that I can put context:annotation-config in my XML config and it will automatically inject the bean class without needing any annotations?
So instead of using these annotation types:
public class Mailman
{
private String name;
#Autowired
private Parcel Parcel;
public Mailman(String name)
{
this.name = name;
}
#Autowired
public void setParcel(Parcel Parcel)
{
this.Parcel = Parcel;
}
#Autowired
public void directionsToParcel(Parcel Parcel)
{
this.Parcel = Parcel;
}
}
I would just need to write this:
<beans ... >
<bean id="mailMan" class="MailMan">
<constructor-arg value="John Doe"/>
</bean>
<bean id="parcel" class="Parcel" />
<context:annotation-config />
</beans>
and then my MailMan class would look a lot simpler without the need for annotations:
public class Mailman
{
private String name;
private Parcel Parcel;
public Mailman(String name)
{
this.name = name;
}
}
By default, a Spring context will pay no attention to #Autowired annotations. In order to process them, the context needs to have a AutowiredAnnotationBeanPostProcessor bean registered in the context.
<context:annotation-config/> registers one of these for you (along with a few others), so you do need it (unless you register AutowiredAnnotationBeanPostProcessor yourself, which is perfectly valid).
If you don't like having #Autowired in your code, then you can explicitly inject properties in the XML using <property>, which just moves the clutter from one place to another.
If your context is extremely simple, then you can use implicit autowiring, as described here. Essentially, this tells Spring to autowire automatically by property name or type. This required very little configuration, but it very quickly gets out of control - it's automatic nature means it's hard to control, and gives you very little flexibility.
#Autowired really is the best option, in general.
<context:annotation-config /> just auto-registers all the standard bean post processors provided by Spring like PersistenceAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor and RequiredAnnotationBeanPostProcessor, so that you don't have to register them individually in your spring context file. You still have to provide the #Autowired annotations on the attributes/setter as earlier.
No.
#Autowired Annotations are needed on the class along with <context:annotation-config/>
The xml config part is not needed when using the annotations.
Also from the doc Note that <context:annotation-config/> only looks for annotations on beans in the same application context it is defined in. This means that, if you put in a WebApplicationContext for a DispatcherServlet, it only checks for #Autowired beans in your controllers, and not your services.
If you dont need to use annotations then you need to specify the xml configuration.

Categories

Resources