一、问题的提出。
项目使用Spring MVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,希望动态的过滤掉对象的某些属性。所谓动态,是指的运行时,不同的controler方法可以针对同一POJO过滤掉不同的属性。
以下是一个Controler方法的定义,使用@ResponseBody把获得的对象列表写入响应的输出流(当然,必须配置jackson的MappingJacksonHttpMessageConverter,来完成对象的序列化)
1
2
3
4
5
6
7
8
|
@RequestMapping (params = "method=getAllBmForList" )
@ResponseBody public List<DepartGenInfo> getAllBmForList(HttpServletRequest request,
HttpServletResponse response) throws Exception {
BmDto dto = bmglService.getAllBm();
return dto.getBmList();
} |
POJO定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class DepartGenInfo implements java.io.Serializable {
private String depid;
private String name;
private Company company;
//getter...
//setter...
} public class Company {
private String comid;
private String name;
<pre name= "code" class = "java" > //getter...
//setter...
} |
我希望在getAllBmForList返回时,过滤掉DepartGenInfo的name属性,以及company的comid属性。
jackson支持@JsonIgnore和@JsonIgnoreProperties注解,但是无法实现动态过滤。jackson给出了几种动态过滤的办法,我选择使用annotation mixin
•JSON View
•JSON Filter
•Annotation Mixin
二、使用annotation mixin动态过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@RequestMapping (params = "method=getAllBmForList" )
public void getAllBmForList(HttpServletRequest request,
HttpServletResponse response) throws Exception {
BmDto dto = bmglService.getAllBm();
ObjectMapper mapper = new ObjectMapper();
SerializationConfig serializationConfig = mapper.getSerializationConfig();
serializationConfig.addMixInAnnotations(DepartGenInfo. class ,
DepartGenInfoFilter. class );
serializationConfig.addMixInAnnotations(Company. class ,
CompanyFilter. class );
mapper.writeValue(response.getOutputStream(),dto.getBmList());
return ;
} |
DepartGenInfoFilter的定义如下:
@JsonIgnoreProperties (value={ "name" }) //希望动态过滤掉的属性
public interface DepartGenInfoFilter {
}<br><span> //CompanyFilter的定义如下: </span>
|
这个实现方法看起来非常不简洁,需要在动态过滤的时候写不少代码,而且也改变了@ResponseBody的运行方式,失去了REST风格,因此考虑到使用AOP来进行处理。
二、最终解决方案
先看下我想达到的目标,通过自定义注解的方式来控制动态过滤。
@XunerJsonFilters (value={ @XunerJsonFilter (mixin=DepartGenInfoFilter. class , target=DepartGenInfo. class )
, @XunerJsonFilter (mixin=CompanyFilter. class , target=Company. class )})
@RequestMapping (params = "method=getAllBmForList" )
@ResponseBody
public List getAllBmForList(HttpServletRequest request,
HttpServletResponse response) throws Exception {
BmDto dto = bmglService.getAllBm();
return dto.getBmList();
}
|
@XunerJsonFilters和@XunerJsonFilter是我定义的注解。@XunerJsonFilters是@XunerJsonFilter的集合,@XunerJsonFilter定义了混合的模板以及目标类。
1
2
3
4
5
6
7
8
9
|
@Retention (RetentionPolicy.RUNTIME)
public @interface XunerJsonFilters {
XunerJsonFilter[] value();
} @Retention (RetentionPolicy.RUNTIME)
public @interface XunerJsonFilter {
Class<?> mixin() default Object. class ;
Class<?> target() default Object. class ;
} |
当然,只是定义注解并没有什么意义。重要的是如何根据自定义的注解进行处理。我定义了一个AOP Advice如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class XunerJsonFilterAdvice {
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature msig = (MethodSignature) pjp.getSignature();
XunerJsonFilter annotation = msig.getMethod().getAnnotation(
XunerJsonFilter. class );
XunerJsonFilters annotations = msig.getMethod().getAnnotation(
XunerJsonFilters. class );
if (annotation == null && annotations == null ) {
return pjp.proceed();
}
ObjectMapper mapper = new ObjectMapper();
if (annotation != null ) {
Class<?> mixin = annotation.mixin();
Class<?> target = annotation.target();
if (target != null ) {
mapper.getSerializationConfig().addMixInAnnotations(target,
mixin);
} else {
mapper.getSerializationConfig().addMixInAnnotations(
msig.getMethod().getReturnType(), mixin);
}
}
if (annotations != null ) {
XunerJsonFilter[] filters= annotations.value();
for (XunerJsonFilter filter :filters){
Class<?> mixin = filter.mixin();
Class<?> target = filter.target();
if (target != null ) {
mapper.getSerializationConfig().addMixInAnnotations(target,
mixin);
} else {
mapper.getSerializationConfig().addMixInAnnotations(
msig.getMethod().getReturnType(), mixin);
}
}
}
try {
mapper.writeValue(WebContext.getInstance().getResponse()
.getOutputStream(), pjp.proceed());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return null ;
}
} |
其中pointcut的expression能够匹配到目标类的方法。
在doAround方法中,需要获得当前引用的HttpResponse对象,因此使用以下方法解决:
创建一个WebContext工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class WebContext {
private static ThreadLocal<WebContext> tlv = new ThreadLocal<WebContext>();
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
protected WebContext() {
}
public HttpServletRequest getRequest() {
return request;
}
public void setRequest(HttpServletRequest request) {
this .request = request;
}
public HttpServletResponse getResponse() {
return response;
}
public void setResponse(HttpServletResponse response) {
this .response = response;
}
public ServletContext getServletContext() {
return servletContext;
}
public void setServletContext(ServletContext servletContext) {
this .servletContext = servletContext;
}
private WebContext(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext) {
this .request = request;
this .response = response;
this .servletContext = servletContext;
}
public static WebContext getInstance() {
return tlv.get();
}
public static void create(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext) {
WebContext wc = new WebContext(request, response, servletContext);
tlv.set(wc);
}
public static void clear() {
tlv.set( null );
}
} |
别忘了在web.xml中增加这个filter。
OK,It is all。
四、总结
设计的一些要点:
1、要便于程序员使用。程序员根据业务逻辑需要过滤字段时,只需要定义个"Filter“,然后使用注解引入该Filter。
2、引入AOP来保持原来的REST风格。对于项目遗留的代码,不需要进行大幅度的修改,只需要增加注解来增加对过滤字段的支持。
仍需解决的问题:
按照目前的设计,定义的Filter不支持继承,每一种动态字段的业务需求就会产生一个Filter类,当类数量很多时,不便于管理。
五、参考资料
http://www.cowtowncoder.com/blog/archives/cat_json.html
http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring
相关推荐
- 另外,也可以通过全局配置`ObjectMapper`,启用`HtmlEscapingEnabled`属性,使Jackson在序列化时自动转义HTML。 4. **其他解决方案**: - 如果你使用的是Thymeleaf等模板引擎,它们通常会自动转义输出内容,...
@ResponseBody 和 @RequestBody 注解的区别 在 Spring 框架中,@ResponseBody 和 @RequestBody 是两个常用的注解,它们都用于处理 HTTP 请求和响应,但是它们的作用和使用场景却有所不同。 一、@ResponseBody 注解...
-- @ResponseBody() spring-mvc 3 xml 配置--> <!--处理 @ResponseBody 中文乱码问题 --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> ...
例如,如果你的控制器方法返回一个Java对象,添加`@ResponseBody`后,Spring会使用内置的HttpMessageConverter将对象转换为JSON字符串,使得前端可以解析。这对于RESTful API的开发尤其重要,因为它允许我们以数据...
下面我们将深入探讨`@ResponseBody`的使用、工作原理以及相关的配置和最佳实践。 1. **基本使用** `@ResponseBody`可以放在方法上,表示该方法的返回值将不经过视图解析,而是直接写入HTTP响应体。例如,我们有一...
"@ResponseBody 默认输出的误区的解答" @ResponseBody 是 Spring 框架中的一个注解,用于标注 Controller 中的方法,表示该方法的返回值将被写入到 HTTP 响应体中。但是,关于@ResponseBody 默认输出的误区一直存在...
在Spring MVC框架中,`@RequestBody` 和 `@ResponseBody` 是两个非常重要的注解,它们在处理HTTP请求和响应时起到了关键作用。本篇文章将详细解释这两个注解的工作原理、使用场景以及如何实现Java对象与XML/JSON数据...
这里,`@ResponseBody`使得`User`对象被转换成JSON格式,直接返回给客户端,而不是通过视图解析器查找对应的视图模板。 Spring MVC提供了多种`HttpMessageConverter`,包括处理XML的`Jaxb2...
@ResponseBody注解的功能与@RequestMapping中的consumes属性和@RequestBody注解刚好相反。@RequestBody注解用于方法的参数,表示HTTP请求体中的内容需要被转换成相应的Java对象。而@ResponseBody则用于方法的返回值...
在Spring中,可以通过配置`Jackson2ObjectMapperBuilder`或`ObjectMapper`来调整序列化和反序列化的行为,比如设置日期格式、忽略未知属性等。 6. **最佳实践**: - 为了减少依赖冲突,通常会将Jackson库的版本与...
标题中的“Spring MVC – Easy REST-Based JSON Services with @ResponseBody”是指使用Spring MVC框架构建基于REST的JSON服务,并通过使用`@ResponseBody`注解来简化这一过程。REST(Representational State ...
描述中提到的"springmvc进行Jason数据封装转化时可以即使用也是@responsebody",指的是在Spring MVC的控制器(Controller)中,我们可以使用`@ResponseBody`注解配合Jackson库,实现将Java对象直接转换为JSON格式的...
首先,我们可以尝试使用`@RequestMapping`注解的`produces`属性来指定响应内容的MIME类型和字符集。例如: ```java @RequestMapping(value = "testPersonalValidtor.do", produces = "application/json;charset=utf...
Spring MVC注解之@ResponseBody和@RequestBody详解 在Spring MVC框架中,@ResponseBody和@RequestBody是两个非常重要的注解,它们分别用于处理HTTP请求响应体和请求体的序列化和反序列化。下面,我们将详细介绍这两...
采用springmvc输出json时,需要加载这里的两个jar,采用@ResponseBody,可以将pojo类对象自动输出为json变量,也可以将一个List<POJO类>list输出为一个json数组,或json格式的任意输出,配合jquery easyuui的...
springmvc+国际化i18N+springmvc验证+jetbrick-template使用+@responsebody+谷歌guava: 1)围绕springmvc做的国际化 2)围绕springmvc做的验证 3)使用的jetbrick-template模板引擎 ……
在Spring Boot应用中,当你使用`@ResponseBody`注解将对象转换为JSON并发送到客户端时,日期(Date)类型的字段通常需要特殊处理,因为它们默认可能会被转换为Unix时间戳或者不友好的格式。本篇文章主要介绍了两种...
注解包含: 拦截器 , 过滤器 , 序列化 , @After , @AfterReturning , @AfterThrowing , @annotation , @Around , @Aspect , @Autowired , @Bean , @Before , @Component , @ComponentScan , @ComponentScans , @...
1)spring MVC 中@ResponseBody需要的所有JAR包 2)性能还不错的模板引擎jetbrick-template-2.x 2.x的所需的所有jar包 3)日志jar包:slf4j和logback 所有 4)阿里 druid 连接池jar包 5)mysql数据库链接驱动jar包 6...