diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java index 4f2b6b9e1..1014b3270 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java @@ -24,6 +24,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * Method interceptor to forward a delegation to bean property accessor methods to the property of a given target. @@ -38,7 +39,6 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor { * Creates a new {@link PropertyAccessingMethodInterceptor} for the given target object. * * @param target must not be {@literal null}. - * @param factory must not be {@literal null}. */ public PropertyAccessingMethodInterceptor(Object target) { @@ -54,6 +54,11 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); + + if (ReflectionUtils.isObjectMethod(method)) { + return invocation.proceed(); + } + PropertyDescriptor descriptor = BeanUtils.findPropertyForMethod(method); if (descriptor == null) { diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java index 7ab28f450..f942e1b90 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java @@ -89,13 +89,13 @@ public class ProxyProjectionFactory implements ProjectionFactory { * Returns the {@link MethodInterceptor} to add to the proxy. * * @param source must not be {@literal null}. - * @param target must not be {@literal null}. + * @param projectionType must not be {@literal null}. * @return */ - private MethodInterceptor getMethodInterceptor(Object source, Class target) { + private MethodInterceptor getMethodInterceptor(Object source, Class projectionType) { MethodInterceptor propertyInvocationInterceptor = new PropertyAccessingMethodInterceptor(source); - return new ProjectingMethodInterceptor(this, getSpelMethodInterceptorIfNecessary(target, + return new ProjectingMethodInterceptor(this, getSpelMethodInterceptorIfNecessary(source, projectionType, propertyInvocationInterceptor)); } @@ -103,21 +103,24 @@ public class ProxyProjectionFactory implements ProjectionFactory { * Inspects the given target type for methods with {@link Value} annotations and caches the result. Will create a * {@link SpelEvaluatingMethodInterceptor} if an annotation was found or return the delegate as is if not. * - * @param target the proxy target type. + * @param source The backing source object. + * @param projectionType the proxy target type. * @param delegate the root {@link MethodInterceptor}. * @return */ - private MethodInterceptor getSpelMethodInterceptorIfNecessary(Class target, MethodInterceptor delegate) { + private MethodInterceptor getSpelMethodInterceptorIfNecessary(Object source, Class projectionType, + MethodInterceptor delegate) { - if (!typeCache.containsKey(target)) { + if (!typeCache.containsKey(projectionType)) { AnnotationDetectionMethodCallback callback = new AnnotationDetectionMethodCallback(Value.class); - ReflectionUtils.doWithMethods(target, callback); + ReflectionUtils.doWithMethods(projectionType, callback); - typeCache.put(target, callback.hasFoundAnnotation()); + typeCache.put(projectionType, callback.hasFoundAnnotation()); } - return typeCache.get(target) ? new SpelEvaluatingMethodInterceptor(delegate, target, beanFactory) : delegate; + return typeCache.get(projectionType) ? new SpelEvaluatingMethodInterceptor(delegate, source, beanFactory) + : delegate; } /** diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java index d09eb1321..a43a0eff4 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java @@ -62,6 +62,16 @@ public class PropertyAccessingMethodInterceptorUnitTests { new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation); } + /** + * @see DATAREST-221 + */ + @Test + public void forwardsObjectMethodInvocation() throws Throwable { + + when(invocation.getMethod()).thenReturn(Object.class.getMethod("toString")); + new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation); + } + static class Source { String firstname; diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java index 9e4619fa8..79ea6ded1 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import org.junit.Test; import org.springframework.aop.TargetClassAware; +import org.springframework.beans.factory.annotation.Value; /** * Unit tests for {@link ProxyProjectionFactory}. @@ -67,15 +68,29 @@ public class ProxyProjectionFactoryUnitTests { factory.createProjection(new Object(), Object.class); } + /** + * @see DATAREST-221 + */ + @Test + public void exposesSpelInvokingMethod() { + + Customer customer = new Customer(); + customer.firstname = "Dave"; + customer.lastname = "Matthews"; + + CustomerExcerpt excerpt = factory.createProjection(customer, CustomerExcerpt.class); + assertThat(excerpt.getFullName(), is("Dave Matthews")); + } + static class Customer { - String firstname, lastname; - Address address; + public String firstname, lastname; + public Address address; } static class Address { - String zipCode, city; + public String zipCode, city; } interface CustomerExcerpt { @@ -83,6 +98,9 @@ public class ProxyProjectionFactoryUnitTests { String getFirstname(); AddressExcerpt getAddress(); + + @Value("#{target.firstname + ' ' + target.lastname}") + String getFullName(); } interface AddressExcerpt {