Spring and passing parameters to factory-method in runtime - java

The documentation of method context.getBean(name, user) says
Allows for specifying explicit constructor arguments / factory method
arguments
but no matter what I do (tried everything), with the most logical setting I get this when the beans are being loaded up during initialization:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fileValidator' defined in
PortletContext resource
[/WEB-INF/classes/context/customer-form-portlet.xml]: Unsatisfied
dependency expressed through constructor argument with index 0 of type
[com.liferay.portal.model.User]: Ambiguous factory method argument
types - did you specify the correct bean references as factory method
arguments?
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fileValidator' defined in
PortletContext resource
[/WEB-INF/classes/context/customer-form-portlet.xml]: Unsatisfied
dependency expressed through constructor argument with index 0 of type
[com.liferay.portal.model.User]: Ambiguous factory method argument
types - did you specify the correct bean references as factory method
arguments?
<bean id="fileValidator"
class="cz.instance.transl.validation.file.FileValidator"
factory-method="createInstance" />
private FileValidator(User user) {
this.user = user;
}
public static FileValidator createInstance(User user) {
return new FileValidator(user);
}
The commentary says you can do it, but if you specify constructor arguments in xml definiton of that bean or not, it fails.

The javadoc says:
args - arguments to use if creating a prototype using explicit arguments to a static factory method.
So the bean definition must be a prototype-scoped bean, i.e.
<bean id="fileValidator"
scope="prototype"
class="cz.instance.transl.validation.file.FileValidator"
factory-method="createInstance" />

Reading across 20 posts, I found that it was not apparent how to get a custom factory method to take parameters at run-time, especially since we are forced to use the constructor-arg tags and refer to an existing bean in the context as setup below and the class in question acting as a static factory method.
<bean id="user" class="something.something.User" />
<bean id="fileValidator"
class="cz.instance.transl.validation.file.FileValidator"
factory-method="createInstance" >
<constructor-args ref="user" />
</bean>
I got it working by fetching an instance of the bean used in the constructor-arg out of the context and then populating it with the values that you are working with at run-time. This bean will then be used as the parameter when you get your factory-generated bean.
public class X {
public void callFactoryAndGetNewInstance() {
User user = context.getBean("user");
user.setSomethingUsefull(...);
FileValidator validator = (FileValidator)context.getBean("fileValidator");
...
}
}
Note this doesn't solve the problem asked of using context.getBean(arg1, arg2) as that method isn't relevant in this scenario. The reason it isn't is because all these beans are singleton and at this point the constructor isn't invoked. Not a problem and not anything to care about if you are working in a single-user system as you only have 1 User bean in your context at any time anyhow!
However, For a multi-user system you will need to make sure that you have a unique User bean for each real user and that you use the correct User bean in the factory method invocation.
In order to do this in a multi-user system, you will need to change the bean types to be prototype AND you should create a bean of your FileValidator that represents the factory (if you plan to dependency injection into the factory) and another bean FileValidator that represents your new instance. They will both be of the same class type but you must give each one a unique name. See below:
<bean id="user" scope="prototype" class="something.something.User" />
<bean id="validatorFactory"
class="cz.instance.transl.validation.file.FileValidator">
<constructor-arg value="something" />
</bean>
<bean id="fileValidatorBean"
class="cz.instance.transl.validation.file.FileValidator"
scope="prototype"
factory-method="createInstance" >
<constructor-arg ref="user" />
</bean>
and in the class where you would like to get this new FileValidator bean from the factory, you can use the technique below:
public void someMethod() {
...
User user = context.getBean("user");
user.setSomethingUsefull(...);
FileValidator fileValidator =
(FileValidator)context.getBean("fileValidatorBean",
user);
...
}

In order to call your factory method Spring needs access to a user instance to pass to createInstance. In this case I am just creating a bean and passing it in:
<bean id="user" class="something.something.User">
</bean>
<bean id="validator" class="cz.instance.transl.validation.file.FileValidator" factory-method="createInstance">
<constructor-arg ref="user"/>
</bean>

You can use an abstract factory too setting the factory-bean attribute. Here we have an ActionFactory which create actions.
<bean id="actions_factory" class="com.imagina.control.actions.impl.ActionFactoryImpl"/>
<bean id="load_person_action" class="com.imagina.control.actions.impl.LoadPersonAction"
factory-bean="actions_factory" factory-method="create">
<constructor-arg value="load_person_action"/>
</bean>
To use this configuration you have to take this points in account:
create method is not static. Now belongs to an instance
constructor-arg is the parameter of factory method

Related

Spring, Dependency Injection inquiry

From the book "Spring in Action", I have seen this configuration xml file:
<bean id="knight" class="com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest" />
Inject quest bean
</bean>
What exactly is <constructor-arg ref="quest" />? Does it mean that each time I call quest as a reference in a constructor it fetches a BraveKnight?
Thanks in advance.
The XML snippet you have is called a bean definition. You are declaring a bean which Spring can generate for you.
Using <constructor-arg> tells Spring to use a com.springinaction.knights.BraveKnight constructor which accepts an argument of whatever type the bean referenced by the id quest is and inject that bean in the constructor call. This is called constructor based dependency injection. It is covered here.
Given two classes
class Foo {}
class Bar {
private Foo foo;
public Bar (Foo foo) {
this.foo = foo;
}
}
and the following bean definitions
<bean id="foo" class="Foo" />
<bean id="bar" class="Bar">
<constructor-arg ref="foo">
</bean>
A Foo bean will be created with id foo. That bean will be used when invoking the Bar constructor to create the bean with id bar. Remember that this is all done through reflection.

Ambiguous factory method argument types error when wiring an executorService

I have a bean definition as:
<bean id="executor" class="java.util.concurrent.Executors"
factory-method="newSingleThreadExecutor" destroy-method="shutdownNow" />
When loading, this unfortunately causes:
Ignoring factory method [public static java.util.concurrent.ExecutorService java.util.concurrent.Executors.newSingleThreadExecutor(java.util.concurrent.ThreadFactory)] of bean 'executor':
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'executor' defined in class path resource [main-config.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.util.concurrent.ThreadFactory]: Ambiguous factory method argument types - did you specify the correct bean references as factory method arguments?
Spring seems to want to use the newSingleThreadExecutor(ThreadFactory) method whereas I just want to use the no argument method. Any thoughts on why?
I don't know which version of Spring you are using, but every document for versions above 3.0 state that to supply arguments to the static factory method, you need to use the <constructor-arg> within the <bean> element.
Specifying
<bean id="executor" class="java.util.concurrent.Executors"
factory-method="newSingleThreadExecutor" destroy-method="shutdownNow" />
will use the no-arg method newSingleThreadExeutor().

How do you inject a proxy into a service?

There is some information in the tomcat engine that we want to access run time, so we have the following in our app context (got this from this blog post):
<bean id="tomcatEngineProxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
<property name="objectName" value="Catalina:type=Engine" />
<property name="proxyInterface" value="org.apache.catalina.Engine" />
<property name="useStrictCasing" value="false" />
</bean>
In a controller, we then autowired it in like this:
#Autowired
private MBeanProxyFactoryBean tomcatEngineProxy = null;
We cannot wire in org.apache.catalina.Engine like in the blog post, because that class is not available to us at build time. It's only available at run time with all the different tomcat versions running on the different machines.
We were able to get the information we needed from this #Autowire using reflection. Now, we want to move this functionality into a service. I added this to our app context:
<bean id="myService" class="com.foo.bar.MyServiceImpl">
<constructor-arg ref="tomcatEngineProxy" />
</bean>
And the class looks like this:
public class MyServiceImpl implements MyService
{
public MyServiceImpl(MBeanProxyFactoryBean tomcatEngineProxy) throws Exception
{
//stuff with the proxy
}
.....
}
When I do this, I get the following error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myService' defined in ServletContext resource [/WEB-INF/spring/root-context.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.jmx.access.MBeanProxyFactoryBean]: Could not convert constructor argument value of type [$Proxy44] to required type [org.springframework.jmx.access.MBeanProxyFactoryBean]: Failed to convert value of type '$Proxy44 implementing org.apache.catalina.Engine,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.jmx.access.MBeanProxyFactoryBean'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy44 implementing org.apache.catalina.Engine,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.jmx.access.MBeanProxyFactoryBean]: no matching editors or conversion strategy found
Knowing basically nothing about how proxies work and how to use them, I'm not sure how to go about making this work. Is there some declaration I can use for my constructor arg that will match up? What is different between the #Autowire in the controller that does work and the constructor arg that doesn't work?
It's because your factory bean is exposing the result as the engine interface:
<property name="proxyInterface" value="org.apache.catalina.Engine" />
So if you try to wire in the "tomcatEngineProxy" bean itself, it's only compatible assignment is to "org.apache.catalina.Engine", since the created proxy implements only that interface.
try referencing the factory bean directly instead (notice the ampersand which is the syntax for finding the actual factory bean which created the object instead of the object itself):
<constructor-arg ref="&tomcatEngineProxy" />
How to inject FactoryBean instead of object it produces?
I believe that #Autowired works since the binding is done according to the type of the Bean (i.e. MBeanProxyFactoryBean), however the binding to the constructor argument is done by name and the name you provided tomcatEngineProxy does not match the type you expect since the type of a bean created using a FactoryBean is different than that of the FactoryBean.
It looks like the simplest solution will be to change MyServiceImpl to be:
public class MyServiceImpl implements MyService
{
#Autowired
private MBeanProxyFactoryBean tomcatEngineProxy;
public MyServiceImpl() throws Exception
{
//stuff with the proxy
}
.....
}
This should do the trick.

Does Spring require all beans to have a default constructor?

I don't want to create a default constructor for my auditRecord class.
But Spring seems to insist on it:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'auditRecord' defined in ServletContext resource
[/WEB-INF/applicationContext.xml]:
Instantiation of bean failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Could not instantiate bean class [com.bartholem.AuditRecord]:
No default constructor found;
nested exception is
java.security.PrivilegedActionException:
java.lang.NoSuchMethodException:
com.bartholem.AuditRecord
Is this really necessary?
No, you are not required to use default (no arg) constructors.
How did you define your bean? It sounds like you may have told Spring to instantiate your bean something like one of these:
<bean id="AuditRecord" class="com.bartholem.AuditRecord"/>
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<property name="someProperty" val="someVal"/>
</bean>
Where you did not provide a constructor argument. The previous will use default (or no arg) constructors. If you want to use a constructor that takes in arguments, you need to specify them with the constructor-arg element like so:
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<constructor-arg val="someVal"/>
</bean>
If you want to reference another bean in your application context, you can do it using the ref attribute of the constructor-arg element rather than the val attribute.
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<constructor-arg ref="AnotherBean"/>
</bean>
<bean id="AnotherBean" class="some.other.Class" />
nicholas' answer is right on the money for XML configuration. I'd just like to point out that when using annotations to configure your beans, it's not only simpler to do constructor injection, it's a much more natural way to do it:
class Foo {
private SomeDependency someDependency;
private OtherDependency otherDependency;
#Autowired
public Foo(SomeDependency someDependency, OtherDependency otherDependency) {
this.someDependency = someDependency;
this.otherDependency = otherDependency;
}
}
You might be able to do constructor based injection, i.e. something like this (taken from documentation found here)
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
but I'm not sure it will work.
If you are defining a JavaBean, you need to follow the convention and put a public no-arg constructor on it.
Almost all Java developers know that compiler adds a default constructor or better known as a no-argument constructor in every Java class, but many of them forget that it only does when you don't provide any other constructor. This means it becomes the developers' responsibility to add a no-argument constructor if he is adding explicit constructor. Now, Why it's important to provide default constructor in Java, What happens if your class doesn't have a no-argument constructor? Well, this is how it's asked in many Java interviews, most commonly as part of Spring and Hibernate interviews.
You should always define a no argument constructor in your Java class, even if you are writing an explicitly constructor, until you are absolutely sure that, it's won't be instantiated using reflection and instantiating it with no argument constructor is a bug, like in your example of Spring Bean.

How to wire proxy objects in spring?

I am using proxy beans with property-selectable transport protocol. My problem is that bean properties cannot be converted, but I really don't know why. This is the situation:
My property: service.protocol=rmi
<!-- This is the 'multiplexing' factory bean (using this because properties
cannot be used in bean names and aliases -->
<bean name="dbFormGenWindowComponent"
class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">
<property name="targetBeanName" value="dbFormGenWindowComponent-${service.protocol}invoker" />
</bean>
<!-- Here are the service invoker beans with two protocols: -->
<bean name="dbFormGenWindowComponent-rmiinvoker" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="${ringwindow.serviceURL.rmi}/${ringwindow.service.name}-dbFormGenWindowComponent"/>
<property name="serviceInterface" value="foo.bar.service.formgen.windows.FormGenWindowComponent" />
<property name="lookupStubOnStartup" value="false"/>
</bean>
The exception on startup is:
org.springframework.beans.TypeMismatchException:
Failed to convert property value of
type [$Proxy541] to required type
[foo.bar.service.formgen.windows.FormGenWindowComponent]
for property 'formGenWindowComponent';
nested exception is
java.lang.IllegalArgumentException:
Cannot convert value of type
[$Proxy541] to required type
[foo.bar.service.formgen.windows.FormGenWindowComponent]
for property 'formGenWindowComponent':
no matching editors or conversion
strategy found
I think nested factory beans should work fine. Do you have any idea how to get this work?
This usually happens when you have defined your injection point types to be concrete classes, rather than interfaces, but you are proxying based on interface. For example:
public interface Foo { .. }
public class FooImpl { .. } // this is declared as bean
public class Bar {
private FooImpl foo; // this fails
private Foo foo; // correct way
}
In the case of factory beans this may be due to the factory bean's return type being defined as a concrete class. If you can't change anything in the classes, you can configure spring to use cglib-proxying, by:
<aop:scoped-proxy> - inside a bean definition - this will configure proxies of the bean
<aop:aspectj-autoproxy proxy-target-class="true"> - changes this globally

Categories

Resources