Autowire a single argument in a constructor - java

In a constructor with 2 arguments, I want to explicitly wire one and autowire the other.
Something like:
public Obj(Interface arg, Interface arg2) {
// stuff
}
And in the bean definition:
<bean id="objImpl" class="com.work.Obj">
<constructor-arg index="0" ref="interfaceImpl"/>
<constructor-arg index="1" autowire-this-somehow/>
</bean>
Does anyone know of a way ?

Try this:
<bean id="objImpl" class="com.work.Obj" autowire="constructor">
<constructor-arg index="0" ref="interfaceImpl"/>
</bean>
Here, you have specified that bean of type com.work.Obj should created using constructor autowiring. If you specify any constructor arg, then it overrides autowired arg. So index 0 is explicitly provided. Other args which are not explicitly provided will be autowired by type.
Caution: constructor autowiring has the same limitation as byType - Spring won't attempt to guess which bean to autowire when it finds multiple beans that match a constructor arg. Further, if the class has multiple constructors, any of which can be satisfied by autowiring, then Spring won't attempt to guess which constructor to use. You will get an exception in this case.
EDIT: For this to work, other beans of type Interface (constructor arg type) except the one that needs to be autowired, should be marked with autowire-candidate=false
Example:
<bean id="impl1" class="stackoverflow.SomeImpl" autowire-candidate="false"/>
<bean id="impl2" class="stackoverflow.SomeImpl"/>
<bean id="obj" class="stackoverflow.Obj" autowire="constructor">
<constructor-arg index="0" ref="impl1"/>
</bean>
And Obj class:
package stackoverflow;
public class Obj {
public Obj(SomeInterface i1, SomeInterface i2){
System.out.println("i1" + i1);
System.out.println("i2" + i2);
}
}
Here SomeImpl implements SomeInterface. On running impl2 bean is autowired in the second constructor arg i2. The first arg is manually provided in Spring config.

Related

Bean initialized with FactoryBean receives the FactoryBean instead of the Obect created by it

I'm trying to reference a bean from another with xml, sending an object produced with a FactoryBean. My problem is that the data generated by the beanFactory is, apparently a FactoryBean object and hasn't the type that should have been created by the factory.
The beans are defined like this:
<bean id="daFactoryBean" class="com.whatever.something.MyFactoryBean">
[...]
</bean>
<bean id="theBeanThatProducesProblems" class="com.whatever.something.AGoodName">
<constructor-arg ref="daFactoryBean"/>
<constructor-arg ref="anotherBean"/>
</bean>
Note that "daFactoryBean" would produce objects typed "TheClassIWantToProduce". And that "theBeanThatProducesProblems" expects that type on "daFactoryBean" but it receives the Factory itself.
The factoryBean is defined like this
public class MyFactoryBean implements
FactoryBean<TheClassIWantToProduce> {
FactoryBean([..]) { [...] }
[...]
#Override
public TheClassIWantToProduce getObject() { [...] }
}
and the class that I need to produce is this
public AGoodName extends RestTemplate {
AGoodName(TheClassIWantToProduce foo, AnotherClassThatDoesnTComeFromAFactory foo2){
}
}
The thing is that I don't exactly know how to say the spring's xml that I want the object produced by the factory, not the factory itself. As the types aren't valid, the expected value isn't being initialised. Any hints?
Solved, I had to create a sub-bean that used my factory-bean
<bean id="daFactoryBean" class="com.whatever.something.MyFactoryBean">
[...]
</bean>
<bean id="theBeanThatProducesProblems" class="com.whatever.something.AGoodName">
<constructor-arg>
<bean id="aNewBean" factory-bean="daFactoryBean" class="com.whatever.something.TheClassIWantToProduce"/>
</constructor-arg>
<constructor-arg ref="anotherBean"/>
</bean>

Inject bean dependency into Spring application context

I have this class that creates an application context from XML:
public class SpringModel {
public SpringModel(Object dependency) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(...);
Foo foo = (Foo) applicationContext.getBean("foo");
}
}
The bean named "foo" has a constructor that looks like this:
public Foo(Object dependency) {
...
}
I want to inject the argument from SpringModel's constructor into the constructor for Foo. Is this possible?
If you're using an xml based application context you could define the dependencies like
<beans>
<bean id="dependencyObj" class="Object"/>
<bean id="springModelObj" class="SpringModel">
<constructor-arg ref="dependencyObj"/>
</bean>
<bean id="foo" class="Foo">
<constructor-arg ref="dependencyObj" />
</bean>
</beans>
It might also be easier to have your Foo object reference be a private variable in the SpringModel class with an appropriate setter method. Then you could link it to the SpringModel bean in the xml configuration with a property tag.

How to tell Spring to instantiate selected beans without providing constructor args?

Problem:
I'm integrating with a library written by the other team. This library provides a set of classes that I'm using in my Spring-driven application. Every bean in my application is in singleton scope.
Also 99% of the classes from that library uses constructor injection.
I'm using XML and my configuration looks very similar to the following:
<bean id="lib.service1" class="lib.Service1"/>
<bean id="lib.service2" class="lib.Service2">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service3" class="lib.Service3">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service4" class="lib.Service3">
<constructor-arg ref="lib.service2"/>
<constructor-arg ref="lib.service3"/>
</bean>
<!-- other bean definitions -->
<bean id="lib.serviceN" class="lib.ServiceN">
<constructor-arg ref="lib.serviceX"/>
<constructor-arg ref="lib.serviceY"/>
<constructor-arg ref="lib.serviceZ"/>
<constructor-arg ref="lib.serviceK"/>
</bean>
<!-- other bean definitions -->
What I want:
I want to simplify my configuration to not to use bean IDs and ask spring to do constructor injection for me based on the type of arguments in the bean constructors. I can also ask library implementers to add #Inject annotation to the class constructors (the 99% of the classes have just one public constructor), but this is all that I can ask wrt refactoring of their library.
And eventually I want to have just following in my spring config (doesn't work, but illustrates the idea):
<bean class="lib.Service1"/>
<bean class="lib.Service2"/>
<bean class="lib.Service3"/>
<!-- ... -->
<bean class="lib.ServiceN"/>
Here I'm expecting Spring to figure out that I want to use constructor injection for all those beans and infer bean instances based on the constructor argument types.
Note that I cannot use component scan - they have one package (lib. in the example given above) and some classes in that package are useless for my application and too expensive to be needlessly created. Plus some classes that I'm not using are experimental and can be changed/renamed/removed without prior notice.
Add autowire="constructor", assuming these bean types only have one constructor and that the corresponding parameters match single beans.
<bean class="lib.Service1" autowire="constructor"/>
From the documentation
"constructor"
Analogous to "byType" for constructor arguments. If there
is not exactly one bean of the constructor argument type in the
bean factory, a fatal error is raised. Note that explicit
dependencies, i.e. "property" and "constructor-arg" elements, always
override autowiring. Note: This attribute will not be inherited by
child bean definitions. Hence, it needs to be specified per concrete
bean definition.

Spring injection not working for constructor-arg

I have following java class:
package configuration;
import common.config.ConfigurationService;
public class AppConfig {
private ConfigurationService configurationService;
public AppConfig(ConfigurationService configurationService){
this.configurationService = configurationService;
}
also
public class ConfigurationServiceImpl
implements ConfigurationService, Runnable
{...
and the application context file is as follows:
<bean id="appConfig" class="configuration.AppConfig" scope="prototype">
<constructor-arg ref="configurationService"></constructor-arg>
</bean>
<bean id="configurationService" class="common.config.ConfigurationServiceImpl" scope="singleton" />
<bean id="propertyPlaceholderConfigurer" class="common.config.PropertyPlaceholderConfigurer">
<constructor-arg ref="configurationService" />
<constructor-arg ref="serviceName" />
</bean>
<bean id="serviceName" class="java.lang.String"><constructor-arg value="filter"/></bean>
during initialization I am getting following error and my beans are not initialized:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfig' defined in class path resource [conf/applicationContext.xml]: 1 constructor arguments specified but no matching constructor found in bean 'appConfig' (hint: specify index and/or type arguments for simple parameters to avoid type ambiguities)
While Spring injection works if I modify the java class code as follows:
package configuration;
import common.config.ConfigurationServiceImpl;
public class AppConfig {
private ConfigurationServiceImpl configurationService;
public AppConfig(ConfigurationServiceImpl configurationService){
this.configurationService = configurationService;
}
First of all , you have to know that Spring do not support interface injection, and thats why the code in your first case do not work,because you are passing ConfigurationService which is an interface as the constructor args.
In the second case , you are doing it right by passing the implementation class of ConfigurationService and taking it as the constructor argument.
Just Looking at it, the package name for AppConfig in the Spring configuration does not match the package declared in the Java source. You have "common.config" versus "configuration". It may be that the error text is misleading, that the reason the constructor is not found is that the class itself is not found.

Populating a spring bean using a constructor-arg field

How can i inject a properties file containing a Map to be used as additional constructor arg using the field.
With a Map being loaded from a properties file
the bean is currently setup using:
<bean id="graphDbService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
init-method="enableRemoteShell" destroy-method="shutdown">
<constructor-arg index="0" value= "data/neo4j-db"/>
<constructor-arg index="1" value=? />
</bean>
Java Equivalent:
Map<String,String> configuration = EmbeddedGraphDatabase.loadConfigurations( "neo4j_config.props" );
GraphDatabaseService graphDb = new EmbeddedGraphDatabase( "data/neo4j-db", configuration );
Thanks
Something like this:
<bean id="configuration" class="org.neo4j.kernel.EmbeddedGraphDatabase"
factory-method="loadConfigurations">
<constructor-arg value="neo4j_config.props"/>
</bean>
<bean id="graphDbService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
init-method="enableRemoteShell" destroy-method="shutdown">
<constructor-arg index="0" value="data/neo4j-db"/>
<constructor-arg index="1" ref="configuration" />
</bean>
This takes advantage of the ability to create beans using arbitrary static factory methods, in this case using loadConfigurations() as a factory method to create the configuration bean, which is then injected into the proper constructor of EmbeddedGraphDatabase.
Create a bean that loads the properties (and takes the file name as an argument) and inject that instead.
EDIT When using annotations, things like constructor injection become more simple:
#Bean
public Map<String,String> configuration() {
return EmbeddedGraphDatabase.loadConfigurations( "neo4j_config.props" );
}
#Bean
public GraphDatabaseService graphDb() {
return new EmbeddedGraphDatabase( "data/neo4j-db", configuration() );
}
Note that the second bean definition method "simply" calls the first. When this code is executed, Spring will do some magic so you can still override the bean elsewhere (i.e. beans still overwrite each other) and it will make sure that the method body will be executed only once (no matter how often and from where it was called).
If the config is in a different #Configuration class, then you can #Autowired it:
#Autowired
private Map<String,String> configuration;
#Bean
public GraphDatabaseService graphDb() {
return new EmbeddedGraphDatabase( "data/neo4j-db", configuration );
}

Categories