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.
Related
My question is about which configuration strategy I should use for my Java Spring application which runs multiple threads in a clustered environment.
The hierarchy of the application is like this:
Main application files & config
|
|__Parent module files & config
|
|__Child module files & config
I have a DataProvider which is #Transactional:
#Transactional
public class DataProvider {
protected Configuration configuration;
public DataProvider(Configuration configuration, DummyArg1 arg1, DummyArg2 arg2) {
this.configuration = configuration;
createResult();
addMoreStuffFromModules();
}
private void createResult() {
this.result.setSomeStuff = "someStuff";
}
private void addMoreStuffFromModules() {
this.result.setSomeMoreStuff = configuration.getModuleDataProvider.getData();
}
}
Main application configuration:
<bean id="dataProvider" abstract="true" class="com.main.DataProvider">
<constructor-arg ref="main-configuration"/>
<constructor-arg ref="main-dummyArg1"/>
<constructor-arg ref="main-dummyArg2"/>
</bean
<bean id="main-Configuration" class="com.main.Configuration" /> <!-- just an empty class in main application -->
Parent module configuration:
<bean id="dataProvider" abstract="true" class="com.main.DataProvider">
<constructor-arg ref="parent-configuration"/>
<constructor-arg ref="parent-dummyArg1"/>
<constructor-arg ref="main-dummyArg2"/>
</bean
<bean id="parent-Configuration" class="com.parent.Configuration" />
As you can see. Parent module needs to provide new bean for dataProvider and all constructor arguments to be able to inject its own configuration into the Data Provider class.
This is the same for the child module:
<bean id="dataProvider" abstract="true" class="com.main.DataProvider">
<constructor-arg ref="child-configuration"/>
<constructor-arg ref="parent-dummyArg1"/>
<constructor-arg ref="main-dummyArg2"/>
</bean
<bean id="child-Configuration" class="com.child.Configuration" />
Is this a good strategy? I think it's not. Because what happens if the parent configuration updates (for example sets configuration to reference another bean)? Then we would want the child configuration to automatically get that new reference in the constructor-arg.
I would prefer if its possible, to have the configuration for the modules only set the constructor args as they want to change (not the other args), for example like this:
<bean id="dataProvider" abstract="true" class="com.main.DataProvider">
<constructor-arg ref="child-configuration"/>
<!-- arg1 inherited from parent and/or main configuration -->
<!-- arg2 inherited from parent and/or main configuration-->
</bean
<bean id="child-Configuration" class="com.child.Configuration" />
Any ideas on how to achieve this?
If you make the Configuration a bean, you can just override that bean in the child modules and simply not override the DataProvider bean.
Update
Java based configuration example, as I don't use XML configs:
Suppose you have 3 beans that DataProvider needs:
#Component
public class Configuration {
// main config here
}
#Component
public class OtherData {
}
#Component
public class MoreData {
}
And a Data provider like this:
#Component
public class DataProvider {
#Inject private Configuration conf;
#Inject private OtherData otherData;
#Inject private MoreData moreData;
}
Then in your other module do:
#Component #Primary
public class ChildConfiguration extends Configuration {
// different config here
}
... and you will have overriden only the Configuration part without changing the other two parts.
I have a spring rest application which has a Rest Controller as below
#RestController
public class IngestorController
{
#Autowired
private IngestorService ingestorService;
#RequestMapping( value = "/ingestor/createAndDeploy/{ingestorName}", method = RequestMethod.POST )
public void createAndDeploy( #PathVariable String ingestorName )
{
ingestorService.createAndDeploy( ingestorName );
}
}
Simlilarly I have a Service Bean as below
#Service
public class IngestorService
{
#Autowired
private IngestorCommandBuilder ingestorCommandBuilder;
private String uri;
private DeployTemplate deployTemplate;
public void init() throws URISyntaxException
{
deployTemplate = new DeployTemplate( new URI( uri ) );
}
#Transactional
public void createAndDeploy( Ingestor ingestor )
{
//.....
}
}
I have the Spring config as show below
<bean id="ingestorCommandBuilder" class="org.amaze.server.ingestor.IngestorCommandBuilder" />
<bean id="ingestorService" class="org.amaze.server.service.IngestorService" init-method="init">
<property name="uri" value="http://localhost:15217" />
</bean>
<bean id="ingestorController" class="org.amaze.server.controller.IngestorController"/>
When ever I try to start the application context the application context starts and it hits the init method in the IngestorService, deployTemplate object also initilized for the service bean.
But this bean is not autowired for the IngestorController. When I hit the rest endpoint from postman, the service bean has deployTemplate property as null.. The object that is assigned to the ingestorService variable in the Controller is a different object not the one which was called for the init method...
I tried making the service bean singleton (Even if the default scope is singleton) but dint work...
I am not able to find out the mistake I am doing.. Any suggestions appreciated...
If you use annotation-based configuration, you mostly dont need to describe all your beans in application context xml file. Annotations are all you need to autowire service.
To define your init method properly, use #PostConstruct annotation. Properties can be easily moved to externat .properties file and injected to your code with #Value annotation.
Alternatively, use #Qualifier with #Autowired.
First ensure that you have:
<context:annotation-config />
In your Spring config. Now you have severael alternatives:
<context:component-scan base-package="your.package.com" />
to scan for components, or
<!-- autowiring byName, bean name should be same as the property name annotate your ingestorservice with #Service("ingestorServiceByName") -->
<bean name="ingestorServiceByName" class="your.package.com.IngestorService" autowire="byName" />
<!-- autowiring byType, tif there are more bean of the same "general" type, this will not work and you will have to use qualifier or mark one bean as #Primary -->
<bean name="ingestorServiceByType" class="your.package.com.IngestorService" autowire="byType" />
<!-- autowiring by constructor is similar to type, but the DI will be done by constructor -->
<bean name="ingestorServiceConstructor" class="your.package.com.IngestorService" autowire="constructor" />
Please include your full Spring configuration to make it easier to analyze your problem.
When you are using Annotation based DI, you need not define the beans in XML.
#PostConstruct can be used to replace your init-method of xml config.
Just use
<context:component-scan base-package="..."/> <mvc:annotation-driven/>
I've got this:
#Named
#Singleton
public class MyDefaultDef {
#Inject
public MyDefaultDef(SomeRef someRef, List<AnotherRef> anotherRefs) {
//...
}
//...
}
Question1: [main] How does work autowiring of List<AnotherRef> anotherRefs as a constructor arg?
I mean if I'd like to replace that bean definition with xml, I had to specify each element of the list. I.e.
<constructor-arg>
<list>
<ref bean="..."/>
<ref bean="..."/>
</list>
</constructor-arg>
But from where Spring takes those elements in case of annotations?
Question2: How to replace MyDefaultDef bean definition to xml?
When Spring needs to autowire a List<SomeBean>, it looks up all beans in its BeanFactory and retrieves all those that are of type SomeBean. It thens creates a List and adds them to it. It then autowires it.
There is no way in Spring XML to do this listing by type. You'll need to compromise. You leave your class as such
public class MyDefaultDef {
#Inject
public MyDefaultDef(SomeRef someRef, List<AnotherRef> anotherRefs) {
//...
}
//...
}
and simply declare
<bean class="com.example.MyDefaultDef" autowire="constructor"/>
Spring will end up using your constructor above.
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.
I have a class
public class MakeMeBean {
#Autowired private IAmBean var1;
private IAmNOTBean var2;
public MakeMeBean() {}
public MakeMeBean(IAmNOTBean var) {
this.var2 = var;
}
}
I want to make this class as a bean so I make a wireup.xml as
<bean id="make-me-bean" class="com.blah.blah.MakeMeBean">
<constructor-arg index="0" ref=<PUT REFERENCE BEAN HERE>
<constructor-arg index="1" <I don't want to put anything>
</bean>
Question
a.) How can I make a bean in which one instance variable is a bean and another not? I don't want to inject var2(another bean in wireup.xml)
b.) <PUT REFERENCE BEAN HERE> is a bean imported from jar file, how can I make reference to this bean in wireup.xml
You can't just have some beans in context that you created and another half that spring created (at least not that simple), if you want to manage the instances over spring, spring should have the objects on its context. Of course you have the possibility to instantiate the objects in the context, and after the instantiation you could invoke some setters to set some properties.
In order to use another to user another bean, that I suppose comes from another Spring context, the other spring context needs to be imported in the first one. In order to import a context file you can use:
<import resource="resourcePath" />