今天繼續實現AOP,到這里我個人認為是最靈活,可擴展的方式了,就拿日志管理來說,用Spring AOP 自定義注解形式實現日志管理。廢話不多說,直接開始!!!
關于配置我還是的再說一遍。
在applicationContext-mvc.xml中要添加的
1
2
3
4
5
6
7
8
9
10
11
|
< mvc:annotation-driven /> <!-- 激活組件掃描功能,在包com.gcx及其子包下面自動掃描通過注解配置的組件 --> < context:component-scan base-package = "com.gcx" /> <!-- 啟動對@AspectJ注解的支持 --> <!-- proxy-target-class等于true是強制使用cglib代理,proxy-target-class默認是false,如果你的類實現了接口 就走JDK代理,如果沒有,走cglib代理 --> <!-- 注:對于單利模式建議使用cglib代理,雖然JDK動態代理比cglib代理速度快,但性能不如cglib --> <!--如果不寫proxy-target-class="true"這句話也沒問題--> < aop:aspectj-autoproxy proxy-target-class = "true" /> <!--切面--> < bean id = "systemLogAspect" class = "com.gcx.annotation.SystemLogAspect" ></ bean > |
接下來開始編寫代碼。
創建日志類實體
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
|
public class SystemLog { private String id; private String description; private String method; private Long logType; private String requestIp; private String exceptioncode; private String exceptionDetail; private String params; private String createBy; private Date createDate; public String getId() { return id; } public void setId(String id) { this.id = id == null ? null : id.trim(); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description == null ? null : description.trim(); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method == null ? null : method.trim(); } public Long getLogType() { return logType; } public void setLogType(Long logType) { this.logType = logType; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this.requestIp = requestIp == null ? null : requestIp.trim(); } public String getExceptioncode() { return exceptioncode; } public void setExceptioncode(String exceptioncode) { this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim(); } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim(); } public String getParams() { return params; } public void setParams(String params) { this.params = params == null ? null : params.trim(); } public String getCreateBy() { return createBy; } public void setCreateBy(String createBy) { this.createBy = createBy == null ? null : createBy.trim(); } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } } |
編寫dao接口
1
2
3
4
5
6
7
8
9
10
|
package com.gcx.dao; import com.gcx.entity.SystemLog; public interface SystemLogMapper { int deleteByPrimaryKey(String id); int insert(SystemLog record); int insertSelective(SystemLog record); SystemLog selectByPrimaryKey(String id); int updateByPrimaryKeySelective(SystemLog record); int updateByPrimaryKey(SystemLog record); } |
編寫service層
1
2
3
4
5
6
7
8
9
10
11
|
package com.gcx.service; import com.gcx.entity.SystemLog; public interface SystemLogService { int deleteSystemLog(String id); int insert(SystemLog record); int insertTest(SystemLog record); SystemLog selectSystemLog(String id); int updateSystemLog(SystemLog record); } |
編寫service實現類serviceImpl
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 com.gcx.service.impl; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.gcx.annotation.Log; import com.gcx.dao.SystemLogMapper; import com.gcx.entity.SystemLog; import com.gcx.service.SystemLogService; @Service("systemLogService") public class SystemLogServiceImpl implements SystemLogService { @Resource private SystemLogMapper systemLogMapper; @Override public int deleteSystemLog(String id) { return systemLogMapper.deleteByPrimaryKey(id); } @Override public int insert(SystemLog record) { return systemLogMapper.insertSelective(record); } @Override public SystemLog selectSystemLog(String id) { return systemLogMapper.selectByPrimaryKey(id); } @Override public int updateSystemLog(SystemLog record) { return systemLogMapper.updateByPrimaryKeySelective(record); } @Override public int insertTest(SystemLog record) { return systemLogMapper.insert(record); } } |
到這里基本程序編寫完畢
下面開始自定義注解
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.gcx.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** 要執行的操作類型比如:add操作 **/ public String operationType() default ""; /** 要執行的具體操作比如:添加用戶 **/ public String operationName() default ""; } |
下面編寫切面
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
package com.gcx.annotation; import java.lang.reflect.Method; import java.util.Date; import java.util.UUID; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.gcx.entity.SystemLog; import com.gcx.entity.User; import com.gcx.service.SystemLogService; import com.gcx.util.JsonUtil; /** * @author 楊建 * @E-mail: email * @version 創建時間:2015-10-19 下午4:29:05 * @desc 切點類 */ @Aspect @Component public class SystemLogAspect { //注入Service用于把日志保存數據庫 @Resource //這里我用resource注解,一般用的是@Autowired,他們的區別如有時間我會在后面的博客中來寫 private SystemLogService systemLogService; private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class); //Controller層切點 @Pointcut("execution (* com.gcx.controller..*.*(..))") public void controllerAspect() { } /** * 前置通知 用于攔截Controller層記錄用戶的操作 * * @param joinPoint 切點 */ @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint) { System.out.println("==========執行controller前置通知==============="); if(logger.isInfoEnabled()){ logger.info("before " + joinPoint); } } //配置controller環繞通知,使用在方法aspect()上注冊的切入點 @Around("controllerAspect()") public void around(JoinPoint joinPoint){ System.out.println("==========開始執行controller環繞通知==============="); long start = System.currentTimeMillis(); try { ((ProceedingJoinPoint) joinPoint).proceed(); long end = System.currentTimeMillis(); if(logger.isInfoEnabled()){ logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!"); } System.out.println("==========結束執行controller環繞通知==============="); } catch (Throwable e) { long end = System.currentTimeMillis(); if(logger.isInfoEnabled()){ logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage()); } } } /** * 后置通知 用于攔截Controller層記錄用戶的操作 * * @param joinPoint 切點 */ @After("controllerAspect()") public void after(JoinPoint joinPoint) { /* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); */ //讀取session中的用戶 // User user = (User) session.getAttribute("user"); //請求的IP //String ip = request.getRemoteAddr(); User user = new User(); user.setId(1); user.setName("張三"); String ip = "127.0.0.1"; try { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String operationType = ""; String operationName = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operationType = method.getAnnotation(Log.class).operationType(); operationName = method.getAnnotation(Log.class).operationName(); break; } } } //*========控制臺輸出=========*// System.out.println("=====controller后置通知開始====="); System.out.println("請求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); System.out.println("方法描述:" + operationName); System.out.println("請求人:" + user.getName()); System.out.println("請求IP:" + ip); //*========數據庫日志=========*// SystemLog log = new SystemLog(); log.setId(UUID.randomUUID().toString()); log.setDescription(operationName); log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); log.setLogType((long)0); log.setRequestIp(ip); log.setExceptioncode( null); log.setExceptionDetail( null); log.setParams( null); log.setCreateBy(user.getName()); log.setCreateDate(new Date()); //保存數據庫 systemLogService.insert(log); System.out.println("=====controller后置通知結束====="); } catch (Exception e) { //記錄本地異常日志 logger.error("==后置通知異常=="); logger.error("異常信息:{}", e.getMessage()); } } //配置后置返回通知,使用在方法aspect()上注冊的切入點 @AfterReturning("controllerAspect()") public void afterReturn(JoinPoint joinPoint){ System.out.println("=====執行controller后置返回通知====="); if(logger.isInfoEnabled()){ logger.info("afterReturn " + joinPoint); } } /** * 異常通知 用于攔截記錄異常日志 * * @param joinPoint * @param e */ @AfterThrowing(pointcut = "controllerAspect()", throwing="e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { /*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //讀取session中的用戶 User user = (User) session.getAttribute(WebConstants.CURRENT_USER); //獲取請求ip String ip = request.getRemoteAddr(); */ //獲取用戶請求方法的參數并序列化為JSON格式字符串 User user = new User(); user.setId(1); user.setName("張三"); String ip = "127.0.0.1"; String params = ""; if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { for ( int i = 0; i < joinPoint.getArgs().length; i++) { params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";"; } } try { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String operationType = ""; String operationName = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operationType = method.getAnnotation(Log.class).operationType(); operationName = method.getAnnotation(Log.class).operationName(); break; } } } /*========控制臺輸出=========*/ System.out.println("=====異常通知開始====="); System.out.println("異常代碼:" + e.getClass().getName()); System.out.println("異常信息:" + e.getMessage()); System.out.println("異常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); System.out.println("方法描述:" + operationName); System.out.println("請求人:" + user.getName()); System.out.println("請求IP:" + ip); System.out.println("請求參數:" + params); /*==========數據庫日志=========*/ SystemLog log = new SystemLog(); log.setId(UUID.randomUUID().toString()); log.setDescription(operationName); log.setExceptioncode(e.getClass().getName()); log.setLogType((long)1); log.setExceptionDetail(e.getMessage()); log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); log.setParams(params); log.setCreateBy(user.getName()); log.setCreateDate(new Date()); log.setRequestIp(ip); //保存數據庫 systemLogService.insert(log); System.out.println("=====異常通知結束====="); } catch (Exception ex) { //記錄本地異常日志 logger.error("==異常通知異常=="); logger.error("異常信息:{}", ex.getMessage()); } /*==========記錄本地異常日志==========*/ logger.error("異常方法:{}異常代碼:{}異常信息:{}參數:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params); } } |
我這里寫的比較全,前置通知,環繞通知,后置通知,異常通知,后置飯后通知,都寫上了,在我們實際編寫中不寫全也沒事,我習慣上把記錄日志的邏輯寫在后置通知里面,我看網上也有些在前置通知里面的,但我感覺寫在后置通知里比較好。
下面開始在controller中加入自定義的注解!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.gcx.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.gcx.annotation.Log; import com.gcx.service.UserService; @Controller @RequestMapping("userController") public class UserController { @Autowired private UserService userService; @RequestMapping("testAOP") @Log(operationType="add操作:",operationName="添加用戶") public void testAOP(String userName,String password){ userService.addUser(userName, password); } } |
下面編寫測試類
1
2
3
4
5
6
7
8
|
@Test public void testAOP1(){ //啟動Spring容器 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"}); //獲取service或controller組件 UserController userController = (UserController) ctx.getBean("userController"); userController.testAOP("zhangsan", "123456"); } |
數據庫數據:
我原本想寫兩個切點,一個是service層,一個是controller層,service層是用來記錄異常信息的日志,而controller層的是用來記錄功能的日志,運行結果如下。
這樣做的話不知道在實際的項目中運行效率好不好,在這里請看到博客的大牛給點建議!!
以上這篇spring AOP自定義注解方式實現日志管理的實例講解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/jianjianyang/p/4910851.html