I currently am trying to proxy some existing JAX/RS resources, in order to allow me to use the Hibernate Validator's method validation support. However, when I proxy my class (currently using cglib 2.2), the FormParam annotation is not present on parameters in the proxy class, and so the JAX/RS runtime (apache wink) is not populating parameters. Here's some test code that shows this:
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import javassist.util.proxy.ProxyFactory;
public class ProxyTester {
@Target( { PARAMETER })
@Retention(RUNTIME)
public static @interface TestAnnotation {
}
public static interface IProxyMe {
void aMethod(@TestAnnotation int param);
}
public static class ProxyMe implements IProxyMe {
public void aMethod(@TestAnnotation int param) {
}
}
static void dumpAnnotations(String type, Object proxy, Object forObject,
String forMethod) {
String className = forObject.getClass().getName();
System.err.println(type + " proxy for Class: " + className);
for (Method method : proxy.getClass().getMethods()) {
if (method.getName().equals(forMethod)) {
final int paramCount = method.getParameterTypes().length;
System.err.println(" Method: " + method.getName() + " has "
+ paramCount + " parameters");
int i = 0;
for (Annotation[] paramAnnotations : method
.getParameterAnnotations()) {
System.err.println(" Param " + (i++) + " has "
+ paramAnnotations.length + " annotations");
for (Annotation annotation : paramAnnotations) {
System.err.println(" Annotation "
+ annotation.toString());
}
}
}
}
}
static Object javassistProxy(IProxyMe in) throws Exception {
ProxyFactory pf = new ProxyFactory();
pf.setSuperclass(in.getClass());
Class c = pf.createClass();
return c.newInstance();
}
static Object cglibProxy(IProxyMe in) throws Exception {
Object p2 = Enhancer.create(in.getClass(), in.getClass()
.getInterfaces(), new MethodInterceptor() {
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
return arg3.invokeSuper(arg0, arg2);
}
});
return p2;
}
static Object jdkProxy(final IProxyMe in) throws Exception {
return java.lang.reflect.Proxy.newProxyInstance(in.getClass()
.getClassLoader(), in.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return method.invoke(in, args);
}
});
}
public static void main(String[] args) throws Exception {
IProxyMe proxyMe = new ProxyMe();
dumpAnnotations("no", proxyMe, proxyMe, "aMethod");
dumpAnnotations("javassist", javassistProxy(proxyMe), proxyMe,
"aMethod");
dumpAnnotations("cglib", cglibProxy(proxyMe), proxyMe, "aMethod");
dumpAnnotations("jdk", jdkProxy(proxyMe), proxyMe, "aMethod");
}
}
This gives me the following output:
no proxy for Class: ProxyTester$ProxyMe Method: aMethod has 1 parameters Param 0 has 1 annotations Annotation @ProxyTester.TestAnnotation() javassist proxy for Class: ProxyTester$ProxyMe Method: aMethod has 1 parameters Param 0 has 0 annotations cglib proxy for Class: ProxyTester$ProxyMe Method: aMethod has 1 parameters Param 0 has 0 annotations jdk proxy for Class: ProxyTester$ProxyMe Method: aMethod has 1 parameters Param 0 has 0 annotations
Are there any other alternatives?
A dynamic proxy class is a class that implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface.
When a Spring JDK proxy is used, the join point annotation should be present on both the interface's method and the concrete class's method for the aspect to trigger correctly. The proves that both the interface and the concrete class require the join point annotation when a JDK proxy is used.
JDK dynamic proxy is available with the JDK. It can be only proxy by interface so target class needs to implement interface. In your is implementing one or more interface then spring will automatically use JDK dynamic proxies. On the other hand, CGLIB is a third party library which spring used for creating proxy.
From Spring documentation : Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice). If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used.
I suspect, annotations are not added dynamically to the proxy instances. (probably because it is not straightforward). However it is possible to get the annotations from actual method instance during invocation (or filtering). For example,
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyTester
{
@Target({ PARAMETER })
@Retention(RUNTIME)
public static @interface TestAnnotation {}
public static interface IProxyMe {
void aMethod(@TestAnnotation int param);
}
public static class ProxyMe implements IProxyMe {
public void aMethod(@TestAnnotation int param) {
System.out.println("Invoked " + param);
System.out.println("-----------------");
}
}
static void dumpAnnotations(String type, Object proxy, Object forObject, String forMethod)
{
String className = forObject.getClass().getName();
System.out.println(type + " proxy for Class: " + className);
for(Method method : proxy.getClass().getMethods()) {
if(method.getName().equals(forMethod)) {
printAnnotations(method);
}
}
}
static void printAnnotations(Method method)
{
int paramCount = method.getParameterTypes().length;
System.out.println("Method: " + method.getName() + " has " + paramCount + " parameters");
for(Annotation[] paramAnnotations : method.getParameterAnnotations())
{
System.out.println("Annotations: " + paramAnnotations.length);
for(Annotation annotation : paramAnnotations)
{
System.out.println(" Annotation " + annotation.toString());
}
}
}
static Object javassistProxy(IProxyMe in) throws Exception
{
ProxyFactory pf = new ProxyFactory();
pf.setSuperclass(in.getClass());
MethodHandler handler = new MethodHandler()
{
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable
{
if(thisMethod.getName().endsWith("aMethod"))
printAnnotations(thisMethod);
return proceed.invoke(self, args);
}
};
return pf.create(new Class<?>[0], new Object[0], handler);
}
static Object cglibProxy(IProxyMe in) throws Exception
{
Object p2 = Enhancer.create(in.getClass(), in.getClass().getInterfaces(),
new MethodInterceptor()
{
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
{
printAnnotations(arg1);
return arg3.invokeSuper(arg0, arg2);
}
});
return p2;
}
static Object jdkProxy(final IProxyMe in) throws Exception
{
return java.lang.reflect.Proxy.newProxyInstance(in.getClass().getClassLoader(), in.getClass().getInterfaces(),
new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
printAnnotations(method);
return method.invoke(in, args);
}
});
}
public static void main(String[] args) throws Exception
{
IProxyMe proxyMe = new ProxyMe();
IProxyMe x = (IProxyMe) javassistProxy(proxyMe);
IProxyMe y = (IProxyMe) cglibProxy(proxyMe);
IProxyMe z = (IProxyMe) jdkProxy(proxyMe);
dumpAnnotations("no", proxyMe, IProxyMe.class, "aMethod");
dumpAnnotations("javassist", x, IProxyMe.class, "aMethod");
dumpAnnotations("cglib", y, IProxyMe.class, "aMethod");
dumpAnnotations("jdk", z, IProxyMe.class, "aMethod");
System.out.println("<<<<< ---- Invoking methods ----- >>>>>");
x.aMethod(1);
y.aMethod(2);
z.aMethod(3);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With