How do you inject a proxy into a service? - java

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.

Related

Spring: apply #Autowired to property to eliminate setter

My understanding about the #Autowired annotation applying on a property of a bean is that by doing that we can eliminate the setter method. This seems valid when we choose to annotation based configuration: we just create a bean annotated with #Component and annotate #Autowired to its property of interest.
However I failed to do the same when I tested the idea using xml based configuration.
Here is what I put in the bean class:
#Autowired
private String message2;
public String getMessage2() {
return message2;
}
In the xml file:
<bean id="testBean" class="TestBean">
<property name="message2" value="Hello world!"/>
</bean>
the IDE complained "can't resolve the property" and failed to compile. Maybe using #Autowired with xml configuration is an odd marriage that is disallowed?
Anyone care to help me on this might-be-silly question?
If you don't want the setter, then get rid of the <property> element in your TestBean bean definition. That property requires that a setter be available to set the property. Because it's missing, if you actually tried to load the XML configuration, with a ClassPathXmlApplicationContext for example, you'd get this error
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property message2 of bean class [org.example.TestBean]: Bean property message2 is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
After removing <property>, declare an appropriate bean to inject
<bean id="message2" class="java.lang.String">
<constructor-arg value="Helllo world!"/>
</bean>
You'll also need
<context:annotation-config />
to register the AutowiredAnnotationBeanPostProcessor that handles processing of #Autowired.
Note that declaring a String bean is awkward. You'd be better served by using the <property> or some property resolution mechanism that pulls the value from a config file and assigns it through a #Value annotated field.
#Value("${config.message2}")
private String message2;

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.

Spring and passing parameters to factory-method in runtime

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

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

Can Spring's ObjectFactoryCreatingFactoryBean work with generics that refer to interfaces?

I'm using Spring's ObjectFactoryCreatingFactoryBean to retrieve a prototype scoped bean, as described in the Spring Documentation. Below is my applicationContext.
<bean id="exportFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName">
<idref local="export" />
</property>
</bean>
<bean id="export" class="com.someorg.ExportImpl" scope="prototype"/>
I autowire the exportFactory into a class like so:
#Autowired
#Qualifier("exportFactory")
private ObjectFactory<?> exportFactory;
And this works as expected. Each call to the exportFactory.getObject() method returns a new ExportImpl. On further inspect, a call to getObject() actually returns the following instance: org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory
Now, ExportImpl is an implementation of an Export interface. And when I attempt to to declare the exportFactory using generics, described below, I get an exception.
#Autowired
#Qualifier("exportFactory")
private ObjectFactory<Export> exportFactory;
Stacktrace:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.someorg.Export] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=exportFactory)}
The application context successfully loads with this configuration, and the exception is thrown when I call exportFactory.getObject(). Using the same configuration I can successfully retrieve an instance of ExportImpl, so I know that bean is correctly wired.
I'd like to know a) what is Spring doing here and b) is there a reason I'm unable to use an ObjectFactory with type parameter that's an interface?
It turns out that ObjectFactoryCreatingFactoryBean is not necessary when you obtain ObjectFactory via #Autowired. In this case ObjectFactory for your bean is created automatically, though I can't find any reference of this behaviour in the documentation.
So, the behaviour you observe can be explained as follows:
When you write #Autowired #Qualifier("exportFactory") ObjectFactory<?>, Spring creates ObjectFactory that returns a bean named exportFactory, which itself is an ObjectFactory returned by the ObjectFactoryCreatingFactoryBean (its class is org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory).
When you write #Autowired #Qualifier("exportFactory") ObjectFactory<Export>, Spring tries to find bean of type Export named exportFactory, and the search fails.
Change the qualifier to:
#Qualifier("export")

Categories

Resources