使用SpringAop切面+自定义注解将日志记录存储到数据库的应用实例步骤:
1.配置applicationContext-mvc.xml
<mvc:annotation-driven />
<!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="com.gcx" />
<!-- 启动对@AspectJ注解的支持 -->
<!-- proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口 就走JDK代理,如果没有,走cglib代理 -->
<!-- 注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib -->
<!--如果不写proxy-target-class="true"这句话也没问题-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--切面-->
<bean id="systemLogAspect" class="com.gcx.annotation.SystemLogAspect"></bean>
2.创建日志类实体
(1)日志分为:操作日志,系统日志
(2)操作日志:
public class OperationLog implements java.io.Serializable{
private static final long serialVersionUID = 5509842054233533179L;
@Id
private Long id;
/*
* 备注
*/
private String description;
/*
* 操作名称
*/
private String name;
/*
* 父模块名称
*/
private String parentName;
/*
* URL
*/
private String url;
/*
* 操作值
*/
private Float value;
/*
* 操作类型
*/
private Integer category;
/*
* 触发类型 0:触发前 1:触发后 2:异常
*/
private Integer triggerType;
/*
* 操作日期
*/
private Date recordTime;
/*
* 操作人
*/
private String operator;
(3)系统日志:
public class SystemLog implements java.io.Serializable{
private static final long serialVersionUID = 8422381527159954814L;
@Id
private Long id;
/*
* 备注
*/
private String description;
/*
* 异常名称
*/
private String name;
/*
* 异常内容
*/
private String content;
/*
* 错误类型,如数据库、webservice等
*/
private String category;
/*
* 异常日期
*/
private Date recordTime;
3.编写日志dao层,dao层实现类daoImpl,service层,service实现类serviceImpl,controller层
(1)到这里基本程序编写完毕
4.自定义系统日志注解
package com.gcx.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD}) //定义注解的作用目标、范围字段、枚举常量、方法
@Retention(RetentionPolicy.RUNTIME) //注解会在class中存在,运行时可通过反射获取
@Documented //说明该注解将被包含在javadoc中
public @interface LogAnnotation {
/** 要执行的操作类型比如:add操作 **/
public String operationType() default "";
/** 要执行的具体操作比如:添加用户 **/
public String operationName() default "";
}
5.编写切面类
(1)将切面的名称命名为:SystemLogAspect
(2)这个类当中将声明各种通知,以及处理通知的方法
(3)调用Logservice来实现将记录的日志写入到数据库当中,用到的就是反射的方法
(4)下面是代码:
//这个类就是切面
@Aspect
//让其被Spring容器去管理
@Component
public class SystemLogAspect {
//注入用于把日志保存数据库
@Resource
private OperationLogDAO operationLogDAO;
@Resource
private SystemLogDAO systemLogDAO;
@Resource
private UserDAO userDAO;
//本地异常日志记录对象
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);
// 指定切入点,拦截哪些方法,系统中所有执行操作方法的切点(注解其实就是切点)
@Pointcut(value="@annotation(com.gcx.annotation.OperationLogger)")
public void operationLogPoint(){}
@Pointcut("execution(* com.gcx.annotation..*.*(..))")
public void systemLogPoint() {}
/**
* 前置通知 用于拦截记录用户的操作
* @param joinPoint 切点
* JoinPoint就是连接点,切点的实现方法就是通知
*/
@Before("operationLogPoint()")
public void before(JoinPoint joinPoint){
System.out.println("我是前置通知");
if(logger.isInfoEnabled()){
logger.info("before " + joinPoint);
}
}
@After("operationLogPoint()")
public void After(JoinPoint joinPoint){
System.out.println("我是后置通知");
//去处理日志
OperationLog log= new OperationLog();
//控制日志是否记录,避免记录空日志
Boolean isOK = true;
try {
//获取URL信息
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String url = request.getRequestURL().toString();
String userId = request.getHeader("user");
String userName = null;
if(!userId.equals("-1")){
userName = userDAO.getSingleUserByid(Long.parseLong(userId)).getName();
}else{
userName = "-1";
}
//被织入的目标对象(获取注解中对方法的描述信息)
LogAnnotation annotationClass = point.getTarget().getClass().getAnnotation(LogAnnotation.class);
String moduleName = annotationClass.moduleName();
//操作名称
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
LogAnnotation annotationMethod = targetMethod.getAnnotation(LogAnnotation.class);
String operationName = annotationMethod.operationName();
if(operationName == null || operationName.isEmpty()){
isOK = false;
}
//触发类型
Integer triggerType = 0;
log.setName(operationName);
log.setParentName(moduleName);
log.setUrl(url);
log.setRecordTime(new Date());
log.setTriggerType(triggerType);
log.setOperator(userName);
log.setDescription(userName+"准备执行操作"+operationName);
} catch (SecurityException e) {
isOK = false;
recordSystemLog(e);
e.printStackTrace();
} finally{
if(isOK){
//保存数据库
operationLogDAO.saveLog(log);
}
}
}
@Around("operationLogPoint()")
public void around(JoinPoint joinPoint){
System.out.println("我是环绕通知");
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(logger.isInfoEnabled()){
logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
}
System.out.println("==========结束执行controller环绕通知===============");
} catch (Throwable e) {
long end = System.currentTimeMillis();
if(logger.isInfoEnabled()){
logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
}