Json過濾器(基於spring自定義註解的方式,對欄位進行過濾)
在實際的開發過程中,會經常遇到如下情景:
- 後臺需要給前端返回JSON資料,但是查詢出來返回的資料裡面有很多屬性是不需要的
本文通過自定義註解的方式進行實現,對response進行攔截,通過註解引數,設定欄位資訊(即,過濾哪些欄位,保留哪些欄位),並將bean自動封裝為json,作為結果返回。
實現的具體程式碼可以在github中直接下載執行:https://github.com/MonkeyJJC/JsonFilter
整體思路:
(1)通過ResponseBodyAdvice實現在響應體寫出之前做一些處理;比如,修改返回值、加密等(此處即進行引數解析,設定過濾器的相關引數,為後續訊息轉換器處理準備)
(2)自定義訊息轉換器HttpMessageConverter
(3)自定義訊息轉換器的配置,通過@Bean定義HttpMessageConverter是向專案中新增訊息轉換器,如果Spring掃描到HttpMessageConverter型別的bean,就會將它自動新增到呼叫鏈中。否則spring會使用預設的處理器
Controll層:
@RestController
public class Demo {
@GetMapping("user")
@SerializeField(clazz = User.class, includes = {"name", "id"})
public User user() {
User user = new User(1L, "jjc", "123456");
return user;
}
}
其中@SerializeField就是自定義的註解,通過註解的方式宣告是對User類進行過濾操作,留下的欄位是{“name”, “id”},先看一下實現效果:
{
id: 1,
name: "jjc"
}
註解定義
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SerializeField { Class clazz(); /** * 需要返回的欄位 * @return */ String[] includes() default {}; /** * 需要去除的欄位 * @return */ String[] excludes() default {}; }
通過ResponseBodyAdvice對結果攔截
對通過@ResponseBody返回的結果進行攔截(返回的結果即beforeBodyWrite中的Object,此處進行攔截,並進行相關處理)
@ControllerAdvice
public class JsonFilterResponseBodyAdvice implements ResponseBodyAdvice {
·····
@Override
public Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class converterType, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
JsonFilterObject jsonFilterObject = new JsonFilterObject();
if (null == object) {
return null;
}
if (!methodParameter.getMethod().isAnnotationPresent(SerializeField.class)) {
return object;
}
/**
* 處理類進行過濾處理
*/
if (methodParameter.getMethod().isAnnotationPresent(SerializeField.class)) {
/**
* java.lang.reflect.Method.getAnnotation(Class <T> annotationClass)
* @return: 如果存在於此元素,則返回該元素註釋指定的註釋型別,否則返回為null
* 在處理類中,會對應強轉為對應的型別,如SerializeField類
*/
Object obj = methodParameter.getMethod().getAnnotation(SerializeField.class);
handleAnnotation(SerializeField.class, obj, jsonFilterObject);
}
/**
* 不進行set,返回null,因為未初始化
*/
jsonFilterObject.setObject(object);
return jsonFilterObject;
}
private void handleAnnotation(Class clazz, Object object, JsonFilterObject jsonFilterObject) {
/**
* 獲取註解使用處的引數資訊,如@SerializeField(clazz = Address.class,includes = {"school", "home", "user"})
* 獲取clazz型別及includes等資訊
*/
}
自定義訊息轉換器
可以看到,springMVC通過訊息轉換器,進行最後的輸出,通過ResponseBodyAdvice攔截之後,解析到相關資訊,初始化了過濾器,在此處,自定義的訊息轉換器中,呼叫過濾器,進行過濾相關操作:
public class JsonFilterHttpMessageConverter extends FastJsonHttpMessageConverter {
private Charset charset;
private SerializerFeature[] features;
public JsonFilterHttpMessageConverter() {
super();
setSupportedMediaTypes(Arrays.asList(
new MediaType("application", "json", UTF8),
new MediaType("application", "*+json", UTF8),
new MediaType("application", "jsonp", UTF8),
new MediaType("application", "*+jsonp", UTF8)));
setCharset(UTF8);
setFeatures(SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteMapNullValue);
}
/**
*
* @param obj- the object to write to the output message
* @param outputMessage- the HTTP output message to write to
* @throws IOException- in case of I/O errors
* @throws HttpMessageNotWritableException- in case of conversion errors
* 官方地址:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/AbstractGenericHttpMessageConverter.html#writeInternal-T-org.springframework.http.HttpOutputMessage-
*/
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
if(obj instanceof JsonFilterObject){
JsonFilterObject jsonFilterObject = (JsonFilterObject) obj;
OutputStream out = outputMessage.getBody();
SimpleSerializerFilter simpleSerializerFilter = new SimpleSerializerFilter(jsonFilterObject.getIncludes(), jsonFilterObject.getExcludes());
/**
* JSON序列化介面toJSONString
* String toJSONString(Object, SerializeFilter, SerializerFeature...)
*/
String text = JSON.toJSONString(jsonFilterObject.getObject(), simpleSerializerFilter, features);
byte[] bytes = text.getBytes(this.charset);
out.write(bytes);
}else {
/**
* 未宣告@SerializeField註解
*/
OutputStream out = outputMessage.getBody();
String text = JSON.toJSONString(obj, this.features);
byte[] bytes = text.getBytes(this.charset);
out.write(bytes);
}
}
@Override
public void setCharset(Charset charset) {
this.charset = charset;
}
@Override
public void setFeatures(SerializerFeature... features) {
this.features = features;
}
}
完整程式碼可以從github中直接下載執行:https://github.com/MonkeyJJC/JsonFilter
對於複雜bean的過濾,如bean中欄位包含容器或其他bean的情況,後續會對輪子進行相關功能完善。
對於spring訊息轉換器等概念可以參考:
SpringMVC原始碼剖析(五)-訊息轉換器HttpMessageConverter