Spring-AOP应用
上一篇《Spring-AOP理解》讲到aop基于代理对原有方法进行增强,Spring很多注解的都是使用了AOP的动态代理去实现。
例如:
- 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )
- 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
- 权限控制(security框架hasAuthority(),hasRole()等)
有时的业务场景需要我们自定义去实现一个切面进行处理。比如记录指定接口的请求响应数据,其实拦截器也可以做到,但是拦截器针对某个接口拦截使用配置相对较麻烦。下面将使用Spring的AOP进行日志记录,当要记录接口的时候添加在接口方法上即可,使用起来也比较简单。
一、定义一个自定义注解
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
String name();
}
二、 切面类进行拦截注解逻辑处理
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.util.concurrent.Executor;
@Aspect
@Component
public class OperationLogAspect {
@Pointcut(value = "@annotation(com.xy.annotation.OperationLog)")
public void aspect() {
}
@Around("aspect()&&@annotation(operationLog)")
public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
OperationLogSaveReq log = new OperationLogSaveReq();
log.setName(operationLog.name());
log.setIp(IpUtils.getLocalIp(request));
log.setParameter(JSONObject.toJSONString(joinPoint.getArgs()));
log.setUrl(String.valueOf(request.getRequestURL()));
Long startTime = System.currentTimeMillis();
Object obj=null;
try {
obj= joinPoint.proceed();
}catch (Throwable throwable){
Long endTime = System.currentTimeMillis();
log.setSpendTime(endTime.intValue()-startTime.intValue());
log.setResult(JSONObject.toJSONString(throwable.getMessage()));
// log.setIsSuccess(false);
saveOperationLog(log);
throw throwable;
}
Long endTime = System.currentTimeMillis();
log.setSpendTime(endTime.intValue()-startTime.intValue());
log.setResult(JSONObject.toJSONString(obj));
if ( obj instanceof ResponseResult){
log.setIsSuccess(((ResponseResult<?>) obj).isSuccess());
}else {
log.setIsSuccess(true);
}
saveOperationLog(log);
return obj;
}
}
三、需要记录操作日志的地方 添加 @OperationLog 注解。
有必要给这个接口定义一个名称,便于排查日志检索。
@PostMapping("delete")
@OperationLog(name = "删除数据")
public ResponseResult<Void> delete(@RequestBody @Validated BaseIdReq req){
delete(req.getId());
return ResponseResult.success();
}
Leave a Reply