本篇文章对 HandlerMethodReturnValueHandler 接口实现了在控制层返回自定义 JSON 格式。
# 引入需要的依赖
<dependency> | |
<groupId>com.fasterxml.jackson.core</groupId> | |
<artifactId>jackson-databind</artifactId> | |
<version>2.13.2.2</version> | |
</dependency> | |
<dependency> | |
<groupId>com.fasterxml.jackson.datatype</groupId> | |
<artifactId>jackson-datatype-jsr310</artifactId> | |
<version>2.13.2</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
<version>2.2.6.RELEASE</version> | |
</dependency> |
# 自定义实现过滤器
package com.example.demo.filter; | |
import com.fasterxml.jackson.annotation.JsonFilter; | |
import com.fasterxml.jackson.core.JsonGenerator; | |
import com.fasterxml.jackson.databind.SerializerProvider; | |
import com.fasterxml.jackson.databind.ser.BeanPropertyFilter; | |
import com.fasterxml.jackson.databind.ser.FilterProvider; | |
import com.fasterxml.jackson.databind.ser.PropertyFilter; | |
import com.fasterxml.jackson.databind.ser.PropertyWriter; | |
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
@SuppressWarnings("deprecation") | |
@JsonFilter("JacksonFilter") | |
public class JacksonJsonFilter extends FilterProvider { | |
Map<Class<?>, Set<String>> includeMap = new HashMap<>(); | |
Map<Class<?>, Set<String>> excludeMap = new HashMap<>(); | |
public void include(Class<?> type, String[] field){ | |
addToMap(includeMap, type, field); | |
} | |
public void exclude(Class<?> type, String[] field){ | |
addToMap(excludeMap, type, field); | |
} | |
private void addToMap(Map<Class<?>, Set<String>> map, Class<?> type, String[] field) { | |
Set<String> fieldSet = map.getOrDefault(type, new HashSet<>()); | |
fieldSet.addAll(Arrays.asList(field)); | |
map.put(type, fieldSet); | |
} | |
@Override | |
public BeanPropertyFilter findFilter(Object o) { | |
throw new UnsupportedOperationException("Access to deprecated filters not supported"); | |
} | |
@Override | |
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) { | |
return new SimpleBeanPropertyFilter() { | |
@Override | |
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) | |
throws Exception { | |
if (apply(pojo.getClass(), writer.getName())) { | |
writer.serializeAsField(pojo, jgen, prov); | |
} else if (!jgen.canOmitFields()) { | |
writer.serializeAsOmittedField(pojo, jgen, prov); | |
} | |
} | |
}; | |
} | |
public boolean apply(Class<?> type, String name) { | |
Set<String> includeFields = includeMap.get(type); | |
Set<String> filterFields = excludeMap.get(type); | |
if (includeFields != null && includeFields.contains(name)) { | |
return true; | |
} else if (filterFields != null && !filterFields.contains(name)) { | |
return true; | |
} else if (includeFields == null && filterFields == null) { | |
return true; | |
} | |
return false; | |
} | |
} |
# 自定义返回 JSON 注解
JsonReturn
package com.example.demo.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Repeatable; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Target(ElementType.METHOD) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Repeatable(JsonReturns.class) // 让方法支持多重 @JSON 注解 | |
public @interface JsonReturn { | |
Class<?> type(); | |
String[] include() default {}; | |
String[] exclude() default {}; | |
} |
JsonReturns
package com.example.demo.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Target(ElementType.METHOD) | |
@Retention(RetentionPolicy.RUNTIME) | |
public @interface JsonReturns { | |
JsonReturn[] value(); | |
} |
# 封装 JSON 转换
我们来封装一个类,用作解析注解以及设置过滤器的
JsonReturnValueSerializer
package com.example.demo.serializer; | |
import com.example.demo.annotations.JsonReturn; | |
import com.example.demo.filter.JacksonJsonFilter; | |
import com.fasterxml.jackson.core.JsonProcessingException; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; | |
public class JsonReturnValueSerializer { | |
ObjectMapper objectMapper = null; | |
JacksonJsonFilter jacksonJsonFilter = new JacksonJsonFilter(); | |
public JsonReturnValueSerializer(){ | |
objectMapper = new ObjectMapper(); | |
objectMapper.registerModule(new JavaTimeModule()); | |
} | |
public void filter(Class<?> clazz, String[] include, String[] exclude){ | |
if (clazz == null){ | |
return; | |
} | |
if (include != null && include.length > 0){ | |
jacksonJsonFilter.include(clazz, include); | |
} | |
if (exclude != null && exclude.length > 0){ | |
jacksonJsonFilter.exclude(clazz, exclude); | |
} | |
objectMapper.addMixIn(clazz, jacksonJsonFilter.getClass()); | |
} | |
public void filter(JsonReturn json) { | |
this.filter(json.type(), json.include(), json.exclude()); | |
} | |
public String toJson(Object object) throws JsonProcessingException { | |
objectMapper.setFilterProvider(jacksonJsonFilter); | |
return objectMapper.writeValueAsString(object); | |
} | |
} |
# 实现 HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler
接口 Spring MVC 用于处理请求返回值 。 看一下这个接口的定义和描述,接口有两个方法supportsReturnType
用来判断 处理类 是否支持当前请求,handleReturnValue
就是具体返回逻辑的实现。
package com.example.demo.handle; | |
import com.example.demo.annotations.JsonReturn; | |
import com.example.demo.annotations.JsonReturns; | |
import com.example.demo.serializer.JsonReturnValueSerializer; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.core.MethodParameter; | |
import org.springframework.http.MediaType; | |
import org.springframework.web.context.request.NativeWebRequest; | |
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; | |
import org.springframework.web.method.support.ModelAndViewContainer; | |
import javax.servlet.http.HttpServletResponse; | |
import java.lang.annotation.Annotation; | |
import java.util.Arrays; | |
public class JsonReturnValueHandler implements HandlerMethodReturnValueHandler { | |
private HandlerMethodReturnValueHandler target; | |
public JsonReturnValueHandler(HandlerMethodReturnValueHandler target) { | |
this.target = target; | |
} | |
@Override | |
public boolean supportsReturnType(MethodParameter returnType) { | |
return target.supportsReturnType(returnType); | |
} | |
@Override | |
public void handleReturnValue(Object returnValue, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception { | |
// 设置这个就是最终的处理类了,处理完不再去找下一个类进行处理 | |
modelAndViewContainer.setRequestHandled(true); | |
// 获得注解并执行 filter 方法 最后返回 | |
HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class); | |
Annotation[] annotations = methodParameter.getMethodAnnotations(); | |
JsonReturnValueSerializer jsonReturnValueSerializer = new JsonReturnValueSerializer(); | |
Arrays.asList(annotations).forEach(annotation -> { | |
if (annotation instanceof JsonReturn){ | |
JsonReturn jsonReturn = (JsonReturn) annotation; | |
jsonReturnValueSerializer.filter(jsonReturn); | |
}else if (annotation instanceof JsonReturns){ | |
JsonReturns jsonReturns = (JsonReturns) annotation; | |
Arrays.asList(jsonReturns.value()).forEach(jsonReturnValueSerializer::filter); | |
} | |
}); | |
response.setContentType(MediaType.APPLICATION_JSON_VALUE); | |
String json = jsonReturnValueSerializer.toJson(returnValue); | |
target.handleReturnValue(json, methodParameter, modelAndViewContainer, nativeWebRequest); | |
} | |
} |
# 初始化 Bean
InitializingReturnValueHandler
package com.example.demo.init; | |
import com.example.demo.handle.JsonReturnValueHandler; | |
import org.springframework.beans.factory.InitializingBean; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; | |
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; | |
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class InitializingReturnValueHandler implements InitializingBean { | |
@Autowired | |
private RequestMappingHandlerAdapter adapter; | |
@Override | |
public void afterPropertiesSet() throws Exception { | |
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers(); | |
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers); | |
this.decorateHandles(handlers); | |
adapter.setReturnValueHandlers(handlers); | |
} | |
private void decorateHandles(List<HandlerMethodReturnValueHandler> handlers) { | |
for (HandlerMethodReturnValueHandler handler : handlers){ | |
if (handler instanceof RequestResponseBodyMethodProcessor){ | |
JsonReturnValueHandler jsonReturnValueHandler = new JsonReturnValueHandler((RequestResponseBodyMethodProcessor) handler); | |
int index = handlers.indexOf(handler); | |
handlers.set(index, jsonReturnValueHandler); | |
break; | |
} | |
} | |
} | |
} |
# 配置
JsonReturnConfig
package com.example.demo.config; | |
import com.example.demo.init.InitializingReturnValueHandler; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
@Configuration | |
public class JsonReturnConfig { | |
@Bean | |
public InitializingReturnValueHandler init(){ | |
return new InitializingReturnValueHandler(); | |
} | |
} |
# 测试
# 定义实体
DemoEntity
package com.example.demo.entity; | |
import java.io.Serializable; | |
import java.math.BigDecimal; | |
public class DemoEntity implements Serializable { | |
private Refer refer; | |
private BigDecimal price; | |
private String categories; | |
public BigDecimal getPrice() { | |
return price; | |
} | |
public void setPrice(BigDecimal price) { | |
this.price = price; | |
} | |
public String getCategories() { | |
return categories; | |
} | |
public void setCategories(String categories) { | |
this.categories = categories; | |
} | |
public Refer getRefer() { | |
return refer; | |
} | |
public void setRefer(Refer refer) { | |
this.refer = refer; | |
} | |
@Override | |
public String toString() { | |
return "DemoEntity{" + | |
"refer=" + refer + | |
", price=" + price + | |
", categories='" + categories + '\'' + | |
'}'; | |
} | |
} |
Refer
package com.example.demo.entity; | |
public class Refer { | |
private String id; | |
private String code; | |
private String name; | |
public String getId() { | |
return id; | |
} | |
public void setId(String id) { | |
this.id = id; | |
} | |
public String getCode() { | |
return code; | |
} | |
public void setCode(String code) { | |
this.code = code; | |
} | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
@Override | |
public String toString() { | |
return "Refer{" + | |
"id='" + id + '\'' + | |
", code='" + code + '\'' + | |
", name='" + name + '\'' + | |
'}'; | |
} | |
} |
# 定义控制层
DemoController
package com.example.demo.controller; | |
import com.example.demo.annotations.JsonReturn; | |
import com.example.demo.entity.DemoEntity; | |
import com.example.demo.entity.Refer; | |
import org.springframework.web.bind.annotation.GetMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import java.math.BigDecimal; | |
import java.util.UUID; | |
@RestController | |
public class DemoController { | |
@GetMapping("/queryAll") | |
public DemoEntity queryAll(){ | |
return initDemoEntity(); | |
} | |
@JsonReturn(type = DemoEntity.class, include = {"categories", "refer", "price"}) | |
@JsonReturn(type = Refer.class, exclude = {"id"}) | |
@GetMapping("/demo") | |
public DemoEntity demo(){ | |
return initDemoEntity(); | |
} | |
private DemoEntity initDemoEntity(){ | |
DemoEntity demoEntity = new DemoEntity(); | |
Refer refer = new Refer(); | |
refer.setId(UUID.randomUUID().toString()); | |
refer.setCode("DEMO"); | |
refer.setName("测试"); | |
demoEntity.setRefer(refer); | |
demoEntity.setPrice(new BigDecimal("10.00")); | |
demoEntity.setCategories("demo分类"); | |
return demoEntity; | |
} | |
} |
# 请求
localhost:8080/queryAll
页面返回结果
{"refer":{"id":"f3ca1a6a-b5e3-49b9-94aa-38a0e2160e6e","code":"DEMO","name":"测试"},"price":10.00,"categories":"demo分类"} |
localhost:8080/demo
页面返回结果
{"refer":{"code":"DEMO","name":"测试"},"price":10.00,"categories":"demo分类"} |