写用户的操作日志,使用拦截器还是使用aop的方式呢?纠结了好久, 先是使用拦截器的方式,写了一半,发现好多参数没法获取,感觉比较麻烦。 后来又换做aop的方式。再后来,发现两种方式其实都可以。对于一些 自定义的参数,可以写一个注解来解决。 以下是两种方式的总结:
1.创建拦截器类
public class LogInterceptor implements HandlerInterceptor { private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class); @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //把整个log中的参数,交给logUtil来获取,并返回log对象 Log log = null; try { log = LoggerUtil.getLog(httpServletRequest); }catch (GeneralException g){ logger.warn("logger",g.getMessage()); }catch (Exception e){ logger.error("logger",e.getMessage()); } httpServletRequest.setAttribute(LoggerUtil.LOG_OPERATE,log); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { //返回视图时,插入操作日志 LogMapper logMapper = getMapper(LogMapper.class,httpServletRequest); Log log = (Log) httpServletRequest.getAttribute(LoggerUtil.LOG_OPERATE); if(log == null){ logger.warn("日志信息为空",log); }else{ logMapper.insert(log); } } private <T> T getMapper(Class<T> clazz,HttpServletRequest request) { BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); return factory.getBean(clazz); } }拦截器的执行顺序,这里不解释了。这里注意mapper类是如何创建的。 2. 注册拦截器
@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login"); registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } } LoggerUtil类 public class LoggerUtil { public static final String LOG_TARGET_TYPE="targetType"; public static final String LOG_ACTION="action"; public static final String LOG_REMARK="remark"; public LoggerUtil(){} public static Log getLog(HttpServletRequest request){ //1.依次获取每个属性信息 userId,operator,action,remark,ip,targetType Log log = new Log(); log.setIp(LoggerUtil.getCliectIp(request)); log.setOperator("operator"); log.setUserId(1); log.setAction("create"); log.setCustomerId("0000-1111"); log.setTargetType("message"); log.setRemark("消息发布"); return log; } /** * 获取客户端ip地址 * @param request * @return */ public static String getCliectIp(HttpServletRequest request){ String ip = request.getHeader("X-Real-IP"); if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) { return ip; } ip = request.getHeader("X-Forwarded-For"); if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个IP值,第一个为真实IP。 int index = ip.indexOf(','); if (index != -1) { return ip.substring(0, index); } else { return ip; } } else { return request.getRemoteAddr(); } } } loggerUtil类主要返回一个Log对象的实体类。我们可以将切入方法设置到自定义的log注解上,这样aop就会只在有log注解的方法进行拦截了。
特殊字段的注解 @Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取 @Target(ElementType.METHOD)//目标是方法 @Documented//文档生成时,该注解将被包含在javadoc中,可去掉 public @interface LogAnnotation { String action() default ""; String targetType() default ""; String remark() default ""; } 引用部分 @LogAnnotation(targetType = "user",action = "create",remark = "用户登录") @RequestMapping(value = "/login",method = RequestMethod.POST) public ModelMap login(HttpServletRequest request){ ModelMapHelper helper = new ModelMapHelper(); String userName = request.getParameter("userName"); String password = request.getParameter("password"); String imgCode = request.getParameter("imageCode"); String sessionCode = (String) request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); logger.info("input code is "+imgCode+" session id is "+request.getSession().getId()+" session code is "+sessionCode); if(StringUtils.isEmpty(imgCode)){ helper.setErrorMap("验证码不能为空"); return helper; } ..... }