问题描述
项目使用SpringMVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,有些属性我们不需要输出或者有些属性循环引用会造成无法输出。
例如:实体User其中包括用户名、密码、邮箱等,但是我们在输出用户信息不希望输出密码、邮箱信息;
例如:实体user和department是多对一的关系,user内保存着department的信息,那么json输出时会导致这两个实体数据的循环输出;
jackson默认可以使用JsonIgnoreProperties接口来定义要过滤的属性,然后使用ObjectMapper#addMixInAnnotations来设置对应实体对应的JsonIgnoreProperties接口,这样就能达到过滤的目的。可是这样很不爽,因为如果你对n个实体对应有m种过滤需求就至少要建n*m个JsonIgnoreProperties接口。
解决方案
主要逻辑如下图
大致处理流程:
1、使用自定义注解controller方法
2、然后定义aop捕获所有controller方法
3、当aop捕获到controller方法时调用JavassistFilterPropertyHandler#filterProperties方法;
4、filterProperties读取注解并根据自定义注解使用javassist创建JsonIgnoreProperties临时实现类(同时缓存到map内,下次可直接取出)并存入当前线程内(ThreadJacksonMixInHolder, 使用threadlocal实现),
5、在springmvc输出json的类内自定义ObjectMapper, 从当前线程内取出JsonIgnoreProperties临时类, 调用ObjectMapper# addMixInAnnotations使之起效
6、最后使用ObjectMapper输出
用法:
1、定义aop, 用来捕获springmvc的controller方法,
package com.xiongyingqi.json.filter.aop;
import com.xiongyingqi.jackson.FilterPropertyHandler;
import com.xiongyingqi.jackson.impl.JavassistFilterPropertyHandler;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
/**
* @author 瑛琪 <a href="http://xiongyingqi.com">xiongyingqi.com</a>
* @version 2013-9-27 下午5:41:12
*/
@Aspect
public class IgnorePropertyAspect {
public static final Logger LOGGER = Logger.getLogger(IgnorePropertyAspect.class);
@Pointcut("execution(* com.kingray.web.*.*(..))")
private void anyMethod() {
}
@Around("anyMethod()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object returnVal = pjp.proceed(); // 返回源结果
try {
FilterPropertyHandler filterPropertyHandler = new JavassistFilterPropertyHandler(true);
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
returnVal = filterPropertyHandler.filterProperties(method, returnVal);
} catch (Exception e) {
LOGGER.error(e);
e.printStackTrace();
}
return returnVal;
}
@AfterThrowing(pointcut = "anyMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println(" -------- AfterThrowing -------- ");
}
}
spring配置:
<!-- 启动mvc对aop的支持,使用aspectj代理 -->
<aop:aspectj-autoproxyproxy-target-class="true" />
<beanid="ignorePropertyAspect" class="com.xiongyingqi.json.filter.aop.IgnorePropertyAspect"></bean>
2、配置spring-mvc的messageconverter
<pre name="code" class="java"> <bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="cacheSeconds" value="0" />
<!--日期格式转换 -->
<property name="webBindingInitializer">
<bean class="com.kingray.spring.http.convert.DateConverter" />
</property>
<property name="messageConverters">
<list>
<bean
class="com.xiongyingqi.spring.http.convert.json.Jackson2HttpMessageConverter">
</bean>
<bean
class="com.xiongyingqi.spring.http.convert.json.JacksonHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.BufferedImageHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.ResourceHttpMessageConverter">
</bean>
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.feed.AtomFeedHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.feed.RssChannelHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.xml.SourceHttpMessageConverter">
</bean>
<bean
class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
</bean>
</list>
</property>
</bean>
3、重写spring的MappingJackson2HttpMessageConverter类,这样输出的json内容就能自定义
package com.xiongyingqi.spring.http.convert.json;
import java.io.IOException;
import com.xiongyingqi.jackson.helper.ThreadJacksonMixInHolder;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jack