我是靠谱客的博主 友好蜡烛,这篇文章主要介绍Spring AOP实现复杂的日志记录操作(自定义注解),现在分享给大家,希望可以做个参考。

Spring AOP复杂的日志记录(自定义注解)

做项目中,业务逻辑要求只要对数据库数据进行改动的都需要记录日志(增删改),记录的内容有操作者、操作的表名及表名称、具体的操作,以及操作对应的数据。

首先想到的就是Spring 的AOP功能。可是经过一番了解过后,发现一般的日志记录,只能记录一些简单的操作,例如表名、表名称等记录不到。

于是想到了自定义注解的方法,把想要记录的内容放在注解中,通过切入点来获取注解参数,就能获取自己想要的数据,记录数据库中。顺着这个思路,在网上查找了一些相关资料,最终实现功能。话不多说,以下就是实现的思路及代码:

第一步

在代码中添加自定义注解,并且定义两个属性,一个是日志的描述(description),还有个是操作表类型(tableType),属性参数可按需求改变。代码如下:

复制代码
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
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * ClassName: SystemServiceLog <br/> * Function: AOP日志记录,自定义注解 <br/> * date: 2016年6月7日 上午9:29:01 <br/> * @author lcma * @version * @since JDK 1.7 */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SystemServiceLog { /** * 日志描述 */ String description() default ""; /** * 操作表类型 */ int tableType() default 0; }

第二步

定义切面类,获取切面参数,保存数据库具体代码如下:

复制代码
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
import java.lang.reflect.Method; import java.util.Date; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.iflytek.zhbs.common.annotation.SystemServiceLog; import com.iflytek.zhbs.common.util.JacksonUtil; import com.iflytek.zhbs.common.util.WebUtils; import com.iflytek.zhbs.dao.BaseDaoI; import com.iflytek.zhbs.domain.CmsAdmin; import com.iflytek.zhbs.domain.CmsOperationLog; @Aspect @Component @SuppressWarnings("rawtypes") public class SystemLogAspect { @Resource private BaseDaoI<CmsOperationLog> logDao; /** * 日志记录 */ private static final Logger LOGGER = Logger.getLogger(SystemLogAspect.class); /** * Service层切点 */ @Pointcut("@annotation(com.iflytek.zhbs.common.annotation.SystemServiceLog)") public void serviceAspect() { } /** * doServiceLog:获取注解参数,记录日志. <br/> * @author lcma * @param joinPoint 切入点参数 * @since JDK 1.7 */ @After("serviceAspect()") public void doServiceLog(JoinPoint joinPoint) { LOGGER.info("日志记录"); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //获取管理员用户信息 CmsAdmin admin = WebUtils.getAdminInfo(request); try { //数据库日志 CmsOperationLog log = new CmsOperationLog(); log.setOperationType(getServiceMthodTableType(joinPoint)); //获取日志描述信息 String content = getServiceMthodDescription(joinPoint); log.setContent(admin.getRealName() + content); log.setRemarks(getServiceMthodParams(joinPoint)); log.setAdmin(admin); log.setCreateTime(new Date()); logDao.save(log); } catch (Exception e) { LOGGER.error("异常信息:{}", e); } } /** * getServiceMthodDescription:获取注解中对方法的描述信息 用于service层注解 . <br/> * @author lcma * @param joinPoint 切点 * @return 方法描述 * @throws Exception * @since JDK 1.7 */ private String getServiceMthodDescription(JoinPoint joinPoint) throws Exception { 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 description = ""; for(Method method : methods) { if(method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if(clazzs.length == arguments.length) { description = method.getAnnotation(SystemServiceLog.class).description(); break; } } } return description; } /** * getServiceMthodTableType:获取注解中对方法的数据表类型 用于service层注解 . <br/> * @author lcma * @param joinPoint * @return * @throws Exception * @since JDK 1.7 */ private nt getServiceMthodTableType(JoinPoint joinPoint) throws Exception { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); int tableType = 0; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { tableType = method.getAnnotation(SystemServiceLog.class).tableType(); break; } } } return tableType; } /** * getServiceMthodParams:获取json格式的参数. <br/> * @author lcma * @param joinPoint * @return * @throws Exception * @since JDK 1.7 */ private String getServiceMthodParams(JoinPoint joinPoint) throws Exception { Object[] arguments = joinPoint.getArgs(); String params = JacksonUtil.toJSon(arguments); return params; } }

需要注意的是,定义切点的时候,@Pointcut里面是自定义注解的路径

每个切面传递的数据的都不一样,最终决定,获取切面的所有参数,转成json字符串,保存到数据库中。

第三步

在service需要记录日志的地方进行注解,代码如下:

复制代码
1
@SystemServiceLog(description=Constants.ADMIN_SAVE_OPTIONS,tableType=Constants.ADMIM_TABLE_TYPE)

代码图片:

在常量类里面配置自定义注解的参数内容:

第四步

把切面类所在的包路径添加到Spring注解自动扫描路径下,并且启动对@AspectJ注解的支持,代码如下:

复制代码
1
2
3
4
5
<!-- 启动对@AspectJ注解的支持 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 自动扫描包路径 --> <context:component-scan base-package="com.iflytek.zhbs.common.aoplog" /> <context:component-scan base-package="com.iflytek.zhbs.service" />

最后数据库记录数据的效果如图:

OK,功能已经实现,初次写博客,写的不好的地方请谅解。

多个注解可以合并成一个,包括自定义注解

spring中有时候一个类上面标记很多注解。

实际上Java注解可以进行继承(也就是把多个注解合并成1个)

比如说SpringMVC的注解

复制代码
1
2
@RestController @RequestMapping("/person")

可以合并为一个

复制代码
1
@PathRestController("/user")

实现是:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @RestController @RequestMapping public @interface PathRestController { @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持靠谱客。

最后

以上就是友好蜡烛最近收集整理的关于Spring AOP实现复杂的日志记录操作(自定义注解)的全部内容,更多相关Spring内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(107)

评论列表共有 0 条评论

立即
投稿
返回
顶部