1
16 package org.springframework.data.web;
17
18 import java.io.IOException;
19 import java.lang.reflect.Type;
20 import java.util.Map;
21
22 import org.springframework.beans.BeansException;
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.core.ResolvableType;
27 import org.springframework.core.annotation.AnnotationUtils;
28 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
29 import org.springframework.http.HttpInputMessage;
30 import org.springframework.http.MediaType;
31 import org.springframework.http.converter.HttpMessageConverter;
32 import org.springframework.http.converter.HttpMessageNotReadableException;
33 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
34 import org.springframework.lang.Nullable;
35 import org.springframework.util.Assert;
36 import org.springframework.util.ConcurrentReferenceHashMap;
37
38 import com.fasterxml.jackson.databind.ObjectMapper;
39 import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
40
41
50 public class ProjectingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter
51 implements BeanClassLoaderAware, BeanFactoryAware {
52
53 private final SpelAwareProxyProjectionFactory projectionFactory;
54 private final Map<Class<?>, Boolean> supportedTypesCache = new ConcurrentReferenceHashMap<>();
55
56
59 public ProjectingJackson2HttpMessageConverter() {
60 this.projectionFactory = initProjectionFactory(getObjectMapper());
61 }
62
63
68 public ProjectingJackson2HttpMessageConverter(ObjectMapper mapper) {
69
70 super(mapper);
71
72 this.projectionFactory = initProjectionFactory(mapper);
73 }
74
75
82 private static SpelAwareProxyProjectionFactory initProjectionFactory(ObjectMapper mapper) {
83
84 Assert.notNull(mapper, "ObjectMapper must not be null!");
85
86 SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
87 projectionFactory
88 .registerMethodInvokerFactory(new JsonProjectingMethodInterceptorFactory(new JacksonMappingProvider(mapper)));
89
90 return projectionFactory;
91 }
92
93
97 @Override
98 public void setBeanClassLoader(ClassLoader classLoader) {
99 projectionFactory.setBeanClassLoader(classLoader);
100 }
101
102
106 @Override
107 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
108 projectionFactory.setBeanFactory(beanFactory);
109 }
110
111
115 @Override
116 public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
117
118 if (!canRead(mediaType)) {
119 return false;
120 }
121
122 ResolvableType owner = contextClass == null ? null : ResolvableType.forClass(contextClass);
123 Class<?> rawType = ResolvableType.forType(type, owner).resolve(Object.class);
124 Boolean result = supportedTypesCache.get(rawType);
125
126 if (result != null) {
127 return result;
128 }
129
130 result = rawType.isInterface() && AnnotationUtils.findAnnotation(rawType, ProjectedPayload.class) != null;
131 supportedTypesCache.put(rawType, result);
132
133 return result;
134 }
135
136
140 @Override
141 public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
142 return false;
143 }
144
145
149 @Override
150 public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
151 throws IOException, HttpMessageNotReadableException {
152 return projectionFactory.createProjection(ResolvableType.forType(type).resolve(Object.class),
153 inputMessage.getBody());
154 }
155 }
156