1、問題的提出。
項目使用Spring MVC框架,並用jackson庫處理JSON和POJO的轉換。在POJO轉化成JSON時,但願動態的過濾掉對象的某些屬性。所謂動態,是指的運行時,不一樣的controler方法能夠針對同一POJO過濾掉不一樣的屬性。
如下是一個Controler方法的定義,使用@ResponseBody把得到的對象列表寫入響應的輸出流(固然,必須配置jackson的MappingJacksonHttpMessageConverter,來完成對象的序列化)html
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定義java
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
2、使用annotation mixin動態過濾ios
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 {
}
//CompanyFilter的定義以下:
|
這個實現方法看起來很是不簡潔,須要在動態過濾的時候寫很多代碼,並且也改變了@ResponseBody的運行方式,失去了REST風格,所以考慮到使用AOP來進行處理。
2、最終解決方案
先看下我想達到的目標,經過自定義註解的方式來控制動態過濾。web
@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定義了混合的模板以及目標類。spring
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以下:express
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工具類:json
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。
4、總結
設計的一些要點:
一、要便於程序員使用。程序員根據業務邏輯須要過濾字段時,只須要定義個"Filter「,而後使用註解引入該Filter。
二、引入AOP來保持原來的REST風格。對於項目遺留的代碼,不須要進行大幅度的修改,只須要增長註解來增長對過濾字段的支持。
仍需解決的問題:
按照目前的設計,定義的Filter不支持繼承,每一種動態字段的業務需求就會產生一個Filter類,當類數量不少時,不便於管理。
5、參考資料
http://www.cowtowncoder.com/blog/archives/cat_json.html
http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_springapp