需求:
记录系统日志,要求字段有:一级菜单名,二级菜单名,进行了什么操作
实现方式:
两个注解:
package com.bshf.recipe.demo.syslog;
import java.lang.annotation.*;
/**
* <p>用于记录系统操作日志的注解 -- 类注解 </p>
* <p>在需要记录系统操作日志的Controller上添加该注解</p>
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CategoryLog {
/**
* 一级菜单名
*/
String menu1();
/**
* 二级菜单名
*/
String menu2();
}
package com.bshf.recipe.demo.syslog;
import java.lang.annotation.*;
/**
* <p>用于记录系统操作日志的注解 -- 方法注解</p>
* <p>在需要记录系统操作日志的Controller中待记录系统操作日志的方法上添加该注解。</p>
*
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DescLog {
/**
* 操作名称
*/
String value();
}
注解处理器:
package com.bshf.recipe.demo.syslog;
import java.lang.reflect.Method;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SyslogInterceptor extends HandlerInterceptorAdapter {
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
if(! (handler instanceof HandlerMethod)) {
return ;
}
HandlerMethod method = (HandlerMethod)handler;
Class<? extends Object> controller = method.getBean().getClass();
//获取类上的CategoryLog注解
CategoryLog annotation = controller.getAnnotation(CategoryLog.class);
//若类上没注解,则不记录日志
if(null == annotation) {
return ;
}
String menu1 = annotation.menu1();
String menu2 = annotation.menu2();
Method anyMethod = method.getMethod();
//获取所有方法上的DescLog注解
DescLog methodLog = anyMethod.getAnnotation(DescLog.class);
if (null == methodLog) {
return ;
}
String value = methodLog.value();
//真正开始记录日志的代码。
// SysLog sysLog = new SysLog();
// sysLog.setXXX();
// sysLogService.save(sysLog);
}
}
配置文件:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.bshf.recipe.demo.syslog.SyslogInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
Demo测试:
package controller;
import annotation.CategoryLog;
import annotation.DescLog;
/**
* @author 15620646321@163.com
* @date 2017年4月6日
*/
@CategoryLog(firstMenu = "基本信息", secondMenu = "用户")
@Controller
@RequestMapping("/demo")
public class DemoController {
@DescLog("HelloWorld登陆了")
@RequestMapping("/test")
public void test() {
System.out.println("HelloWorld!");
}
}
程序运行流程:
当调用test接口时,会首先会进入拦截器的preHandle方法,由于继承的HandlerInterceptorAdapter,而HandlerInterceptorAdapter 中的preHandle方法默认为true,所以进行test方法的业务逻辑,当走完逻辑(这时候只是走完了逻辑,页面并没有渲染数据),会调用postHandle方法进行记录日志,postHandle方法拿到注解上的值,就可以知道谁对某个模块操作了什么。当走完postHandle后,会调用HandlerInterceptorAdapter的afterCompletion(页面渲染完数据后会执行此方法)方法进行清理工作。
问:为什么不在afterCompletion方法进行日志记录?
答:因为如果我有多个拦截器或者就这一个,只要遇到preHandle方法返回false的,test主逻辑方法就不会在执行,就只会执行preHandle和afterCompletion方法,test主逻辑和postHandle方法都不会执行。这样就会产生我本来没有操作成功,return false了,但是还是给我记录了一条日志。而postHandle方法只要你程序抛出异常了或者preHandle返回false了,就不会执行,这样就不会记录系统日志。想要记录异常日志,可以配合logback+aop。
如对HandlerInterceptor不懂的请看我的这篇文章:http://www.jianshu.com/p/1e8d088c2be9
若有兴趣,欢迎来加入群,【Java初学者学习交流群】:458430385,此群有Java开发人员、UI设计人员和前端工程师。有问必答,共同探讨学习,一起进步!
欢迎关注我的微信公众号【Java码农社区】,会定时推送各种干货:
qrcode_for_gh_577b64e73701_258.jpg
网友评论