SpringMVC AOP- 使用切面完成日志记录

xu.wang

发布于 2018.11.01 22:39 阅读 2788 评论 0

    最近项目中需要记录用户的操作,如果每个controller或者service中都写插入语句的话未免不太现实,刚好最近师兄推荐切面编程,故使用切面的方式进行记录操作日志。

第一步:添加需要的jar

 <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

第二步:在application中打开切面的支持

   <!-- 启动对@AspectJ注解的支持  -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

 

第三步: 编写自定义注释,这里我只添加了一个参数 remark,实际可根据逻辑添加多个

/**
 *
 * 操作日志注解
 *
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {

    /**
     * 记录操作描述
     * @return
     */
    String remark() default "";

第四步: 编写切面

@Component
@Aspect
public class ControllerOperationLog {


    @Resource
    private OperationLogService operationLogService;

    private static final Logger log = LoggerFactory.getLogger(ControllerOperationLog.class);

    /**
     * 切点
     */
    @Pointcut("execution(* com.*.controller..*(..))")
    public void recordLog()  {
    }


    /**
     * 所拦截方法执行之前执行
     */
    @Before("recordLog()")
    public void before(){
    }



    /**
     * 同时在所拦截方法的前后执行,学习Around的使用
     *	在ProceedingJoinPoint.proceed()前后加逻辑
     */
    @Around("execution(public * com.jtexplorer.controller..*.*(..))")
//    @Around("recordLog()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        joinPoint.proceed();
        long costTime = System.currentTimeMillis() - startTime;

        String methodName = joinPoint.getSignature().getName();
        //记录执行请求耗时
        log.info(methodName + " finished ! Cost : " + costTime + " ms.");
    }

    /**
     * 所拦截方法执行之后执行
     */
    @After("recordLog()")
    public void after(JoinPoint point){

        HttpServletRequest request = this.getRequest(point);

        try {
            MethodLog methodLog = getMethodRemark(point);
            if(methodLog != null) {
                insertLog(request, methodLog.remark(), getParam(request));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 获取参数request
     *
     * @param point
     * @return
     */
    private HttpServletRequest getRequest(JoinPoint point) {
        Object[] args = point.getArgs();
        for (Object obj : args) {
            if (obj instanceof HttpServletRequest) {
                return (HttpServletRequest) obj;
            }
        }
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }


    /**
     * 将日志插入数据库中
     **/
    public void insertLog(HttpServletRequest request,String operation,String data){

        operationLogService.insertLog(request.getSession(), request, operation+"; data : "+data);

    }

    /**
     * 获取方法的中文备注,用于记录用户的操作日志
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private MethodLog getMethodRemark(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();

        Class targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    MethodLog methodCache = m.getAnnotation(MethodLog.class);
                    if (methodCache != null && !("").equals(methodCache.remark())) {
                        return methodCache;
                    }
                    break;
                }
            }
        }
        return null;
    }


    /**
    * controller中捕获到异常时执行
    **/
    @AfterThrowing(pointcut = "recordLog()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {

    }



    /**
     * 获取IP
     * @param request
     * @return
     */
    private String getRequestIP(HttpServletRequest request) {
        String ip = null;
        if (request.getHeader("x-forwarded-for") == null) {
            ip = request.getRemoteAddr();
        }else{
            ip = request.getHeader("x-forwarded-for");
        }
        return ip;
    }

    /**
     * 将controller的请求参数转化成jsonString
     * @param request
     * @return
     */
    private String getParam(HttpServletRequest request) {
        Map properties = request.getParameterMap();
        Map returnMap = new HashMap();
        Iterator entries = properties.entrySet().iterator();
        Map.Entry entry;
        String name = "";
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            value = null;
            if (null == valueObj) {
                value = "";
            } else if (valueObj instanceof String[]) {
                String[] values = (String[]) valueObj;
                for (int i = 0; i < values.length; i++) {
                    if (value == null) {
                        value = (values[i] == null ? "" : values[i]);
                    }else {
                        value += "," + (values[i] == null ? "" : values[i]);
                    }
                }
            } else {
                value = valueObj.toString();
            }
            returnMap.put(name, value);
        }
        JSONObject json = new JSONObject(returnMap);
        return json.toJSONString();
    }


}

第五步: 在controller中使用

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    @MethodLog(remark = "管理端用户登录")  //增加此处的注解,remark可填写操作类型
    public JsonResult adminLogin(@RequestParam(required = false, defaultValue = "") String username,
                                 @RequestParam(required = false, defaultValue = "") String password,
                                 HttpSession session) {


        return adminUserService.adminLogin(username,password,session);
    }

 

 

除切面实现日志记录外,切面中的@After @Before @Around @AfterThowing 和 注解中表达式的使用会进一步补充。