SpringMVC AOP 注解模式 全局管理访问日志
注:此处不详细讲解aop的使用方式,只记录一个使用过的例子并配上一点说明
我们需要开启自动注入,让spring自动实现织入
<aop:aspectj-autoproxy />
@annotation注解
对于切面来说,我们一般会定义一个切点Pointcut,这里不讲这个。想说的是,我们定义Advice的时候,
Advice匹配的维度除了可以直接写切点Pointcut的名称之外:
@Pointcut("execution(* com.baiding.*.controller.*.*(..))")
public void pointcutName(){}
@Before("pointcutName()")
public void before(){
System.out.println("BaiDing Spring AOP");
}
还可以在Advice上使用@annotation来指定匹配哪些方法:
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void logAspect(ProceedingJoinPoint point) {
System.out.println("BaiDing Spring AOP");
}
@annotation注解会检查系统中所有对象的所有方法级别连接点Joinpoint,如果被检测的方法标注有@annotation所指定的注解类型,那么当前方法将被Pointcut表达式匹配
上面的代码块表示所有的@RequestMapping注解所标识的方法将得到@Around增强
实际例子
@Component @Aspect public class ControllerAspect { private Logger logger= LoggerFactory.getLogger(ControllerAspect.class); private static final ObjectMapper mapper = new ObjectMapper(). disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); public ControllerAspect(){} @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public Object logAspect(ProceedingJoinPoint point) { long start = System.currentTimeMillis(); Object[] args = point.getArgs(); Object result = null; ControllerAspect.LogContent content ; HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String uri = request.getRequestURI(); String ip = request.getRemoteAddr(); try { result = point.proceed(); content = new ControllerAspect.LogContent(System.currentTimeMillis() - start, args, result , uri, ip); logger.info(mapper.writeValueAsString(content)); } catch (Throwable var17) { content = new ControllerAspect.LogContentWithException(System.currentTimeMillis() - start, args, result, uri, ip, var17); logger.warn(content.toString(), var17); } return result; } private class LogContentWithException extends ControllerAspect.LogContent { private String throwable; public LogContentWithException(long consumed, Object[] params, Object result, String url, String ip, Throwable throwable) { super(consumed, params, result, url, ip); this.throwable = throwable.toString(); } } private class LogContent implements Serializable { private long consumed; private Object params; private Object result; private String url; private String ip; LogContent(long consumed, Object[] params, Object result, String url, String ip) { this.consumed = consumed; this.params = this.ignoreServletObjectInArgs(params); this.result = result; this.url = url; this.ip = ip; } public long getConsumed() { return consumed; } public Object getParams() { return params; } public Object getResult() { return result; } public String getUrl() { return url; } public String getIp() { return ip; } private Object ignoreServletObjectInArgs(Object[] params) { if (params == null || params.length == 0) { return null; } else { List args = Lists.newArrayList(); int i = 0; for(int l = params.length; i < l; ++i) { Object arg = params[i]; if (!(arg instanceof ServletRequest) && !(arg instanceof ServletResponse)) { args.add(arg); } } return args.size() == 1 ? args.get(0) : args; } } } }
上述是一个完整的切面类 ,可以直接用的。ControllerAspect中定义了一个环绕通知@Around,在@RequestMapping注解标识方法的执行前后执行通知,类中有一个LogContent实体,其中有执行方法耗时consumed,参数params,返回结果result,访问url以及访问ip.具体的结果如下:
2018/06/12,17:19:57.207|{"consumed":1337,"params":["jing.jing","12341234"],"result":"/welocomeUsering.jsp","url":"/login/login.do","ip":"127.0.0.1"}
这样一个完整的访问日志就记录下来了…