SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)
首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。这个时候就马上打开CRT或者SSH连上服务器拿日志来分析。受网络的各种限制。于是我们就想为什么不能直接在管理后台查看报错的信息呢。于是日志管理就出现了。
其次个人觉得做日志管理最好的是Aop,有的人也喜欢用拦截器。都可以,在此我重点介绍我的实现方式。
Aop有的人说拦截不到Controller。有的人说想拦截到Controller必须得拦截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。
首先Aop可以拦截到Controller的,这个是毋容置疑的其次须拦截AnnotationMethodHandlerAdapter也不是必须的。最起码我没有验证成功过这个。我的Spring版本是4.0.3。
Aop之所以有的人说拦截不到Controller是因为Controller被jdk代理了。我们只要把它交给cglib代理就可以了。
第一步定义两个注解:
1. package com.annotation;
2.
3. import java.lang.annotation.*;
4.
5. /**
6. *自定义注解 拦截Controller
7. */
8.
9. @Target({ElementType.PARAMETER, ElementType.METHOD})
10. @Retention(RetentionPolicy.RUNTIME)
11. @Documented
12. public @interface SystemControllerLog {
13.
14. String description() default "";
15.
16.
17. }
18.
19. package com.annotation;
20.
21. import java.lang.annotation.*;
22.
23. /**
24. *自定义注解 拦截service
25. */
26.
27. @Target({ElementType.PARAMETER, ElementType.METHOD})
28. @Retention(RetentionPolicy.RUNTIME)
29. @Documented
30. public @interface SystemServiceLog {
31.
32. String description() default "";
33.
34.
35. }
第二步创建一个切点类:
[java] view plain copy
1. package com.annotation;
2.
3. import com.model.Log;
4. import com.model.User;
5. import com.service.LogService;
6. import com.util.DateUtil;
7. import com.util.JSONUtil;
8. import com.util.SpringContextHolder;
9. import com.util.WebConstants;
10. import org.aspectj.lang.JoinPoint;
11. import org.aspectj.lang.annotation.*;
12. import org.slf4j.Logger;
13. import org.slf4j.LoggerFactory;
14. import org.springframework.stereotype.Component;
15. import org.springframework.web.context.request.RequestContextHolder;
16. import org.springframework.web.context.request.ServletRequestAttributes;
17. import javax.annotation.Resource;
18. import javax.servlet.http.HttpServletRequest;
19. import javax.servlet.http.HttpSession;
20. import java.lang.reflect.Method;
21.
22. /**
23. * 切点类
24. * @author tiangai
25. * @since 2014-08-05 Pm 20:35
26. * @version 1.0
27. */
28. @Aspect
29. @Component
30. public class SystemLogAspect {
31. //注入Service用于把日志保存数据库
32. @Resource
33. private LogService logService;
34. //本地异常日志记录对象
35. private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);
36.
37. //Service层切点
38. @Pointcut("@annotation(com.annotation.SystemServiceLog)")
39. public void serviceAspect() {
40. }
41.
42. //Controller层切点
43. @Pointcut("@annotation(com.annotation.SystemControllerLog)")
44. public void controllerAspect() {
45. }
46.
47. /**
48. * 前置通知 用于拦截Controller层记录用户的操作
49. *
50. * @param joinPoint 切点
51. */
52. @Before("controllerAspect()")
53. public void doBefore(JoinPoint joinPoint) {
54.
55. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
56. HttpSession session = request.getSession();
57. //读取session中的用户
58. User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
59. //请求的IP
60. String ip = request.getRemoteAddr();
61. try {
62. //*========控制台输出=========*//
63. System.out.println("=====前置通知开始=====");
64. System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
65. System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));
66. System.out.println("请求人:" + user.getName());
67. System.out.println("请求IP:" + ip);
68. //*========数据库日志=========*//
69. Log log = SpringContextHolder.getBean("logxx");
70. log.setDescription(getControllerMethodDescription(joinPoint));
71. log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
72. log.setType("0");
73. log.setRequestIp(ip);
74. log.setExceptionCode( null);
75. log.setExceptionDetail( null);
76. log.setParams( null);
77. log.setCreateBy(user);
78. log.setCreateDate(DateUtil.getCurrentDate());
79. //保存数据库
80. logService.add(log);
81. System.out.println("=====前置通知结束=====");
82. } catch (Exception e) {
83. //记录本地异常日志
84. logger.error("==前置通知异常==");
85. logger.error("异常信息:{}", e.getMessage());
86. }
87. }
88.
89. /**
90. * 异常通知 用于拦截service层记录异常日志
91. *
92. * @param joinPoint
93. * @param e
94. */
95. @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
96. public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
97. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
98. HttpSession session = request.getSession();
99. //读取session中的用户
100. User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
101. //获取请求ip
102. String ip = request.getRemoteAddr();
103. //获取用户请求方法的参数并序列化为JSON格式字符串
104. String params = "";
105. if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
106. for ( int i = 0; i < joinPoint.getArgs().length; i++) {
107. params += JSONUtil.toJsonString(joinPoint.getArgs()[i]) + ";";
108. }
109. }
110. try {
111. /*========控制台输出=========*/
112. System.out.println("=====异常通知开始=====");
113. System.out.println("异常代码:" + e.getClass().getName());
114. System.out.println("异常信息:" + e.getMessage());
115. System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
116. System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));
117. System.out.println("请求人:" + user.getName());
118. System.out.println("请求IP:" + ip);
119. System.out.println("请求参数:" + params);
120. /*==========数据库日志=========*/
121. Log log = SpringContextHolder.getBean("logxx");
122. log.setDescription(getServiceMthodDescription(joinPoint));
123. log.setExceptionCode(e.getClass().getName());
124. log.setType("1");
125. log.setExceptionDetail(e.getMessage());
126. log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
127. log.setParams(params);
128. log.setCreateBy(user);
129. log.setCreateDate(DateUtil.getCurrentDate());
130. log.setRequestIp(ip);
131. //保存数据库
132. logService.add(log);
133. System.out.println("=====异常通知结束=====");
134. } catch (Exception ex) {
135. //记录本地异常日志
136. logger.error("==异常通知异常==");
137. logger.error("异常信息:{}", ex.getMessage());
138. }
139. /*==========记录本地异常日志==========*/
140. logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);
141.
142. }
143.
144.
145. /**
146. * 获取注解中对方法的描述信息 用于service层注解
147. *
148. * @param joinPoint 切点
149. * @return 方法描述
150. * @throws Exception
151. */
152. public static String getServiceMthodDescription(JoinPoint joinPoint)
153. throws Exception {
154. String targetName = joinPoint.getTarget().getClass().getName();
155. String methodName = joinPoint.getSignature().getName();
156. Object[] arguments = joinPoint.getArgs();
157. Class targetClass = Class.forName(targetName);
158. Method[] methods = targetClass.getMethods();
159. String description = "";
160. for (Method method : methods) {
161. if (method.getName().equals(methodName)) {
162. Class[] clazzs = method.getParameterTypes();
163. if (clazzs.length == arguments.length) {
164. description = method.getAnnotation(SystemServiceLog. class).description();
165. break;
166. }
167. }
168. }
169. return description;
170. }
171.
172. /**
173. * 获取注解中对方法的描述信息 用于Controller层注解
174. *
175. * @param joinPoint 切点
176. * @return 方法描述
177. * @throws Exception
178. */
179. public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
180. String targetName = joinPoint.getTarget().getClass().getName();
181. String methodName = joinPoint.getSignature().getName();
182. Object[] arguments = joinPoint.getArgs();
183. Class targetClass = Class.forName(targetName);
184. Method[] methods = targetClass.getMethods();
185. String description = "";
186. for (Method method : methods) {
187. if (method.getName().equals(methodName)) {
188. Class[] clazzs = method.getParameterTypes();
189. if (clazzs.length == arguments.length) {
190. description = method.getAnnotation(SystemControllerLog. class).description();
191. break;
192. }
193. }
194. }
195. return description;
196. }
197. }
第三步把Controller的代理权交给cglib
在实例化ApplicationContext的时候需要加上
Xml代码
1. <!-- 启动对@AspectJ注解的支持 -->
2. <aop:aspectj-autoproxy/>
在调用Controller的时候AOP发挥作用所以在SpringMVC的配置文件里加上
Xml代码
1. <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller->
2. <aop:aspectj-autoproxy proxy-target-class="true" />
第四步使用
Controller层的使用
Java代码
[java] view plain copy
1. /**
2. * 删除用户
3. *
4. * @param criteria 条件
5. * @param id id
6. * @param model 模型
7. * @return 数据列表
8. */
9. @RequestMapping(value = "/delete")
10. //此处为记录AOP拦截Controller记录用户操作
11. @SystemControllerLog(description = "删除用户")
12. public String del(Criteria criteria, String id, Model model, HttpSession session) {
13. try {
14. User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
15. if ( null != user) {
16. if (user.getId().equals(id)) {
17. msg = "您不可以删除自己!";
18. criteria = userService.selectByCriteriaPagination(criteria);
19. } else {
20. //删除数据并查询出数据
21. criteria = userService.delete(id, criteria);
22. msg = "删除成功!";
23. }
24. }
25. } catch (Exception e) {
26. msg = "删除失败!";
27. } finally {
28. model.addAttribute("msg", msg);
29. model.addAttribute("criteria", criteria);
30. }
31. //跳转列表页
32. return "user/list";
33. }
Service层的使用
[java] view plain copy
1. /**
2. * 按照分页查询
3. * @param criteria
4. * @return
5. */
6. //此处为AOP拦截Service记录异常信息。方法不需要加try-catch
7. @SystemServiceLog(description = "查询用户")
8. public Criteria<User> selectByCriteriaPagination(Criteria<User> criteria)
9. {
10. criteria.getList().get(0).getAccount();
11. //查询总数
12. long total=userMapper.countByCriteria(criteria);
13. //设置总数
14. criteria.setRowCount(total);
15. criteria.setList(userMapper.selectByCriteriaPagination(criteria));
16. return criteria;
17. }
18. 效果图
19.
20. 用户操作:
21.
异常
22.