From 4142140a372789991c9e4ad4a935d7f2ca6e2c09 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 28 Jun 2013 23:23:50 +0200 Subject: [PATCH] #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. --- pom.xml | 9 +- .../hateoas/core/DummyInvocationUtils.java | 102 ++++++++++++++---- .../AnnotatedParametersParameterAccessor.java | 2 +- .../mvc/ControllerLinkBuilderFactory.java | 2 +- template.mf | 1 + 5 files changed, 92 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index e013db58..7bd1b2fd 100644 --- a/pom.xml +++ b/pom.xml @@ -58,13 +58,14 @@ UTF-8 - 3.1.4.RELEASE + 3.2.3.RELEASE 1.0.9 1.9.10 2.1.1 1.0 0.8.1 1.1.1 + 1.3 1.7.2 1.0.1 true @@ -102,6 +103,12 @@ 2.2.2 true + + + org.objenesis + objenesis + ${objenesis.version} + javax.servlet diff --git a/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java b/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java index e32538c2..91fb8649 100644 --- a/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java +++ b/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java @@ -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 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 getProxyWithInterceptor(Class type, MethodInterceptor interceptor) { - - ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE); + private static 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; + } } } diff --git a/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java b/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java index 764a7810..b00925c4 100644 --- a/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java +++ b/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java @@ -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; diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java index 2f76d723..882b4c68 100644 --- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java +++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java @@ -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; diff --git a/template.mf b/template.mf index abccb7d2..174672ce 100644 --- a/template.mf +++ b/template.mf @@ -11,5 +11,6 @@ Import-Template: net.minidev.json.*;version="${minidevjson.version:[=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0,2.0.0)";resolution:=optional, org.atteo.evo.inflector.*;version="${evo.version:[=.=.=,+1.0.0)}";resolution:=optional, + org.objenesis.*;version="${objenesis.version:[=.=.=,+1.0.0)}", org.springframework.*;version="${spring.version:[=.=.=,+1.0.0)}";resolution:=optional, org.codehaus.jackson.*;version="${jackson1.version:[=.=.=,+1.0.0)}";resolution:=optional