SpringBoot的AOP使用

说明:最近需要做一个异常通知的功能,在service层抛出Exception异常时,发一个通知的操作,于是便整理了下,以备后用

注解说明

@Before: 前置通知, 发生在方法执行之前

@After: 后置通知, 发生在方法执行之后

@AfterRunning: 返回通知, 发生在方法返回结果之后

@AfterThrowing: 异常通知, 发生在方法抛出异常之后

@Around: 环绕通知, 发生在方法执行前和后

切面定义

新建类ServiceAspect,直接贴代码

@Configuration
@Aspect
@Slf4j
public class ServiceAspect {
@Autowired
private ExceptionNoticeAPClient exceptionNoticeAPClient;

private final String ExpGetResultDataPonit = "execution(* com.healthmanagement.*.service..*.*(..))";

@Pointcut(ExpGetResultDataPonit)
public void excuteService() {
}

//配置抛出异常后通知,使用在方法aspect()上注册的切入点
@AfterThrowing(throwing="ex",pointcut="excuteService()")
public ReturnMsg<Object> handle(JoinPoint joinPoint, Exception ex) throws IOException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.
getRequestAttributes()).getRequest();
//获取请求的URL
StringBuffer requestURL = request.getRequestURL();
String exceptionMsg = "【"+ DateUtil.formatDate(new Date(),"yyyy-MM-dd HH:mm:ss")+"】访问:"+requestURL+"时发生异常\n方法名:"+joinPoint+"\n具体异常信息:\n类型:【"+ex.getClass().getName()+"】\n摘要:【"+ex.getMessage()+"】\n详细:【"+ex.toString()+"】\n堆栈:【"+getErrorInfoFromException(ex)+"】";;
if("com.healthmanagement.core.exception.globalexception".equals(ex.getClass().getName().trim().toLowerCase())){
GlobalException exx = (GlobalException) ex;
if(null!=exx.getE()){
exceptionMsg = "【"+DateUtil.formatDate(new Date(),"yyyy-MM-dd HH:mm:ss")+"】访问:"+requestURL+"时发生异常\n方法名:"+joinPoint+"\n具体异常信息:\n类型:【"+exx.getE().getClass().getName()+"】\n摘要:【"+exx.getE().getMessage()+"】\n详细:【"+exx.getE().toString()+"】\n堆栈:【"+getErrorInfoFromException(exx.getE())+"】";
}
}
WarningParam warningParam = new WarningParam();
warningParam.setContent(exceptionMsg);
warningParam.setTouser("user");
exceptionNoticeAPClient.sendWarning(warningParam);
log.error("{}请求异常:【{}】", request.getRequestURL().toString(), ex.toString());
return new ReturnMsg<>(1, ex.getMessage());
}

private static String getErrorInfoFromException(Exception e) {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
} catch (Exception e2) {
return "bad getErrorInfoFromException";
}
}

}

补充部分

//执行方法前的拦截方法
@Before("excuteService()")
public void doBeforeMethod(JoinPoint joinPoint) {
System.out.println("我是前置通知,将要执行一个方法");

}

/**
* 后置返回通知
* 这里需要注意的是:
* 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
* 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
* returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
*/
@AfterReturning(value = ExpGetResultDataPonit, returning = "keys")
public void doAfterReturningAdvice1(JoinPoint joinPoint, Object keys) {

}


/**
* 后置最终通知(目标方法只要执行完了就会执行后置通知方法)
*/
@After("excuteService()")
public void doAfterAdvice(JoinPoint joinPoint) {
System.out.println("后置通知执行了!!!!");
}




/**
* 环绕通知:
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
*/
@Around(ExpGetResultDataPonit)
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
try {//obj之前可以写目标方法执行前的逻辑

} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}

-------------本文结束感谢您的阅读-------------
0%