需求背景
最近的一個項目,在項目基本完工的階段,客戶提出要將所有業務操作的日志記錄到數據庫中,并且要提取一些業務的關鍵信息(比如交易單號)體現在日志中。
為了保證工期,在查閱了資料以后,決定用AOP+自定義注解的方式來完成這個需求。
準備工作
自定義注解需要依賴的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本號。
自定義注解
在項目下單獨建立了一個log包,來存放日志相關的內容
1
2
|
**.common.log.annotation //自定義注解存放位置 **.common.log.aop //aop工具類存放位置 |
在annotation包下面新建自定義注解類:
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
|
package **.common.log.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface XXXOperateLog { /** * 操作類型描述 * @return */ String operateTypeDesc() default ""; /** * 操作類型 * @return */ long operateType() default -1; /** * 模塊編碼 * @return */ String moudleCode() default "M30"; /** * 模塊名稱 * @return */ String moudleName() default "XX模塊"; /** * 業務類型 * @return */ String bussType() default ""; /** * 業務類型描述 * @return */ String bussTypeDesc() default ""; } |
在aop包下新建XXXOperateLogAop
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
package **.common.log.aop; import ** ;//省略 @Aspect @Component public class XXXOperateLogAop{ @Autowired SystemLogService systemLogService; HttpServletRequest request = null; Logger logger = LoggerFactory.getLogger(XXXOperateLogAop.class); ThreadLocal< Long > time = new ThreadLocal< Long >(); //用于生成操作日志的唯一標識,用于業務流程審計日志調用 public static ThreadLocal< String > tag = new ThreadLocal< String >(); //聲明AOP切入點,凡是使用了XXXOperateLog的方法均被攔截 @Pointcut("@annotation(**.common.log.annotation.XXXOperateLog)") public void log() { System.out.println("我是一個切入點"); } /** * 在所有標注@Log的地方切入 * @param joinPoint */ @Before("log()") public void beforeExec(JoinPoint joinPoint) { time.set(System.currentTimeMillis()); info(joinPoint); //設置日志記錄的唯一標識號 tag.set(UUID.randomUUID().toString()); request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } @After("log()") public void afterExec(JoinPoint joinPoint) { MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); logger.debug("標記為" + tag.get() + "的方法" + method.getName() + "運行消耗" + (System.currentTimeMillis() - time.get()) + "ms"); } //在執行目標方法的過程中,會執行這個方法,可以在這里實現日志的記錄 @Around("log()") public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); try { Object[] orgs = pjp.getArgs(); SystemLog valueReturn = null; for (int i = 0; i < orgs.length ; i++) { if(orgs[i] instanceof SystemLog){ valueReturn = (SystemLog) orgs[i]; } } if(valueReturn==null){ valueReturn = new SystemLog(); } if(valueReturn!=null&&request!=null){ MethodSignature ms = (MethodSignature) pjp.getSignature(); Method method = ms .getMethod(); //獲取注解的操作日志信息 XXXOperateLog log = method .getAnnotation(XXXOperateLog.class); String businessType = log .bussType(); String businessDesc = log .bussTypeDesc(); HashMap requestMap = ServletUtils .getParametersToHashMap(request) ; //從參數中尋找業務類型 if(businessType.equals("")) { Object objBusinessType = requestMap .get("business_type"); businessType = objBusinessType == null ? "" : objBusinessType.toString(); } //從執行結果的申請單中找業務類型 Object apply = request .getAttribute("apply") ; if(apply != null){ JSONObject obj = JSONFactory .toJSONAbstractEntity(apply); if(obj != null) { valueReturn.setOtherDesc("申請單號:"+obj.getString("apply_no")); if(businessType.equals("")) { businessType = obj .getString("business_type"); } } } //從方法的執行過程參數中找業務類型(一般是手動設置) if(businessType.equals("")) { businessType = (String) request.getAttribute("business_type"); businessType = businessType == null ? "" : businessType; } if(!businessType.equals("") && businessDesc.equals("")) { businessDesc = XXXSysConstant .BUSINESS_TYPE.getName(businessType); } valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType)); valueReturn.setBussTypeDesc(businessDesc); valueReturn.setMoudleCode(log.moudleCode()); valueReturn.setMoudleName(log.moudleName()); valueReturn.setOperateResult(XXXSysConstant.YesOrNo.YES); valueReturn.setOperateType(log.operateType()); valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId()); valueReturn.setOperateTypeDesc(log.operateTypeDesc()); valueReturn.setRequestIp(getRemoteHost(request)); valueReturn.setRequestUrl(request.getRequestURI()); valueReturn.setServerIp(request.getLocalAddr()); valueReturn.setUids(tag.get()); //保存操作日志 systemLogService.saveSystemLog(valueReturn); }else{ logger.info("不記錄日志信息"); } //保存操作結果 } catch (Exception e) { e.printStackTrace(); } return ret; } //記錄異常日志 @AfterThrowing( pointcut = "log()" , throwing = "e" ) public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { try { info(joinPoint); Object[] orgs = joinPoint .getArgs(); SystemLog valueReturn = null ; for (int i = 0 ; i < orgs.length; i++) { if(orgs[i] instanceof SystemLog){ valueReturn = (SystemLog) orgs[i]; } } if(valueReturn==null){ valueReturn = new SystemLog(); } if(valueReturn!=null&&request!=null){ MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms .getMethod(); XXXOperateLog log = method .getAnnotation(XXXOperateLog.class); String businessType = log .bussType(); String businessDesc = log .bussTypeDesc(); if(businessType.equals("")) { Object objBusinessType = ServletUtils .getParametersToHashMap(request).get("business_type"); businessType = objBusinessType == null ? "" : objBusinessType.toString(); businessDesc = XXXSysConstant .BUSINESS_TYPE.getName(businessType); } valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType)); valueReturn.setBussTypeDesc(businessDesc); valueReturn.setMoudleCode(log.moudleCode()); valueReturn.setMoudleName(log.moudleName()); valueReturn.setOperateType(log.operateType()); valueReturn.setOperateTypeDesc(log.operateTypeDesc()); valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId()); valueReturn.setOperateResult(XXXSysConstant.YesOrNo.NO); String errMes = e.getMessage(); if(errMes!=null && errMes.length()>800){ errMes = errMes.substring(0, 800); } valueReturn.setErrorMessage(errMes); valueReturn.setRequestIp(getRemoteHost(request)); valueReturn.setRequestUrl(request.getRequestURI()); valueReturn.setServerIp(request.getLocalAddr()); valueReturn.setUids(tag.get()); systemLogService.saveSystemLog(valueReturn); }else{ logger.info("不記錄日志信息"); } } catch (Exception e1) { e1.printStackTrace(); } } private void info(JoinPoint joinPoint) { logger.debug("--------------------------------------------------"); logger.debug("King:\t" + joinPoint.getKind()); logger.debug("Target:\t" + joinPoint.getTarget().toString()); Object[] os = joinPoint.getArgs(); logger.debug("Args:"); for (int i = 0; i < os.length ; i++) { logger.debug("\t==>參數[" + i + "]:\t" + os[i].toString()); } logger.debug("Signature:\t" + joinPoint.getSignature()); logger.debug("SourceLocation:\t" + joinPoint.getSourceLocation()); logger.debug("StaticPart:\t" + joinPoint.getStaticPart()); logger.debug("--------------------------------------------------"); } /** * 獲取遠程客戶端Ip * @param request * @return */ private String getRemoteHost(javax.servlet.http.HttpServletRequest request){ String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; } } |
修改配置文件spring-mvc.xml,添加如下配置
1
2
3
4
5
6
7
|
<!-- 開啟AOP攔截 --> < aop:aspectj-autoproxy proxy-target-class = "true" /> < mvc:annotation-driven /> <!-- 定義Spring描述Bean的范圍 --> < context:component-scan base-package = "**.common.log" > < context:include-filter type = "annotation" expression = "org.springframework.stereotype.Controller" /> </ context:component-scan > |
需要注意的是,上述配置必須放在同一個xml文件里面,要么spring-mvc.xml,要么spring-context.xml,否則可能不生效,暫時還未查明是為什么。
注解的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@XXXOperateLog( bussType=XXXSysConstant.BUSINESS_TYPE.YYYY ,bussTypeDesc="業務類型描述" ,operateType = XXXSysConstant.LogOperateType.QUERY ,operateTypeDesc = "操作描述" ) @RequestMapping(value = "/**/**/queryXXXXX4DataGrid.json", method = RequestMethod.POST) public void queryXXXXX4DataGrid(HttpServletRequest request, HttpServletResponse arg1, Model model, Writer writer) { logger.info("==========驗票查詢(出庫)交易信息 開始====================="); try { //do something for business } catch (SystemException se) { throw se; } catch (BusinessException be) { throw be; } catch (Exception e) { throw new SystemException(e); } } |
以上這篇springMVC自定義注解,用AOP來實現日志記錄的方法就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/yang_lover/article/details/53037323