最近有个需求,其实这个需求之前就有,好比定义了一个vo,包含了10个字段,前端
在接口A里,要返回所有字段;java
可是在接口B里呢,须要复用这个 vo, 可是只须要返回其中8个字段。git
可能呢,有些同窗会选择从新定义一个新的vo,但这样,会致使vo类数量特别多;你说,要是所有字段都返回吧,则会给前端同窗形成困扰。github
一、在controller上指定一个profile web
二、在profile要应用到的class类型中,在field上添加注解spring
三、请求接口,返回的结果,以下:json
四、若是注释掉注解那两行,则效果以下:mvc
一、在controller上指定profileapp
/** * 测试include类型的profile,这里指定了: * 激活profile为 includeProfile * User中,对应的field将会被序列化,其余字段都不会被序列化 */ @GetMapping("/test.do") @ActiveFastJsonProfileInController(profile = "includeProfile",clazz = User.class) public CommonMessage<User> test() { User user = new User(); user.setId(111L); user.setAge(8); user.setUserName("kkk"); user.setHeight(165); CommonMessage<User> message = new CommonMessage<>(); message.setCode("0000"); message.setDesc("成功"); message.setData(user); return message; }
二、在ActiveFastJsonProfileInController
注解的clazz指定的类中,对须要序列化的字段进行注解:ide
@Data public class User { @FastJsonFieldProfile(profiles = {"includeProfile"},profileType = FastJsonFieldProfileType.INCLUDE) private Long id; private String userName; private Integer age; @FastJsonFieldProfile(profiles = {"includeProfile"},profileType = FastJsonFieldProfileType.INCLUDE) private Integer height; }
三、请求结果以下:
{ code: "0000", data: { id: 111, height: 165 }, desc: "成功" }
思路以下:
controllerAdvice
,实现 org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
接口,对返回的responseBody
进行处理controllerAdvice
中,获取请求路径,而后根据请求路径,去第二步的map中,查询激活的profile和class信息responseBody
对象进行处理,不对排除集合中的字段序列化这么讲起来,仍是比较抽象,具体能够看第一章的效果截图。
spring boot版本为2.1.10,网上有不少文章,都是说的1.x版本时候的方法,在2.1版本并不适用。由于默认使用是jackson,因此咱们这里将fastjson的HttpMessageConverter的顺序提早了:
@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { super.extendMessageConverters(converters); FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); Charset defaultCharset = Charset.forName("utf-8"); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setCharset(defaultCharset); converter.setFastJsonConfig(fastJsonConfig); converter.setDefaultCharset(defaultCharset); //将fastjson的消息转换器提到第一位 converters.add(0, converter); } }
这里,要点是,获取到RequestMapping
注解上的url,以及对应方法上的自定义注解:
RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class); //获取handlerMapping的map Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods(); for (HandlerMethod handlerMethod : handlerMethods.values()) { Class<?> beanType = handlerMethod.getBeanType();//获取所在类 //获取方法上注解 RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class); }
上面构造了map后,自己能够直接保存到一个public static字段,但感受不是很优雅,因而,构造了一个bean(包含上述的map),注册到spring中:
//bean的类定义 @Data public class VoProfileRegistry { private ConcurrentHashMap<String,ActiveFastJsonProfileInController> hashmap = new ConcurrentHashMap<String,ActiveFastJsonProfileInController>(); } //动态注册到spring applicationContext.registerBean(VoProfileRegistry.class); VoProfileRegistry registry = myapplicationContext.getBean(VoProfileRegistry.class); registry.setHashmap(hashmap);
在controllerAdvice中,对返回的responseBody进行处理时,根据请求url,从上述的map中,获取profile等信息:
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice#beforeBodyWrite @Override public CommonMessage<Object> beforeBodyWrite(CommonMessage<Object> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { String requestPath = request.getURI().getPath(); log.info("path:{}",requestPath); VoProfileRegistry voProfileRegistry = applicationContext.getBean(VoProfileRegistry.class); ConcurrentHashMap<String, ActiveFastJsonProfileInController> hashmap = voProfileRegistry.getHashmap(); //从map中获取该url,激活的profile等信息 ActiveFastJsonProfileInController activeFastJsonProfileInControllerAnnotation = hashmap.get(requestPath); if (activeFastJsonProfileInControllerAnnotation == null) { log.info("no matched json profile,skip"); return body; } ......//进行具体的对responseBody进行过滤 }
若是使用 fastjson
的话,是支持propertyFilter
的,具体能够了解下,也是对字段进行include和exclude,但感受不是特别方便,尤为是粒度要支持到接口级别。
另外,原本,我也有另外一个方案:在controllerAdvice里,获取到要排除的字段集合后,设置到ThreadLocal变量中,而后修改fastjson的源码,(fastjson对类进行序列化时,要获取class的field集合,能够在那个地方,对field集合进行处理),可是吧,那样麻烦很多,想了想就算了。
你们有什么意见和建议均可以提,也欢迎加群讨论。
源码在码云上(github太慢了):