#91 - Improved proxying for method link references with Objenesis.

Upgraded to Spring 3.2.3 to be able to use inlined CGLib directly. We now use Objenesis to create the proxy instances which avoids the need for a default constructor.
This commit is contained in:
Oliver Gierke
2013-06-28 23:23:50 +02:00
parent 3068a03764
commit 4142140a37
5 changed files with 92 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,13 @@ import java.util.Arrays;
import java.util.Iterator;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.objenesis.ObjenesisStd;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -33,6 +37,8 @@ import org.springframework.util.ReflectionUtils;
*/
public class DummyInvocationUtils {
private static ObjenesisStd OBJENESIS = new ObjenesisStd();
public interface LastInvocationAware {
Iterator<Object> getObjectParameters();
@@ -46,7 +52,8 @@ public class DummyInvocationUtils {
*
* @author Oliver Gierke
*/
private static class InvocationRecordingMethodInterceptor implements MethodInterceptor, LastInvocationAware {
private static class InvocationRecordingMethodInterceptor implements MethodInterceptor, LastInvocationAware,
org.springframework.cglib.proxy.MethodInterceptor {
private static final Method GET_INVOCATIONS;
private static final Method GET_OBJECT_PARAMETERS;
@@ -69,29 +76,35 @@ public class DummyInvocationUtils {
this.objectParameters = parameters.clone();
}
/*
/*
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
if (GET_INVOCATIONS.equals(method)) {
return getLastInvocation();
} else if (GET_OBJECT_PARAMETERS.equals(method)) {
return getObjectParameters();
} else if (Object.class.equals(method.getDeclaringClass())) {
return invocation.proceed();
return ReflectionUtils.invokeMethod(method, obj, args);
}
this.invocation = invocation;
this.invocation = new SimpleMethodInvocation(method, args);
Class<?> returnType = method.getReturnType();
return returnType.cast(getProxyWithInterceptor(returnType, this));
}
/*
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Override
public Object invoke(org.aopalliance.intercept.MethodInvocation invocation) throws Throwable {
return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.core.DummyInvocationUtils.LastInvocationAware#getLastInvocation()
@@ -127,25 +140,72 @@ public class DummyInvocationUtils {
Assert.notNull(type, "Given type must not be null!");
MethodInterceptor interceptor = new InvocationRecordingMethodInterceptor(parameters);
InvocationRecordingMethodInterceptor interceptor = new InvocationRecordingMethodInterceptor(parameters);
return getProxyWithInterceptor(type, interceptor);
}
@SuppressWarnings("unchecked")
private static <T> T getProxyWithInterceptor(Class<?> type, MethodInterceptor interceptor) {
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);
private static <T> T getProxyWithInterceptor(Class<?> type, InvocationRecordingMethodInterceptor interceptor) {
if (type.isInterface()) {
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);
factory.addInterface(type);
} else {
factory.setProxyTargetClass(true);
factory.addInterface(LastInvocationAware.class);
factory.addAdvice(interceptor);
return (T) factory.getProxy();
}
factory.setTargetClass(type);
factory.addInterface(LastInvocationAware.class);
factory.addAdvice(interceptor);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(type);
enhancer.setInterfaces(new Class<?>[] { LastInvocationAware.class });
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
return (T) factory.getProxy();
Factory factory = (Factory) OBJENESIS.newInstance(enhancer.createClass());
factory.setCallbacks(new Callback[] { interceptor });
return (T) factory;
}
public interface MethodInvocation {
Object[] getArguments();
Method getMethod();
}
static class SimpleMethodInvocation implements MethodInvocation {
private final Method method;
private final Object[] arguments;
/**
* Creates a new {@link SimpleMethodInvocation} for the given {@link Method} and arguments.
*
* @param method
* @param arguments
*/
private SimpleMethodInvocation(Method method, Object[] arguments) {
this.arguments = arguments;
this.method = method;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation#getArguments()
*/
@Override
public Object[] getArguments() {
return arguments;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation#getMethod()
*/
@Override
public Method getMethod() {
return method;
}
}
}

View File

@@ -19,12 +19,12 @@ import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.hateoas.core.AnnotationAttribute;
import org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation;
import org.springframework.hateoas.core.MethodParameters;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

View File

@@ -25,13 +25,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodParameter;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.MethodLinkBuilderFactory;
import org.springframework.hateoas.core.AnnotationAttribute;
import org.springframework.hateoas.core.AnnotationMappingDiscoverer;
import org.springframework.hateoas.core.DummyInvocationUtils.LastInvocationAware;
import org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation;
import org.springframework.hateoas.core.LinkBuilderSupport;
import org.springframework.hateoas.core.MappingDiscoverer;
import org.springframework.hateoas.core.MethodParameters;