SpringAOP + 注解实现日志管理
1. 什么是AOP
?
AOP
(Aspect Orient Programming),翻译过来就是面向切面编程,面向对象编程是将程序抽象成多个层次的对象,面向切面是将程序抽象成多个切面
2. 为什么要使用AOP
(场景说明)
在时间开发中,我们有很多模块中会使用到重复的代码,我们肯定不会通过复制的形式来复用代码.当然解决方式也是多种的,通常我们会将重复的代码抽离出来,提取成一个方法或者是一个抽象的类.然后在需要调用的地方,调用这个方法即可.但是随着我们的业务改变,加入改方法被删除不需要使用,我们需要对所有调用的地方进行单独的删除,这就增加了工作的难度,此时便有了AOP
.AOP
的原理是代理模式
3. 实际案例
使用AOP的方式,完成日志管理
1. 日志管理数据结构
CREATE TABLE `sys_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`operation` varchar(50) DEFAULT NULL COMMENT '操作',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`method_name` varchar(100) DEFAULT NULL COMMENT '方法名',
`param_name` varchar(500) DEFAULT NULL COMMENT '参数',
`ip` varchar(100) DEFAULT NULL COMMENT 'ip地址',
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
复制代码
日志表对应的实体类,mapper
,service
,controller
可以通过mybatisplus
自动生成,这里就省略不写了.
2. 日志管理切入面(代码加强)
/**
* @ClassName LoginLogAspect
* @Description 操作日志切入面
* @Author xiongchao
* @Date 2020/9/27 10:22
**/
@Aspect
@Component
public class SysLogAspect {
@Autowired
private ISysLogService logService;
@Autowired
private ApplicationProperties properties;
@Autowired
private JwtTokenUtil util;
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation( com.adingxiong.cft.aop.Mylog)")
public void logPoinCut() {
}
//切面 配置通知
@AfterReturning("logPoinCut()")
public void savaLog(JoinPoint joinpoin){
SysLog sysLog = new SysLog();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinpoin.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
Mylog myLog = method.getAnnotation(Mylog.class);
if(myLog != null){
String value = myLog.value();
sysLog.setOperation(value);
}
//获取请求的类名
String className = joinpoin.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
sysLog.setMethodName(className + "." + methodName);
HttpServletRequest request=((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
String token = request.getHeader(properties.getTokenHeader());
String authToken = token.substring(properties.getTokenHead().length());
String username = util.getUserNameFromToken(authToken);
sysLog.setUsername(username);
String ip = IpUtils.getRealIp(request);
sysLog.setIp(ip);
//请求参数
Object args [] = joinpoin.getArgs();
String param = JSON.toJSONString(args);
sysLog.setParamName(param);
sysLog.setCreateTime(new Date());
logService.save(sysLog);
}
}
复制代码
3. 获取IP
的工具类
/**
* @ClassName IpUtils
* @Description 获取用户的真实ip
* @Author xiongchao
* @Date 2020/9/27 10:08
**/
public class IpUtils {
public static String getRealIp(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() ==0 || "unknown".equals(ipAddress)){
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
复制代码
4. 自定义注解(通过注解的方式调用切面方法)
/**
* @ClassName Mylog
* @Description TODO
* @Author xiongchao
* @Date 2020/9/27 10:29
**/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface Mylog {
String value() default "";
}
复制代码
5. 测试
在Service
层或者controller
层添加@MyLog(value="xxxxx")
注解完成调用
@Mylog(value = "获取当前用户信息")
@ApiOperation(value = "获取当前登录用户信息")
@GetMapping("/info")
public Result<Map<String,Object>> getUserInfo(Principal principal){
if(principal == null){
return Result.unauthorized(null);
}
String username = principal.getName();
TUser user = userService.getUserByUsername(username);
List<Role> list = roleService.getUserRole(user.getId());
Map<String,Object> res = new HashMap<>();
res.put("userInfo",user);
res.put("role",list);
return Result.success(res);
}
复制代码
本文使用 mdnice 排版