Spring Beans: Call a specific class method after instantiation - spring

I have 2 classes A & B. Booth are from some 3rd party package.
I have something like this in code
A objA = new objA;
objA.registerModule(new B());
Now I was thinking of #Autowiring A in the code. Is there a way to create a bean of A and B and call registerModule of class A & pass in reference to B in its bean?
Something like this:
<bean id="classB" class = ... />
<bean id="classA>
/*pass in reference to B */
</bean>

Yes there is, take a look at: MethodInvokingFactoryBean
In your case it should be something like this:
<bean id="classB" class = "com.company.B"/>
<bean id="classA" class = "com.company.A"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="classA"/>
<property name="targetMethod" value="registerModule"/>
<property name="arguments">
<list>classB</list>
</property>
</bean>

Related

Spring static factory with factory-method and parameter

I have a problem transfering code to Spring applicationContext.xml
The source is:
File inFile = new File ("path/to/file/", "fileName.docx")
WordprocessingMLPackage wordMLPackage = Docx4J.load(inFile);
My not working solution is:
<bean id="inFile" class="java.io.File">
<constructor-arg value="path/to/file/" />
<constructor-arg value="fileName.docx" />
</bean>
<bean id="docx4j" class="org.docx4j.Docx4J" factory-method="load">
<constructor-arg ref="inFile" />
</bean>
<bean id="wordprocessingMLPackage" class="org.docx4j.openpackaging.packages.WordprocessingMLPackage" factory-bean="docx4j" />
What I'm getting out of the bean "wordprocessingMLPackage" is indeed an instance of the Class WordprocessingMLPackage, but it seems empty although the File I'm trying to load isn't (and yes, the path is doublechecked).
When trying
MainDocumentPart mdp = wordprocessingMLPackage.getMainDocumentPart();
List<Object> content = mdp.getContent();
I'm getting a NullPointerException because mdp is null!
Has anyone an idea... or even a solution?
============================================================
I found a solution especially for my problem.
Here is the source of Docx4j.load():
public static WordprocessingMLPackage load(File inFile) throws Docx4JException {
return WordprocessingMLPackage.load(inFile);
}
That means I can create an instance of WordprocessingMLPackage by its static self!
The code which is working:
<bean id="wordprocessingMLPackage" class="org.docx4j.openpackaging.packages.WordprocessingMLPackage" factory-method="load">
<constructor-arg ref="baseDocument" />
</bean>
So I found a lucky "workaround" for the original problem.
Since this question isn't urgent any more, I'm still interested in the correct solution, especially in a solution which allows injecting the WordprocessingMLPackage in other beans.
Thank you!
Here you need to make use of MethodInvokingFactoryBean as detailed below.
<bean id="beanId"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.docx4j.Docx4J" />
<property name="targetMethod" value="load"/>
<property name="arguments">
<list>
<ref bean="inFile" />
</list>
</property>
</bean>
In your code get hold of applicationContext instance and invoke the below LOC
WordprocessingMLPackage ml = (WordprocessingMLPackage) applicationContext.getBean("beanId");
Let know in comments if you face any issues.
As Bond - Java Bond stated this works:
<bean id="inFile" class="java.io.File">
<constructor-arg value="path/to/file/" />
<constructor-arg value="fileName.docx" />
</bean>
<bean id="beanId" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.docx4j.Docx4J" />
<property name="targetMethod" value="load"/>
<property name="arguments">
<list>
<ref bean="inFile" />
</list>
</property>
</bean>
You can now use the bean as
WordprocessingMLPackage ml = (WordprocessingMLPackage) applicationContext.getBean("beanId");
or you can inject the bean directly as
<bean id="service" class="app.service.Service">
<property name="wordprocessingMLPackage" ref="beanId" />
</bean>
Thank you!!!

How can I inject a Class of an object, not an object itself

I'm trying to inject a list of Class. I want a list of classes, not objects.
My class looks like this:
public class CodeServiceImpl{
private List<Class<?>> codeList;
// getter and setter
My spring configuration file (I'm not using annotations but xml) is
<bean id="myCodeServiceImpl" class = "net.croz.service.CodeServiceImpl">
<property name="codeList">
<list>
<ref bean="myAddress"/>
<ref bean="myCity"/>
<ref bean="myCountry"/>
</list>
</property>
</bean>
<bean id="myAddress" class="java.lang.Class" factory-method="forName">
<constructor-arg value="net.croz.model.Address"/>
</bean>
<bean id="myCity" class="java.lang.Class" factory-method="forName">
<constructor-arg value="net.croz.model.City"/>
</bean>
<bean id="myCountry" class="java.lang.Class" factory-method="forName">
<constructor-arg value="net.croz.model.Country"/>
</bean>
But the list codeList isn't being populated. It ends up being a null object. Thank you for your help.
Actually it works as is:
<bean class="com.my.proj.Foo">
<constructor-arg value="java.lang.String, org.springframework.util.StringUtils, byte[]"/>
</bean>
where Foo is:
public class Foo {
private final List<Class<?>> codeList;
public Foo(Class<?>... codeList) {
this.codeList = Arrays.asList(codeList);
}
}
The ConversionService does the stuff for converting comma-separated string to the Class<?>[] and tries to resolve each class on its own using BeanClassLoader

Inject spring dependency in abstract super class

I have requirement to inject dependency in abstract superclass using spring framework.
class A extends AbstractClassB{
private Xdao daox ;
...
public setXdao() { ... }
}
class AbstractClassB{
..
private yDao daoy;
public seyYdao() { ... }
}
I need to pass superclass dependency everytime i instantiate Abstract class B (which can be subclassed in 100's of ways in my project)
entry in application.xml (spring context file)
<bean id="aClass" class="com.mypro.A"
<property name="daox" ref="SomeXDaoClassRef" />
<property name="daoy" ref="SomeYDaoClassRef"/>
</bean>
How can i just create bean reference of super class AbstractClassB in application.xml so that i can use it in all subclass bean creation?
You can create an abstract bean definition, and then "subtype" that definition, e.g.
<bean id="b" abstract="true" class="com.mypro.AbstractClassB">
<property name="daox" ref="SomeXDaoClassRef" />
</bean>
<bean id="a" parent="b" class="com.mypro.A">
<property name="daoy" ref="SomeYDaoClassRef" />
</bean>
Strictly speaking, the definition for b doesn't even require you to specify the class, you can leave that out:
<bean id="b" abstract="true">
<property name="daox" ref="SomeXDaoClassRef" />
</bean>
<bean id="a" parent="b" class="com.mypro.A">
<property name="daoy" ref="SomeYDaoClassRef" />
</bean>
However, for clarity, and to give your tools a better chance of helping you out, it's often best to leave it in.
Section 3.7 of the Spring Manual discusses bean definition inheritance.
You can use the abstract flag of Spring to tell Spring that a class is abstract. Then all concrete implementations can simply mark this bean as their parent bean.
<bean id="abstractClassB" class="AbstractClassB" abstract="true">
<property name="yDao" ref="yDao" />
</bean>
<bean id="classA" class="A" parent="abstractClassB">
<property name="xDao" ref="xDao" />
</bean>
Have an abstract parent bean:
http://forum.springsource.org/showthread.php?t=55811

Is it possible to alias bean class names in Spring?

I have a string property which looks similar to the following example:
<property name="mappingData">
<list>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
The long class name(s) effect readability & also create a refactoring overhead.
Is it possible to alias the class name and use a short name to declare the beans? Or is there an alternate best practice I'm missing?
Probably a bit late for you, but hopefully useful for others:
You can use parent beans to accomplish this.
First declare a parent bean as a template:
<bean id="FieldMapping" class="com.company.product.longNamingStandard.migration.extractor.FieldMapping"/>
Then use it elsewhere, using the parent attribute.
<property name="mappingData">
<list>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
Please note my convention here is to use upper case id's here for the parent template beans.
each <bean/> comes with an attribute of name and id to help you reference those beans later in your configuration.
I would suggest using the id for declaring the bean.
your config could look like:
<bean id="fooBean" class="com.example.foo"/>
<bean id="barBean" class="com.example.bar"/>
<list>
<ref>fooBean</ref>
<ref>barBean</ref>
</list>
You may try to represent your mapping in some short form, and then convert it to the list of FieldMappings. For example, mappings from your snippet may be represented as a map.
As a theoretic exercise in Spring 3 you can do this with Spring Expression Language (if FieldMapping has the apropriate constructor):
<util:map id = "m">
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</util:map>
...
<property name = "mappingData"
value = "#{m.![new com.company.product.longNamingStandard.migration.extractor.FieldMapping(key, value)]}" />
If this expression is too obscure, you may implement a FactoryBean to take a short form of your mapping data (for example, a map, as in this example) and return a configured list of FieldMappings:
<property name = "mappingData">
<bean class = "FieldMappingListFactoryBean">
<property name = "mappings">
<map>
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</map>
</property>
</bean>
</property>
However, if your field mappings are some kind of reusable DSL, you may try to think about implementing a namespace extension.
I found a way to simulate an effect similar to a "import com.Foo;" in java code. The best option I could find was to use a PropertyPlaceholderConfigurer with local properties defined. Using your example, here's the configuration that you would put at the top of your spring config file to define a "class_FieldMapping" property:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<description>Define properties equivalent to "import foo;" in java source</description>
<property name="properties">
<props>
<prop key="class_FieldMapping">com.company.product.longNamingStandard.migration.extractor.FieldMapping</prop>
</props>
</property>
</bean>
Then, you can use that property within your beans:
<property name="mappingData">
<list>
<bean class="${class_FieldMapping}">
...
</bean>
<bean class="${class_FieldMapping}">
...
</bean>
</list>
</property>
This has the benefit that use can also use it for things where you actually need the class name, and can't reference an instance of an object:
<util:constant static-field="${class_FieldMapping}.MYSTATICVAR" />
Why not declare those inner beans as separate top-level beans with their own names, and then reference them in the list ?
If I use PropertyPlaceholderConfigurer it leads to several exceptions in debug log. It works, but it seems it doesn't work on the first try.

How do you acess a property of a bean for reading in a spring xml config file?

I want to do something like the following in spring:
<beans>
...
<bean id="bean1" ... />
<bean id="bean2">
<property name="propName" value="bean1.foo" />
...
I would think that this would access the getFoo() method of bean1 and call the setPropName() method of bean2, but this doesn't seem to work.
What I understood:
You have a bean (bean1) with a
property called "foo"
You have another bean (bean2) with a
property named "propName", wich also
has to have the same "foo" that in
bean1.
why not doing this:
<beans>
...
<bean id="foo" class="foopackage.foo"/>
<bean id="bean1" class="foopackage.bean1">
<property name="foo" ref="foo"/>
</bean>
<bean id="bean2" class="foopackage.bean2">
<property name="propName" ref="foo"/>
</bean>
....
</beans>
Doing this, your bean2 is not coupled to bean1 like in your example. You can change bean1 and bean2 without affecting each other.
If you REALLY need to do the injection you proposed, you can use:
<util:property-path id="propName" path="bean1.foo"/>
You need to use PropertyPathFactoryBean:
<bean id="bean2" depends-on="bean1">
<property name="propName">
<bean class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="bean1"/>
<property name="propertyPath" value="foo"/>
</bean>
</property>
</bean>
I think you have to inject bean1, then get foo manually because of a timing issue. When does the framework resolve the value of the target bean?
You could create a pointer bean and configure that.
class SpringRef {
private String targetProperty;
private Object targetBean;
//getters/setters
public Object getValue() {
//resolve the value of the targetProperty on targetBean.
}
}
Common-BeanUtils should be helpful.

Resources