【Java】自定义Json过滤器@SerializeField注解

场景描述:当我们查询一个类时,返回所有的字段值,然而当我们只取其中的某几个字段时,问题就来了,是不是要重新写SQL去查询?当然这也是可行的。spring给我们提供了一个相当不错的注解功能,为什么不去写一个自定义注解去完成这件事呢。

具体做法:通过自定义注解@SerializeField的方式实现,拦截response。通过设置注解参数,设置字段信息(过滤字段、保留字段),并将bean自动封装为json并返回。

整体思路:
1、通过ResponseBodyAdvice实现在响应体写出之前做一些处理;比如,修改返回值、加密等(此处即进行参数解析,设置过滤器的相关参数,为后续消息转换器处理准备)
2、自定义消息转换器HttpMessageConverter
3、自定义消息转换器的配置,通过@Bean定义HttpMessageConverter是向项目中添加消息转换器,如果Spring扫描到HttpMessageConverter类型的bean,就会将它自动添加到调用链中。否则spring会使用默认的处理器。

使用案例

@Controller
public class SerializedDemo{
    @GetMapping("grade")
    //自定义的注解,通过注解对Grade类进行过滤,保留字段{“gid”, "gname","director"}
    @SerializeField(clazz = Grade.class, includes = {"gid", "gname","director"})
    public grade queryGrade() {
        Grade grade= new Grade ("id001", "grade1", "ff_sdfq", "dir_zhang");
        return grade;
    }
}

运行结果:

{ id: "id001", name: "grade1", director:"dir_zhang"}

下面开始SerializeField注解的定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SerializeField {
    Class clazz();
    //要显示的字段(返回JSON中包含字段)
    String[] includes() default {};
    //不显示的字段(返回的JSON中不包含字段)
    String[] excludes() default {};
}

通过ResultResponseAdvice对其结果进行拦截

package com.baoming.advice;
import java.util.Arrays;
import java.util.HashSet;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.baoming.annotation.SerializeField;
import com.baoming.bean.JsonFilterObject;
import com.baoming.utils.JsonResp;


@ControllerAdvice
public class ResultResponseAdvice implements ResponseBodyAdvice<Object> {

	@Override
	public boolean supports(MethodParameter returnType, Class converterType) {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
			Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
		// TODO Auto-generated method stub
		JsonFilterObject jsonFilterObject = new JsonFilterObject();
		if (returnType.getMethod().isAnnotationPresent(SerializeField.class)) {
			
			Object obj = returnType.getMethod().getAnnotation(SerializeField.class);
            handleAnnotation(SerializeField.class, obj, jsonFilterObject);
            if (body instanceof JsonResp)
            	jsonFilterObject.setObject(body);
			else
				jsonFilterObject.setObject(JsonResp.success(body,"请求成功"));           
            return jsonFilterObject;
		} else {
			return body;
		}
	}
	private void handleAnnotation(Class<SerializeField> clazz, Object object, JsonFilterObject jsonFilterObject) {
        String[] includes = {};
        String[] excludes = {};
        Class<SerializeField> objClass = null;
        if (clazz.equals(SerializeField.class)) {
            SerializeField serializeField = (SerializeField) object;
            /**
             * 获取注解使用处的参数信息,如@SerializeField(clazz = Address.class,includes = {"school", "home", "user"})
             * 获取clazz及includes等信息
             */
            includes = serializeField.includes();
            excludes = serializeField.excludes();
            objClass = serializeField.clazz();
        }
        if (includes.length > 0 && excludes.length > 0) {
            //throw new IncludesAndExcludesConflictException("Can not use both includes and excludes in the same annotation!");
        } else if (includes.length > 0) {
            jsonFilterObject.getIncludes().put(objClass, new HashSet<String>(Arrays.asList(includes)));
        } else if (excludes.length > 0) {
            jsonFilterObject.getExcludes().put(objClass, new HashSet<String>(Arrays.asList(excludes)));
        }
    }
}

自定义消息转换器

package com.baoming.converter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

import com.baoming.bean.JsonFilterObject;
import com.baoming.filter.SimpleSerializerFilter;
import springfox.documentation.spring.web.json.Json;

import static com.alibaba.fastjson.util.IOUtils.UTF8;

/**
* 自定义消息转换器
*/
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
	 */
	@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 = "";
			if (obj instanceof Json) {
				Json json = (Json) obj;
				text = json.value();
			} else {
				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;
	}
}

SimpleSerializerFilter

package com.baoming.jsonfilter.filter;

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 对SimplePropertyPreFilter进行一层封装
 */
public class SimpleSerializerFilter extends SimplePropertyPreFilter {
    private Map<Class, HashSet<String>> includes;
    private  Map<Class, HashSet<String>> excludes;

    public SimpleSerializerFilter(Map<Class, HashSet<String>> includes, Map<Class, HashSet<String>> excludes) {
        this.includes = includes;
        this.excludes = excludes;
    }

    @Override
    public boolean apply(JSONSerializer serializer, Object source, String name) {
        if(!isEmpty(includes)){
            for (Map.Entry<Class, HashSet<String>> include : includes.entrySet()){
                Class objClass = include.getKey();
                Set<String> includeProp = include.getValue();
                if(objClass.isAssignableFrom(source.getClass())){
                    return includeProp.contains(name);
                }else {
                    continue;
                }
            }
        }
        if(!isEmpty(excludes)){
            for (Map.Entry<Class, HashSet<String>> exclude : excludes.entrySet()){
                Class objClass = exclude.getKey();
                Set<String> includeProp = exclude.getValue();
                if(objClass.isAssignableFrom(source.getClass())){
                    return !includeProp.contains(name);
                }else {
                    continue;
                }
            }
        }
        return true;
    }

    public boolean isEmpty(Map map){
        return map == null || map.size() < 1;
    }
}

文章参考:https://www.codetd.com/article/4232998 github
感谢这位大佬!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值