/*
 * Copyright 2015-2020 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.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.projection;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.AnnotationDetectionMethodCallback;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

A ProxyProjectionFactory that adds support to use Value-annotated methods on a projection interface to evaluate the contained SpEL expression to define the outcome of the method call.
Author:Oliver Gierke, Thomas Darimont, Mark Paluch, Jens Schauder
Since:1.10
/** * A {@link ProxyProjectionFactory} that adds support to use {@link Value}-annotated methods on a projection interface * to evaluate the contained SpEL expression to define the outcome of the method call. * * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch * @author Jens Schauder * @since 1.10 */
public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory implements BeanFactoryAware { private final Map<Class<?>, Boolean> typeCache = new ConcurrentHashMap<>(); private final SpelExpressionParser parser = new SpelExpressionParser(); private @Nullable BeanFactory beanFactory; /* * (non-Javadoc) * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } /* * (non-Javadoc) * @see org.springframework.data.projection.ProxyProjectionFactory#createProjectionInformation(java.lang.Class) */ @Override protected ProjectionInformation createProjectionInformation(Class<?> projectionType) { return new SpelAwareProjectionInformation(projectionType); }
Inspects the given target type for methods with Value annotations and caches the result. Will create a SpelEvaluatingMethodInterceptor if an annotation was found or return the delegate as is if not.
Params:
  • interceptor – the root MethodInterceptor.
  • source – The backing source object.
  • projectionType – the proxy target type.
Returns:
/** * 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 interceptor the root {@link MethodInterceptor}. * @param source The backing source object. * @param projectionType the proxy target type. * @return */
@Override protected MethodInterceptor postProcessAccessorInterceptor(MethodInterceptor interceptor, Object source, Class<?> projectionType) { return typeCache.computeIfAbsent(projectionType, SpelAwareProxyProjectionFactory::hasMethodWithValueAnnotation) ? new SpelEvaluatingMethodInterceptor(interceptor, source, beanFactory, parser, projectionType) : interceptor; }
Returns whether the given type as a method annotated with Value.
Params:
  • type – must not be null.
Returns:
/** * Returns whether the given type as a method annotated with {@link Value}. * * @param type must not be {@literal null}. * @return */
private static boolean hasMethodWithValueAnnotation(Class<?> type) { Assert.notNull(type, "Type must not be null!"); AnnotationDetectionMethodCallback<Value> callback = new AnnotationDetectionMethodCallback<>(Value.class); ReflectionUtils.doWithMethods(type, callback); return callback.hasFoundAnnotation(); } protected static class SpelAwareProjectionInformation extends DefaultProjectionInformation { protected SpelAwareProjectionInformation(Class<?> projectionType) { super(projectionType); } /* * (non-Javadoc) * @see org.springframework.data.projection.DefaultProjectionInformation#isInputProperty(java.beans.PropertyDescriptor) */ @Override protected boolean isInputProperty(PropertyDescriptor descriptor) { if (!super.isInputProperty(descriptor)) { return false; } Method readMethod = descriptor.getReadMethod(); if (readMethod == null) { return false; } return AnnotationUtils.findAnnotation(readMethod, Value.class) == null; } } }