公司让做全局日志,记录下用户的行为操作,想了下用aop来实现,拦截的是web层,本来使用的配置文件,结果报错了,后来改成注解开发,proxy-target-class="true" proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理
因为有些请求的方法需要过滤的,不去记录,所以使用了自定义注解去实现
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 审计日志方法过滤的自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AnnotationAudit { }
因为aop无法获得response响应的内容,只好加上filter一起来实现这个功能
通过 HttpServletResponseWrapper 获得response响应的内容
package com.*.*.base.audit; import com.*.*.base.action.GainetBaseAction; import com.*.*.base.service.GainetBaseService; import com.opensymphony.xwork2.Action; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; /** * [基于AOP切面的日志自定义类] * @author xuegengjia * @date 10:29 2019/3/22 */ @Aspect public class AuditLogAop extends GainetBaseAction{ Logger logger = Logger.getLogger(AuditLogAop.class); //aop拦截规则 @Pointcut("execution (* cn.包名.*.action..*.*(..))") private void logMethod(){ } public static final ThreadLocalthreadLocal= new ThreadLocal<>(); /** * [前置通知,方法调用前触发] * @author xuegengjia * @date 10:01 2019/3/22 */ @Before("logMethod()") public void doBefore(JoinPoint joinPoint) { // 记录方法开始执行的时间 } /** * [后置通知,方法调用后触发] * @author xuegengjia * @date 10:02 2019/3/22 * @param * @return */ @After("logMethod()") public void doAfter(JoinPoint joinPoint) { // 记录方法执行完成的时间 //获取执行的目标方法 Signature signature = joinPoint.getSignature(); MethodSignature methodSignature =(MethodSignature)signature; Method method = methodSignature.getMethod(); //获得目标方法上的注解 AnnotationAudit AnnotationAudit = method.getAnnotation(AnnotationAudit.class); //判断请求是跳转页面还是返回参数 if (!threadLocal.get().getReqPath().endsWith(".jsp") && !threadLocal.get().getReqMethod().contains(".set") && AnnotationAudit == null && (threadLocal.get().getResult() == null || threadLocal.get().getResult() == Action.NONE)) { if ((AuditLogVo) threadLocal.get() != null) { threadLocal.get().setNeedRecord(AuditLogVo.NEED_RECORD); } } } /** * [环绕通知,围绕着整个方法] * @author xuegengjia * @date 10:03 2019/3/22 * @param * @return */ @Around("logMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { AuditLogVo auditLogVo = new AuditLogVo(); auditLogVo.setReqPath(getRequest().getRequestURI()); auditLogVo.setReqId(随机数); auditLogVo.setReqMethod(pjp.getSignature().toString().substring(6).trim());//方法名 // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行 // result的值就是被拦截方法的返回值 Object result = pjp.proceed(); auditLogVo.setResult(result); threadLocal.set(auditLogVo); return result; } @AfterThrowing(value = "logMethod()",throwing = "ex") public void AfterThrowingMethod(JoinPoint joinpoint,Exception ex){ String methodName = joinpoint.getSignature().getName(); logger.info("方法:"+methodName+"异常,异常原因:"+ex); } }
import org.apache.log4j.Logger; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; /** * [审计日志过滤器] * @author xuegengjia * @date 17:38 2019/4/2 * @param * @return */ public class GainetAuditLogFilter implements Filter { Logger logger =Logger.getLogger(GainetAuditLogFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletOutputStream out = response.getOutputStream(); ThreadLocalthisThreadLocal = AuditLogAop.threadLocal; response.setCharacterEncoding("utf-8"); Date date = new Date(); WrapperedResponse wrapResponse = new WrapperedResponse((HttpServletResponse)response); chain.doFilter(request, wrapResponse); //获取响应内容 byte[] data = wrapResponse.getResponseData(); try { AuditLogVo auditLogVo=thisThreadLocal.get(); if(auditLogVo !=null && auditLogVo.getNeedRecord() ==AuditLogVo.NEED_RECORD){ HttpServletRequest req =(HttpServletRequest)request; AuditLog auditLog =new AuditLog(); /** *开始封装参数持久化数据 */ } } catch (Exception e) { logger.error("审计日志过滤器错误"); logger.error(e.getMessage(),e); }finally { thisThreadLocal.remove(); out.write(data); out.flush(); } } @Override public void destroy() { } }
import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.*; public class WrapperedResponse extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream out = null; private PrintWriter writer = null; public WrapperedResponse(HttpServletResponse resp) throws IOException { super(resp); buffer = new ByteArrayOutputStream();// 真正存储数据的流 out = new WapperedOutputStream(buffer); writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding())); } /**重载父类获取outputstream的方法*/ @Override public ServletOutputStream getOutputStream() throws IOException { return out; } /**重载父类获取writer的方法*/ @Override public PrintWriter getWriter() throws UnsupportedEncodingException { return writer; } /**重载父类获取flushBuffer的方法*/ @Override public void flushBuffer() throws IOException { if (out != null) { out.flush(); } if (writer != null) { writer.flush(); } } @Override public void reset() { buffer.reset(); } /**将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据*/ public byte[] getResponseData() throws IOException { flushBuffer(); return buffer.toByteArray(); } /**内部类,对ServletOutputStream进行包装*/ private class WapperedOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } @Override public void write(int b) throws IOException { bos.write(b); } @Override public void write(byte[] b) throws IOException { bos.write(b, 0, b.length); } @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener writeListener) { } } }