原创
spring boot 开发—第六篇AOP记录系统关键操作日志
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1、AOP简介
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
- 1
- 2
- 3
- 4
- 5
2、AOP核心概念
(1)、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
- 1
(2)、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
- 1
(3)、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
- 1
(4)、切入点(pointcut)
对连接点进行拦截的定义
- 1
(5)、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类:
Before(前) org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
Introduction(引入) org.springframework.aop.IntroductionInterceptor
Arround(环绕) org.aopaliance.intercept.MethodInterceptor
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
(6)、目标对象
代理的目标对象
- 1
(7)、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
- 1
(8)、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
- 1
3、具体实例
3.1、定义注解,拦截controller
package com.vesus.springbootlog.annotation;
import java.lang.annotation.*;
/**
* @Description: 定义注解,拦截controller
* @Author: vesus
* @CreateDate: 2018/5/20 上午10:54
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)//元注解,定义注解被保留策略,一般有三种策略
//1、RetentionPolicy.SOURCE 注解只保留在源文件中,在编译成class文件的时候被遗弃
//2、RetentionPolicy.CLASS 注解被保留在class中,但是在jvm加载的时候北欧抛弃,这个是默认的声明周期
//3、RetentionPolicy.RUNTIME 注解在jvm加载的时候仍被保留
@Target({ElementType.METHOD}) //定义了注解声明在哪些元素之前
@Documented
public @interface SystemControllerLog {
//定义成员
String descrption() default "" ;//描述
String actionType() default "" ;//操作的类型,1、添加 2、修改 3、删除 4、查询
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3.2、定义注解,拦截service
package com.vesus.springbootlog.annotation;
import java.lang.annotation.*;
/**
* @Description: 定义注解,拦截service
* @Author: vesus
* @CreateDate: 2018/5/20 上午10:54
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SystemServiceLog {
//定义成员
String decription() default "" ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3.3、定义日志切入类
package com.vesus.springbootlog.aspect;
import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.annotation.SystemServiceLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.SystemLog;
import com.vesus.springbootlog.service.SystemLogService;
import com.vesus.springbootlog.util.ReturnCode;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description: 定义日志切入类
* @Author: vesus
* @CreateDate: 2018/5/20 上午11:05
* @Version: 1.0
*/
@Aspect
@Component
@Order(-5)
public class SystemLogAspect {
@Autowired
private SystemLogService systemLogService;
/***
* 定义service切入点拦截规则,拦截SystemServiceLog注解的方法
*/
@Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemServiceLog)")
public void serviceAspect(){}
/***
* 定义controller切入点拦截规则,拦截SystemControllerLog注解的方法
*/
@Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemControllerLog)")
public void controllerAspect(){}
/***
* 拦截控制层的操作日志
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("controllerAspect()")
public ExecutionResult recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
SystemLog systemLog = new SystemLog();
Object proceed = null ;
//获取session中的用户
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
request.getSession().getAttribute("user");
systemLog.setUserid("vesus");
//获取请求的ip
String ip = request.getRemoteAddr();
systemLog.setRequestip(ip);
//获取执行的方法名
systemLog.setActionmethod(joinPoint.getSignature().getName());
//获取方法执行前时间
Date date=new Date();
systemLog.setActiondate(date);
proceed = joinPoint.proceed();
//提取controller中ExecutionResult的属性
ExecutionResult result = (ExecutionResult) proceed;
if (result.getResultCode().equals(ReturnCode.RES_SUCCESS)){
//设置操作信息
systemLog.setType("1");
//获取执行方法的注解内容
systemLog.setDescription(getControllerMethodDescription(joinPoint)+":"+result.getMsg());
}else{
systemLog.setType("2");
systemLog.setExceptioncode(result.getMsg());
}
Object[] params = joinPoint.getArgs() ;
String returnStr = "" ;
for (Object param : params) {
if (param instanceof String){
returnStr+= param ;
}else if (param instanceof Integer){
returnStr+= param ;
}
}
systemLog.setParams(returnStr);
systemLogService.saveUser(systemLog);
return result ;
}
//异常处理
@AfterThrowing(pointcut = "controllerAspect()",throwing="e")
public void doAfterThrowing(JoinPoint joinPoint,Throwable e) throws Throwable{
SystemLog systemLog = new SystemLog();
Object proceed = null ;
//获取session中的用户
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
request.getSession().getAttribute("user");
systemLog.setUserid("vesus");
//获取请求的ip
String ip = request.getRemoteAddr();
systemLog.setRequestip(ip);
systemLog.setType("2");
systemLog.setExceptioncode(e.getClass().getName());
systemLog.setExceptiondetail(e.getMessage());
systemLogService.saveUser(systemLog);
}
/***
* 获取service的操作信息
* @param joinpoint
* @return
* @throws Exception
*/
public String getServiceMethodMsg(JoinPoint joinpoint) throws Exception{
//获取连接点目标类名
String className =joinpoint.getTarget().getClass().getName() ;
//获取连接点签名的方法名
String methodName = joinpoint.getSignature().getName() ;
//获取连接点参数
Object[] args = joinpoint.getArgs() ;
//根据连接点类的名字获取指定类
Class targetClass = Class.forName(className);
//拿到类里面的方法
Method[] methods = targetClass.getMethods() ;
String description = "" ;
//遍历方法名,找到被调用的方法名
for (Method method : methods) {
if (method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes() ;
if (clazzs.length==args.length){
//获取注解的说明
description = method.getAnnotation(SystemServiceLog. class).decription();
break;
}
}
}
return description ;
}
/***
* 获取controller的操作信息
* @param point
* @return
*/
public String getControllerMethodDescription(ProceedingJoinPoint point) throws Exception{
//获取连接点目标类名
String targetName = point.getTarget().getClass().getName() ;
//获取连接点签名的方法名
String methodName = point.getSignature().getName() ;
//获取连接点参数
Object[] args = point.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 == args.length){
description = method.getAnnotation(SystemControllerLog.class).descrption();
break;
}
}
}
return description ;
}
}
- 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
3.4、定义控制器controller
package com.vesus.springbootlog.controller;
import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.User;
import com.vesus.springbootlog.service.UserService;
import com.vesus.springbootlog.util.ReturnCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserService userService ;
@RequestMapping(value = "/userlist")
@SystemControllerLog(descrption = "查询用户信息",actionType = "4")
public ExecutionResult getUserList(String id) throws Exception{
ExecutionResult result = new ExecutionResult();
try {
List<User> users = userService.findAll() ;
result.setTotal(users.size());
result.setResultCode(ReturnCode.RES_SUCCESS);
result.setFlag(true);
result.setData(users);
result.setMsg("查询成功!");
//异常处理
int aa= 5/0;
}catch (Exception e){
result.setFlag(true);
result.setData(null);
result.setResultCode(ReturnCode.RES_FAILED);
result.setMsg("查询失败!");
throw e ;
}
return result ;
}
}
- 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
3.5、启动
访问http://localhost:8080//userlist?id=123456,可以看到数据库增加了日志文件。
1 0:0:0:0:0:0:0:1 1 vesus 查询用户信息:查询成功! 2018-05-21 getUserList
2 0:0:0:0:0:0:0:1 1 vesus 查询用户信息:查询成功! 2018-05-21 getUserList
3 0:0:0:0:0:0:0:1 1 vesus 查询用户信息:查询成功! 2018-05-21 getUserList
4 0:0:0:0:0:0:0:1 1 vesus 查询用户信息:查询成功! 2018-05-21 getUserList
5 0:0:0:0:0:0:0:1 1 vesus 查询用户信息:查询成功! 2018-05-21 getUserList