通过切面实现项目内所有的方法出现异常可以在切面中捕获并且记录,为了方便项目上线查找问题原因。
定义切面
关键点:
(1)连接点(在什么类什么函数中连接):因为需要获取到全局的代码异常,所有就不需要通过自定义注解方式定义切入点,要将所有的类,方法都囊括进来
@AfterThrowing(value ="execution(* com.neusoft..*.*(..))",throwing = "ex")
executtion
(2) 切入点(切入时机):@AfterThrowing 表示在发生异常后切入
package com.neusoft.common.aspect;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.neusoft.common.utils.IPUtils;
import com.neusoft.modules.sys.entity.SysUserEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
@Slf4j
public class SysExceptionAspect {
// @Pointcut("@annotation(com.neusoft.common.annotation.SysLog)")
// public void logPointCut() {
//
// }
//异常通知
@AfterThrowing(value ="execution(* com.neusoft..*.*(..))",throwing = "ex")
// @AfterThrowing("logPointCut()")
public void printLogAfterException(JoinPoint joinPoint,Throwable ex){
// ip
String ip = IPUtils.getIpAddr(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
// 异常发生时间
Date date = new Date();
// 操作人
String operateUser = "";
if(null != SecurityUtils.getSubject().getPrincipal()){
operateUser = ((SysUserEntity)SecurityUtils.getSubject().getPrincipal()).getUsername();
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//请求的类
String className = joinPoint.getTarget().getClass().getName();
//请求的方法名
String methodName = signature.getName();
// 请求参数
String params = "";
//请求的参数
Object[] args = joinPoint.getArgs();
try{
params = new Gson().toJson(args);
}catch (Exception e){
}
//异常描述
String exceptionStr = JSONObject.toJSON(ex.getStackTrace()[0]).toString()+","+ex.toString();
String exceptionVal = String.format("ip:%s,请求时间:%s,操作人:%s,请求类:%s,请求方法:%s,请求参数:%s,异常:%s",ip,date,operateUser,className,methodName,params,exceptionStr);
log.error("[AOP异常通知]方法抛出异常:{}",exceptionVal);
}
}
记录用户访问日志,包括ip,用户名,访问接口,参数,执行时间
******拥有该注解的方法设置为连接点*************
@target注解:标明注解的使用范围
TYPE:接口、类、枚举
FIELD:字段、枚举的常量
METHOD:方法
PARAMETER:方法参数
CONSTRUCTOR:构造函数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包
@Retention注解:注解是用来注解的注解,称为元注解,其作用可以简单理解为设置注解的生命周期。
@Retention 注解传入的是 RetentionPolicy 枚举,该枚举有三个常量,分别是 SOURCE、CLASS 和 RUNTIME
三者区别如下:
SOURCE 代表着注解仅保留在源级别中,编译器将Java文件编译成class文件时将之遗弃。
CLASS 代表着注解被保留在class文件中,JVM加载class文件时将之遗弃。
RUNTIME 代表着标记的注解会由JVM保留,因此运行时环境可以使用它。一般来说,我们自己定义的注解都是 RUNTIME 级别的,因为大多数情况我们是根据运行时环境去做一些处理
@Documentde:
如果一个注解@B,被@Documented
标注,那么被@B修饰的类,生成文档时,会显示@B。如果@B没有被@Documented
标注,最终生成的文档中就不会显示@B
package com.neusoft.common.annotation;
import java.lang.annotation.*;
/**
* 系统日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLoginLog {
String value() default "";
}
package com.neusoft.common.aspect;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.neusoft.common.annotation.SysLog;
import com.neusoft.common.utils.HttpContextUtils;
import com.neusoft.common.utils.IPUtils;
import com.neusoft.modules.sys.entity.Hgr2UserLoginLogEntity;
import com.neusoft.modules.sys.entity.SysLogEntity;
import com.neusoft.modules.sys.entity.SysUserEntity;
import com.neusoft.modules.sys.form.SysLoginForm;
import com.neusoft.modules.sys.service.Hgr2UserLoginLogService;
import com.neusoft.modules.sys.service.SysLogService;
import com.neusoft.modules.sys.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 系统日志,切面处理类
*
* @author Mark [email protected]
*/
@Aspect
@Component
@Slf4j
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Autowired
private Hgr2UserLoginLogService hgr2UserLoginLogService;
@Autowired
private SysUserService sysUserService;
@Pointcut("@annotation(com.neusoft.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = new Gson().toJson(args);
sysLog.setParams(params);
}catch (Exception e){
}
//获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//用户名
String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
sysLog.setUsername(username);
sysLog.setTime(time);
sysLog.setCreateDate(new Date());
//保存系统日志
sysLogService.save(sysLog);
}
}
连接点的定义
(1)注解形式
- 定义注解
- 方法中引用注解
- 切入点中引用用注解标识的方法
(2)表达式形式
@AfterThrowing(value ="execution(* com.neusoft..*.*(..))",throwing = "ex")