From b4b2e38f5c9179b5b79baff025177745debd8bc2 Mon Sep 17 00:00:00 2001 From: george georgovassilis Date: Thu, 21 Sep 2017 09:56:47 +0200 Subject: [PATCH] #26 refactored ProxyFactory API --- README.md | 21 +++++++++++++- .../BaseRestInvokerProxyFactoryBean.java | 29 ++++--------------- .../utils/CglibProxyFactory.java | 21 ++++++++++---- .../utils/DynamicJavaProxyFactory.java | 11 +++++++ .../springjsonmapper/utils/ProxyFactory.java | 14 ++++++++- .../support/BaseProxyClass.java | 10 +++++++ .../tests/AnnotationsCheckerTest.java | 2 ++ .../resources/test-context-annotations.xml | 14 +++++++-- 8 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 src/test/java/com/github/ggeorgovassilis/springjsonmapper/support/BaseProxyClass.java diff --git a/README.md b/README.md index 6c8e22b..c84f96b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Features: ## News +XXXX-XX-XX: Released 1.6. #26 CglibProxyFactory now really creating opaque proxies; refactored ProxyFactory API. + 2017-09-20: Released 1.5. #20 Backported to Java 6, #21 Separate integration tests, #22 HTTP method headers for JaxRs 2017-08-04: Released 1.4. Handling parametrised return types (credits to [Valentin Ozanne](https://github.com/ValentinOzanne)) @@ -508,13 +510,30 @@ Account joinAccounts(@RequestPart @RequestParam("account1") Account account1, @R ``` Whenever the ```joinAccounts``` method is invoked, the ```serverIp``` property will be looked up in the application context and replaced by its current value. Note that this mechanism works only for values of the ```@RequestMapping``` and ```@Path``` annotations. + #### Where can I find more examples? Have a look at mapping declarations for the unit test: https://github.com/ggeorgovassilis/spring-rest-invoker/tree/master/spring-rest-invoker/src/test/java/com/github/ggeorgovassilis/springjsonmapper/services #### I need proxies to extend a specific class -Since 1.0.RC it's possible to generate opaque proxies with cglib instead of the default dynamic proxies. Opaque proxies extend a concrete class and implement the REST mapping interface. In order to do so, specify the name of the class proxies should extend with the ```proxyTargetClass``` property of the factory proxy bean (i.e. the ```SpringRestInvokerProxyFactoryBean```). If you just need opaque proxies, use ``` java.lang.Object```. +Since 1.0.RC it's possible to generate opaque proxies with cglib instead of the default dynamic proxies. Opaque proxies extend a concrete class and implement the REST mapping interface. In order to do so, specify a ```ProxyFactory``` instance, e.g.: + +```xml + + + + + + + + + +``` + #### I specified some (other) annotations on the mapping interface but they are missing on the service proxy diff --git a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/BaseRestInvokerProxyFactoryBean.java b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/BaseRestInvokerProxyFactoryBean.java index 49b13b8..f8a0947 100644 --- a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/BaseRestInvokerProxyFactoryBean.java +++ b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/BaseRestInvokerProxyFactoryBean.java @@ -38,10 +38,8 @@ * {@link SpringRestInvokerProxyFactoryBean} and * {@link JaxRsInvokerProxyFactoryBean} * - * Will generate by default dynamic java proxies. Use - * {@link #setProxyTargetClass(ClassLoader, Class)} or - * {@link #setProxyTargetClass(Class)} in order to generate proxies extending a - * concrete class. + * Will generate by default dynamic java proxies. Use {@link #setProxyFactory(ProxyFactory)} + * for other implementation, e.g. for having proxies extend a concrete class. * * @see JaxRsInvokerProxyFactoryBean * @see SpringRestInvokerProxyFactoryBean @@ -87,26 +85,11 @@ protected UrlMapping getRequestMapping(Method method, Object[] args) { } /** - * Specify the class to extend - * - * @param classLoader - * Classloader to use - * @param c - * Proxies will extend this base class - */ - - public void setProxyTargetClass(ClassLoader classLoader, Class c) { - proxyFactory = new CglibProxyFactory(classLoader, c); - } - - /** - * Specify class to derive proxies from - * - * @param c - * Base class. Will use this class' classloader + * Optionally set the proxy factory to use. Defaults to {@link DynamicJavaProxyFactory} + * @param proxyFactory */ - public void setProxyTargetClass(Class c) { - setProxyTargetClass(c.getClassLoader(), c); + public void setProxyFactory(ProxyFactory proxyFactory) { + this.proxyFactory = proxyFactory; } @Override diff --git a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/CglibProxyFactory.java b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/CglibProxyFactory.java index 190beda..6207d22 100644 --- a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/CglibProxyFactory.java +++ b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/CglibProxyFactory.java @@ -16,16 +16,13 @@ public class CglibProxyFactory implements ProxyFactory { protected Class baseClass; protected ClassLoader classLoader; - public CglibProxyFactory(ClassLoader classLoader, Class baseClass) { - this.baseClass = baseClass; - this.classLoader = classLoader; - } - @Override - public Object createProxy(final ClassLoader classLoader, final Class[] interfaces, + public Object createProxy(ClassLoader classLoader, final Class[] interfaces, final InvocationHandler callback) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Object.class); + if (classLoader == null) + classLoader = Thread.currentThread().getContextClassLoader(); enhancer.setClassLoader(classLoader); enhancer.setCallback(new net.sf.cglib.proxy.InvocationHandler() { @@ -35,7 +32,19 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } }); enhancer.setInterfaces(interfaces); + if (baseClass!=null) + enhancer.setSuperclass(baseClass); return enhancer.create(); } + @Override + public void setProxyTargetClass(Class c) { + this.baseClass = c; + } + + @Override + public void setProxyTargetClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + } diff --git a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/DynamicJavaProxyFactory.java b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/DynamicJavaProxyFactory.java index fb99a2a..bd594a8 100644 --- a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/DynamicJavaProxyFactory.java +++ b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/DynamicJavaProxyFactory.java @@ -16,4 +16,15 @@ public Object createProxy(ClassLoader classLoader, Class[] interfaces, Invoca return Proxy.newProxyInstance(classLoader, interfaces, callback); } + @Override + public void setProxyTargetClass(Class c) { + throw new RuntimeException("Not implemented. Use an opque ProxyFactory implementation"); + } + + @Override + public void setProxyTargetClassLoader(ClassLoader classLoader) { + throw new RuntimeException("Not implemented. Use an opque ProxyFactory implementation"); + } + + } diff --git a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/ProxyFactory.java b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/ProxyFactory.java index 7c8d412..bc372b5 100644 --- a/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/ProxyFactory.java +++ b/src/main/java/com/github/ggeorgovassilis/springjsonmapper/utils/ProxyFactory.java @@ -25,5 +25,17 @@ public interface ProxyFactory { * @return Proxy */ Object createProxy(ClassLoader classLoader, Class[] interfaces, InvocationHandler callback); - + + /** + * Generated proxies will extend the class "c". Not all implementations support proxying + * classes (e.g. DynamicJavaProxyFactory) + * @param classLoader + */ + void setProxyTargetClass(Class c); + + /** + * Proxies will use this classloader for the proxied class + * @param classLoader + */ + void setProxyTargetClassLoader(ClassLoader classLoader); } diff --git a/src/test/java/com/github/ggeorgovassilis/springjsonmapper/support/BaseProxyClass.java b/src/test/java/com/github/ggeorgovassilis/springjsonmapper/support/BaseProxyClass.java new file mode 100644 index 0000000..18b780e --- /dev/null +++ b/src/test/java/com/github/ggeorgovassilis/springjsonmapper/support/BaseProxyClass.java @@ -0,0 +1,10 @@ +package com.github.ggeorgovassilis.springjsonmapper.support; + +/** + * Base class for proxies. Is used by opaque proxy test. + * @author george georgovassilis + * + */ +public abstract class BaseProxyClass { + +} diff --git a/src/test/java/com/github/ggeorgovassilis/springjsonmapper/tests/AnnotationsCheckerTest.java b/src/test/java/com/github/ggeorgovassilis/springjsonmapper/tests/AnnotationsCheckerTest.java index 3c6da72..9a71287 100644 --- a/src/test/java/com/github/ggeorgovassilis/springjsonmapper/tests/AnnotationsCheckerTest.java +++ b/src/test/java/com/github/ggeorgovassilis/springjsonmapper/tests/AnnotationsCheckerTest.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import com.github.ggeorgovassilis.springjsonmapper.services.BookService; +import com.github.ggeorgovassilis.springjsonmapper.support.BaseProxyClass; import static org.junit.Assert.*; @@ -69,6 +70,7 @@ public void testAnnotationsOnDynamicProxy() throws Exception { */ @Test public void testAnnotationsOnOpaqueProxy() throws Exception { + assertTrue(opaqueProxy instanceof BaseProxyClass); Method method = ReflectionUtils.findMethod(opaqueProxy.getClass(), "findBooksByTitle", new Class[] { String.class }); assertNotNull(method); diff --git a/src/test/resources/test-context-annotations.xml b/src/test/resources/test-context-annotations.xml index f727ff3..e8c8bee 100644 --- a/src/test/resources/test-context-annotations.xml +++ b/src/test/resources/test-context-annotations.xml @@ -15,14 +15,22 @@ - + - - + + + + + +