最近有個需求,其實這個需求之前就有,好比定義了一個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太慢了):