1. 确保框架中引入了aspectj 相关jar包
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring-version}</version>
</dependency>
  1. 确保编写的@Aspect能被spring扫码到
<context:component-scan base-package="xxxxxx">
</context:component-scan>
  1. 编写切面类
@Aspect
@Component
public class LogAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void boBefore(JoinPoint joinPoint, Log controllerLog) {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {

            HttpServletRequest request = ServletUtils.getRequest();
            String requestUrl = StringUtils.substring(request.getRequestURI(), 0, 255);
            String ip = CommonUtils.getIp(request);//增加操作人的IP地址
            String params = argsArrayToString(joinPoint.getArgs(), null);
            long time = System.currentTimeMillis() - TIME_THREADLOCAL.get();

            logger.info("params={} time={} ip={} requestUrl={}", params, time, ip, requestUrl);
            logger.info("jsonResult={}", JSON.toJSONString(jsonResult));
        } catch (Exception exp) {
            // 记录本地异常日志
            logger.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        } finally {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (o != null && !isFilterObject(o))
                {
                    try
                    {
                        String jsonObj = JSON.toJSONString(o);
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }

    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Object value : collection)
            {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }

}
  1. 切面注解类
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public FunctionTypeEnum businessType() default FunctionTypeEnum.QUERY;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;

    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}
  1. 在对应的Controller上增加注解
@Log(title = "用户信息查询", businessType = BusinessType.Query)
@RequestMapping(value="getUsers", method=RequestMethod.POST)
public @ResponseBody PageData queryUsersPage(PageForm form, User user) throws Exception{
        PageData users = this.userService.selectUsersForPage(form, user);
        return users;
}

总结

切面是Spring中一种比较常用的操作,面向切面编程,最大的好处的对业务代码无入侵。当然程序中增加一个@Log 注解是方便要给指定的请求增加日志,不是所有请求。可以指定一些业务参数,实际情况根据自身业务情况来看。不加@Log也是可以的,那就切面到所有Controller,编写好你想要切面的目的表达式即可。