1 /*
2  * Copyright 2015-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.data.web;
17
18 import java.util.Arrays;
19 import java.util.List;
20
21 import org.springframework.beans.BeansException;
22 import org.springframework.beans.MutablePropertyValues;
23 import org.springframework.beans.factory.BeanClassLoaderAware;
24 import org.springframework.beans.factory.BeanFactory;
25 import org.springframework.beans.factory.BeanFactoryAware;
26 import org.springframework.beans.factory.ObjectFactory;
27 import org.springframework.core.MethodParameter;
28 import org.springframework.core.annotation.AnnotatedElementUtils;
29 import org.springframework.core.convert.ConversionService;
30 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
31 import org.springframework.util.ClassUtils;
32 import org.springframework.web.bind.WebDataBinder;
33 import org.springframework.web.bind.support.WebDataBinderFactory;
34 import org.springframework.web.context.request.NativeWebRequest;
35 import org.springframework.web.method.annotation.ModelAttributeMethodProcessor;
36 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
37
38 /**
39  * {@link HandlerMethodArgumentResolver} to create Proxy instances for interface based controller method parameters.
40  *
41  * @author Oliver Gierke
42  * @since 1.10
43  */

44 public class ProxyingHandlerMethodArgumentResolver extends ModelAttributeMethodProcessor
45         implements BeanFactoryAware, BeanClassLoaderAware {
46
47     private static final List<String> IGNORED_PACKAGES = Arrays.asList("java""org.springframework");
48
49     private final SpelAwareProxyProjectionFactory proxyFactory;
50     private final ObjectFactory<ConversionService> conversionService;
51
52     /**
53      * Creates a new {@link PageableHandlerMethodArgumentResolver} using the given {@link ConversionService}.
54      *
55      * @param conversionService must not be {@literal null}.
56      */

57     public ProxyingHandlerMethodArgumentResolver(ObjectFactory<ConversionService> conversionService,
58             boolean annotationNotRequired) {
59
60         super(annotationNotRequired);
61
62         this.proxyFactory = new SpelAwareProxyProjectionFactory();
63         this.conversionService = conversionService;
64     }
65
66     /*
67      * (non-Javadoc)
68      * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
69      */

70     @Override
71     public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
72         this.proxyFactory.setBeanFactory(beanFactory);
73     }
74
75     /*
76      * (non-Javadoc)
77      * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
78      */

79     @Override
80     public void setBeanClassLoader(ClassLoader classLoader) {
81         this.proxyFactory.setBeanClassLoader(classLoader);
82     }
83
84     /*
85      * (non-Javadoc)
86      * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
87      */

88     @Override
89     public boolean supportsParameter(MethodParameter parameter) {
90
91         if (!super.supportsParameter(parameter)) {
92             return false;
93         }
94
95         Class<?> type = parameter.getParameterType();
96
97         if (!type.isInterface()) {
98             return false;
99         }
100
101         // Annotated parameter
102         if (parameter.getParameterAnnotation(ProjectedPayload.class) != null) {
103             return true;
104         }
105
106         // Annotated type
107         if (AnnotatedElementUtils.findMergedAnnotation(type, ProjectedPayload.class) != null) {
108             return true;
109         }
110
111         // Fallback for only user defined interfaces
112         String packageName = ClassUtils.getPackageName(type);
113
114         return !IGNORED_PACKAGES.stream().anyMatch(it -> packageName.startsWith(it));
115     }
116
117     /*
118      * (non-Javadoc)
119      * @see org.springframework.web.method.annotation.ModelAttributeMethodProcessor#createAttribute(java.lang.String, org.springframework.core.MethodParameter, org.springframework.web.bind.support.WebDataBinderFactory, org.springframework.web.context.request.NativeWebRequest)
120      */

121     @Override
122     protected Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory,
123             NativeWebRequest request) throws Exception {
124
125         MapDataBinder binder = new MapDataBinder(parameter.getParameterType(), conversionService.getObject());
126         binder.bind(new MutablePropertyValues(request.getParameterMap()));
127
128         return proxyFactory.createProjection(parameter.getParameterType(), binder.getTarget());
129     }
130
131     /*
132      * (non-Javadoc)
133      * @see org.springframework.web.method.annotation.ModelAttributeMethodProcessor#bindRequestParameters(org.springframework.web.bind.WebDataBinder, org.springframework.web.context.request.NativeWebRequest)
134      */

135     @Override
136     protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {}
137 }
138