spring bean creation for java builder pattern objects - spring

I'm using Google Gson(gson) library form reading/writing json files and spring mvc 3 in my web application side.
So In controller, I want to create a singleton instance of Gson with pretty printing. In java the code would be,
Gson gson = new GsonBuilder().setPrettyPrinting().create();
In Controller, I created an autowired entry as below,
#Autowired
private Gson gson;
and the xml bean configuration is as below,
<bean id="gsonBuilder" class="com.google.gson.GsonBuilder">
<property name="prettyPrinting" value="true"/>
</bean>
<bean id="gson" factory-bean="gsonBuilder" factory-method="create"/>
It throws the following exception in catalina logs,
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'prettyPrinting' of bean class [com.google.gson.GsonBuilder]: Bean property 'prettyPrinting' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1024)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:900)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:58)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1358)
I knew that the setter signature of setPrettyPrinting() is different than spring expects, that's why spring is throwing the exception.
public GsonBuilder setPrettyPrinting() {
prettyPrinting = true;
return this;
}
But I'm unable to find a way to wire the builder pattern beans. I'm pretty new to spring. Can any one let me know, whether it is possible to solve this problem in xml bean approach ?

Simply use a static factory method as described in the documentation and use Java code to create Java objects: it's sooo much easier and safe:
<bean id="gson"
class="com.foo.bar.MyGsonFactory"
factory-method="create"/>
and in MyGsonFactory:
public static Gson create() {
return new GsonBuilder().setPrettyPrinting().create();
}

The setPrettyPrinting method doesn't take a parameter, so it doesn't look like a java bean property setter. That's why your approach didn't work. You can use the factory method mentioned in the other answer or use a method invoking bean in the config file like so:
<bean id="myStarter" class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="targetObject" ref="gsonBuilder"/>
<property name="targetMethod" value="setPrettyPrinting"/>
</bean>
The factory approach seems more straightforward and idiomatic to me, but I include this approach for the sake of completeness.

Related

what is the usage of new keyword while using java configuration in spring

I have a question around the usage of new keyword being used when using java configuration in spring. What is the need of using new keyword
Refer below mentioned example:
Code implemented using Java Config
#Configuration
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
The above code will be equivalent to the following XML configuration
<beans>
<bean id = "helloWorld" class = "com.test.HelloWorld" />
</beans>
In XML config, we do not use new keyword whereas in java config we are using new keyword. can someone please explain the difference
In the XML configuration, you explain to the system what class should be instanciated (there is a "new" but it is behind the scene) but in the Java Config you actually have to return an instance so that is why we use the 'new' keyword. 'new' simply creates an instance of your class.
The two examples shown in question are not really equivalent.
What the
<beans>
<bean id="helloWorld"
class="com.test.HelloWorld" />
</beans>
really does, is it tells Spring to instantiate class com.test.HelloWorld, and name the resulting bean "helloWorld".
Then the java-config approach is not really doing this. Instead this follows the factory-method pattern, when we tell Spring, that the return value of the method is the bean, and the method name is the name of that bean.
An equivalent of that in XML would be the mentioned factory-method approach, which in this case would look something like this:
<beans>
<bean id="helloWorldConfig"
class="com.test.HelloWorldConfig" />
<bean id="helloWorld"
factory-bean="helloWorldConfig"
factory-method="helloWorld" />
</beans>
Note that there are several approaches to factory-method. In the above, we are assuming, the `helloWorldConfig" is the factory, and we're specifying the method on that bean. Theare are cases with static factory methods too. See here for more examples.
<beans>
<bean id = "helloWorld" class = "com.test.HelloWorld" />
</beans>
This XML configurations tells Spring to "create an instance of com.test.HelloWorld and put it in the bean context with bean id helloWorld".
#Configuration
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
In this Java configuration, we are returning an instance of com.test.HelloWorld. Because of the #Bean annotation, this instance is put into the bean context. As no specific bean id is given, the bean id is derived from the method hellowWorld() and thus becomes helloWorld.
As you can see, both configurations require an instance of com.test.HelloWorld. The XML configuration implicitly creates the instance whereas in the Java configuration you have to explicitly do it yourself.

Convert XML bean definition with parent to Java with annotations

I have an application using a framework that provides certain Spring beans via XML files in the framework. The configuration of my application is currently done partly in XML but mostly with Spring annotations.
Some of the XML bean definitions have parents referring to beans supplied by the framework, e.g.
<bean id="MyBean" parent="FrameworkBean">
<property name="context">
<map merge="true">
<entry key="SomeKey" value-ref="SomeValue" />
</map>
</property>
</bean>
FramwworkBean is defined in an XML file in the framework. There is a chain of bean inheritance. At each step some entries are added to the context:
<bean id="FrameworkBean" parent="AbstractBean">
<map merge="true">...
<bean id="AbstractBean" abstract="true" class="ClassWithContext"/>
I understand the result of all this is construction of a ClassWithContext
instance with a map containing all the entries up the chain.
Is it possible to write Java code to do the same, without duplicating code from the framework XML files?
#Bean("MyBean") ClassWithContext myBean() {
return ??? // code that uses "FrameworkBean" somehow
}
The XML bean definition has no dependency on the type of AbstractBean.
If MyBean can be created by Java code, can that code be written to be equally type-agnostic? Or should I just leave this in XML?
If your "FrameworkBean" is not abstract bean you can try the following:
#Bean
public SomeType myBean(#Qualifier("FrameworkBean") FrameworkBeanType frameworkBean) {
SomeType type = getSomeType();
type.setFrameworkBean(frameworkBean);
return type;
}

Exported service not injecting in my bundle in Spring Dynamic Modules

I am using Spring Dynamic Modules for the first time. I have tried to expose a service (simple listofValuesDAO Bean) through a bundle and am trying to inject it in another bundle to use the bean.
Below is configuration tag in osgi-context.xml of Bundle1 through which service was exposed:
<osgi:service ref="listOfValuesDAO" auto-export="interfaces"/>
and I am trying to fetch it in Bundle2 through below tag in osgi-context.xml:
<osgi:reference id="listOfValuesDAO" interface="com.dao.IListOfValuesDAO" />
The issue is that when I try to inject it in my bean in Bundle2 using below configuration:
<bean id="exportServiceImpl" class="com.service.impl.ExportServiceImpl">
<property name="listOfValuesDAO" ref="listOfValuesDAO"/>
</bean>
System throws below exception:
Exception in thread "SpringOsgiExtenderThread-85"org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportServiceImpl' defined in URL [bundle://325.16:0/META-INF/spring/module-context.xml]:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'listOfValuesDAO' of bean class [com.service.impl.ExportServiceImpl]:
Bean property 'listOfValuesDAO' is not writable or has an invalid setter method. Did you mean 'listOfValuesDao'?
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1396)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
Below is property in my ExportServiceImpl Class:
public class ExportServiceImpl implements IExportService {
IListOfValuesDAO listOfValuesDao;
public void setListOfValuesDao(IListOfValuesDAO listOfValuesDao) {
this.listOfValuesDao = listOfValuesDao;
}
public IListOfValuesDAO getListOfValuesDao() {
return listOfValuesDao;
}
}
Could someone please help me in resolving this issue?
It seems to be a problem with case inconsistency: listOfValuesDao and listOfValuesDAO are different names.
You use the first version in the Service, and the second in the XML bean definition. Try:
<bean id="exportServiceImpl" class="com.service.impl.ExportServiceImpl">
<property name="listOfValuesDao" ref="listOfValuesDao"/>
</bean>

Is default constructor required in Spring injection?

I'm trying to inject a constructor that takes some arguments. After compiling Spring complains it couldn't find a default constructor (I haven't defined it) and throws BeanInstatiationException and NoSuchMethodException.
After defining a default constructor the exceptions don't appear anymore, however my object is never initialized with the argument constructor, only the default one is called. Does Spring really require a default constructor in this case? And if yes, how can I make it use the argument constructor instead of the default one?
This is how I wire everything:
public class Servlet {
#Autowired
private Module module;
(code that uses module...)
}
#Component
public class Module {
public Module(String arg) {}
...
}
Bean configuration:
<beans>
<bean id="module" class="com.client.Module">
<constructor-arg type="java.lang.String" index="0">
<value>Text</value>
</constructor-arg>
</bean>
...
</beans>
Stack trace:
WARNING: Could not get url for /javax/servlet/resources/j2ee_web_services_1_1.xsd
ERROR initWebApplicationContext, Context initialization failed
[tomcat:launch] org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'module' defined in URL [...]: Instantiation of bean failed;
nested exception is org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [com.client.Module]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: com.client.Module.<init>()
Spring only "requires" a default constructor if you plan on instantiating it without any arguments.
for example, if your class is like this;
public class MyClass {
private String something;
public MyClass(String something) {
this.something = something;
}
public void setSomething(String something) {
this.something = something;
}
}
and you set it up in Spring like this;
<bean id="myClass" class="foo.bar.MyClass">
<property name="something" value="hello"/>
</bean>
you're going to get an error. the reason is that Spring instantiates your class new MyClass() then tries to set call setSomething(..).
so instead, the Spring xml should look like this;
<bean id="myClass" class="foo.bar.MyClass">
<constructor-arg value="hello"/>
</bean>
so have a look at your com.client.Module and see how its configured in your Spring xml
Most probably you are using component-scanning and since you define annotation #Component for class Module it tries to instantiate the bean. You do not need #Component annotation if You are using XML for bean definition.
Just faced the same problem, i guess till now you might have solved the problem.
Below is what you could have changed your bean configuration to,
<bean id="module" class="com.client.Module">
<constructor-arg value="Text"/>
</bean>

Dynamic Dependency Injection Spring

I have following code inside my class
public void startListeners() throws Exception {
List<QueueConfiguration> queueConfigs = queueConfigResolver.getQueueConfigurations();
for(QueueConfiguration queueConfig : queueConfigs){
//TODO : work on this make it more testable
ICustomListener readerListener = new MyCustomListener(queueConfig);
readerListeners.add(readerListener);
readerListener.start();
}
}
I am using Spring for dependency injection(not in this case but overall). Now there two problems with this code.
I cannot put mock for each of the listeners created, while testing.
I dont want to use ApplicationContext.getBean() because it will have same affect. AFAIK spring cannot do this dynamically , but any other pointers?
As far as I can understand, you want to create a new bean instead of
ICustomListener readerListener = new MyCustomListener(queueConfig);
If that is the case, creating a factory for mycustomlistener and using
public abstract TestClient createTestClient();
to create your beans, and defining
<bean id="testClient" class="com.myproject.testbeans.TestClient" scope="prototype">
</bean>
<bean id="testClientFactory" class="com.myproject.testbeans.TestClientFactory">
<lookup-method name="createTestClient" bean="testClient" />
</bean>
in your context will solve your problem. This way, every time the createTestClient method of the factory is called, a new bean is created and given to your code. However, you have to give the config object via a setter instead of the constructor.

Resources