SpringBoot利用AOP拦截自定义注解(以日志为例)
这波操作有两个动作,一个是自定义注解,另一个是AOP拦截注解,下面按步进行:
- 自定义注解:
package com.pilot.basic.system.annotation;
/**
* @name: OperateLog
* @author: lyr.
* @date: 2019/9/9.
* @version: 1.0
* @Description:自定义日志注解
*/
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {
/**
* 要执行的操作类型
**/
String methodDesc() default "";
/**
* 要执行的操作名称
**/
String moduleName() default "";
}
说明:之所以要定义上面两个方法,是因为日志需要记录请求的方法的详细描述,以供用户知道这个方法是干嘛的,比如:
然而,这两个属性又不能通过“JoinPoint”来获取到,所以这里要作为注解的参数来获取。具体是这样获取到的,在使用注解时,写上这两个属性的值即可,如下:
- AOP拦截注解:
package com.pilot.basic.system.annotation;
/**
* @name: OperateLogAspect
* @author: lyr.
* @date: 2019/9/9.
* @version: 1.0
* @Description:
*/
@Aspect
@Component
public class OperateLogAspect {
//注入Service用于把日志保存数据库
@Autowired
FeignLog feignLog;
private static final Logger logger = LoggerFactory.getLogger(OperateLogAspect.class);
private static ThreadLocal<String> key = new ThreadLocal();
//Controller层切点
@Pointcut("@annotation(com.pilot.basic.system.annotation.OperateLog)")
public void controllerAspect() {
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@Before("controllerAspect()&&@annotation(operateLog)")
public void doBefore(JoinPoint joinPoint,OperateLog operateLog) {
System.out.println("==========执行controller前置通知===============");
if (logger.isInfoEnabled()) {
logger.info("before " + joinPoint);
}
}
/**
* 后置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "controllerAspect()",returning = "obj")
public void after(JoinPoint joinPoint, Object obj) {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
//请求的IP
String ip = request.getRemoteAddr();
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 methodDesc = "";
String moduleName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
methodDesc = method.getAnnotation(OperateLog.class).methodDesc();
moduleName = method.getAnnotation(OperateLog.class).moduleName();
break;
}
}
}
//获取请求参数
String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
}
}
//获取返回结果
key.remove();
ResponseResult result = null;
int statusCode = 0;
if (obj != null) {
if (obj instanceof ResponseResult) {
result = (ResponseResult)obj;
statusCode = result.getStatus();
} else if(obj instanceof ResponsePageResult){
ResponsePageResult result1 = (ResponsePageResult)obj;
}
}
//*========控制台输出=========*//
System.out.println("=====controller后置通知开始=====");
System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + methodDesc);
System.out.println("方法描述:" + moduleName);
System.out.println("请求IP:" + ip);
//*========数据库日志=========*//
OperateLogParam operateLogParam = new OperateLogParam();
Param.setActionName(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
operateLogParam.setMethodDesc(methodDesc);
operateLogParam.setModuleName(moduleName);
operateLogParam.setOperatorTime(LocalDateTime.now());
operateLogParam.setRequestIp(ip);
operateLogParam.setRequestParam(params);
operateLogParam.setRequestUrl(request.getRequestURL().toString() + " " + request.getMethod());
operateLogParam.setResponseCode(String.valueOf(statusCode));
feignLog.insert(operateLogParam);
System.out.println("=====controller后置通知结束=====");
} catch (Exception e) {
//记录本地异常日志
logger.error("==后置通知异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
}
说明:需要注意的是这里的写法(如图),决定了你拦截的是什么,这里用的是@annotation注解,还有@target等,可以探究下这些注解的区别,这里先不多做说明。
如上两步就完成了SpringBoot利用AOP拦截自定义日志注解。不过在写拦截器时调用了微服务,所以下一篇以此为例来简单说下基于springcloud的微服务调用问题。