您的位置:首页 > 其它

机房设计方案

2009-07-11 16:49 288 查看
原文http://www.cnblogs.com/zcw-ios/articles/3343071.html

一、问题的提出。

项目使用Spring MVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,希望动态的过滤掉对象的某些属性。所谓动态,是指的运行时,不同的controler方法可以针对同一POJO过滤掉不同的属性。

以下是一个Controler方法的定义,使用@ResponseBody把获得的对象列表写入响应的输出流(当然,必须配置jackson的MappingJacksonHttpMessageConverter,来完成对象的序列化)
@RequestMapping(params = "method=getAllBmForList")
@ResponseBody
public List<DepartGenInfo> getAllBmForList(HttpServletRequest request,
HttpServletResponse response) throws Exception {

BmDto dto = bmglService.getAllBm();
return dto.getBmList();
}

POJO定义如下
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动态过滤
@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 {
}

CompanyFilter的定义如下:
serializationConfig.addMixInAnnotations();

这个实现方法看起来非常不简洁,需要在动态过滤的时候写不少代码,而且也改变了@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定义了混合的模板以及目标类。
@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如下:
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;
}

}

在Spring MVC中进行AOP的配置
<bean id="xunerJsonFilterAdvice" class="com.xunersoft.common.json.XunerJsonFilterAdvice"/>

<aop:config>
<aop:aspect id="jsonFilterAspect" ref="xunerJsonFilterAdvice">
<aop:pointcut id="jsonFilterPointcut" expression="execution(* com.xunersoft.webapp.rsgl.controller.*.*(..))"/>
<aop:around pointcut-ref="jsonFilterPointcut" method="doAround"/>
</aop:aspect>
</aop:config>

其中pointcut的expression能够匹配到目标类的方法。

在doAround方法中,需要获得当前引用的HttpResponse对象,因此使用以下方法解决:

创建一个WebContext工具类:
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);
}
}

定义一个Servlet Filter:
@Component("webContextFilter")
public class WebContextFilter implements Filter {

public void init(FilterConfig filterConfig) throws ServletException {

}

public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
ServletContext servletContext = request.getSession().getServletContext();
WebContext.create(request, response, servletContext);
chain.doFilter(request, response);
WebContext.clear();
}

@Override
public void destroy() {
// TODO Auto-generated method stub

}

}

别忘了在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: