在使用spring完成项目的时候需要完成记录日志,开始以为Spring 的AOP功能,就可以轻松解决,半个小时都不用,可是经过一番了解过后,发现一般的日志记录,只能记录一些简单的操作,例如表名、表名称等记录不到。
这个时侯就用到了自定义注解,把想要记录的内容放在注解中,通过切入点来获取到注解参数,然后将参数插入数据库记录
定义一个日志描述和一个表名这里根据需要自定义注解
package com.ywj.log;
import java.lang.annotation.*;
/**
* ClassName Crmlog
* AOP日志记录 自定义注解类
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemCrmlog {
/**
* 日志描述
* 对于什么表格进行了什么操作
*/
String description() default "";
/**
* 操作了的表名
* @return
*/
String tableName() default "";
}
对于一些可能碰到的问题我在方法的注释里都有解决办法,大家注意一下,这里我对于方法报错也有处理方法
这里是对于切面类里使用到的两个类解释:
AspectJ使用org.aspectj.lang.JoinPoint
接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint
表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:
java.lang.Object[] getArgs()
:获取连接点方法运行时的入参列表;Signature getSignature()
:获取连接点的方法签名对象;java.lang.Object getTarget()
:获取连接点所在的目标对象;java.lang.Object getThis()
:获取代理对象本身;ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
java.lang.Object proceed() throws java.lang.Throwable
:通过反射执行目标对象的连接点处的方法;java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable
:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。[因为看了这些资料,我就成了别人眼中的大佬!] package com.ywj.log;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ywj.log.biz.Sys_logBiz;
import com.ywj.log.dao.Sys_logDao;
import com.ywj.login.biz.Sys_UserBiz;
import com.ywj.login.dao.Sys_UserDao;
import com.ywj.login.dao.Sys_righDao;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @ClassName SystemLogAspect
* @Author Administrator
* @Describe 定义切入面类
*/
@Aspect
@Component
public class SystemLogAspect {
/**
* 注解Pointcut切入点
* 定义出一个或一组方法,当执行这些方法时可产生通知
* 指向你的切面类方法
* 由于这里使用了自定义注解所以指向你的自定义注解
*/
@Pointcut("@annotation(com.ywj.log.SystemCrmlog)")
public void crmAspect() {
}
/**
*抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知
* 注意在这里不能使用ProceedingJoinPoint
* 不然会报错ProceedingJoinPoint is only supported for around advice
* throwing注解为错误信息
* @param joinPoint
* @param ex
*/
@AfterThrowing(value="crmAspect()", throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) throws Exception {
HttpServletRequest httpServletRequest = getHttpServletRequest();
//获取管理员用户信息\
WebUtil webUtil = new WebUtil();
Map<String, Object> user = webUtil.getUser(httpServletRequest);
CrmLogMessage log=new CrmLogMessage();
//获取需要的信息
String context=getServiceMthodDescription(joinPoint);
String usr_name="";
String rolename="";
if(user!=null){
usr_name = user.get("usr_name").toString();
rolename=user.get("rolename").toString();
}
//管理员姓名
log.setUserName(usr_name);
//角色名
log.setUserRole(rolename);
//日志信息
log.setContent(usr_name+context);
//设置参数集合
log.setRemarks(getServiceMthodParams(joinPoint));
//设置表名
log.setTableName(getServiceMthodTableName(joinPoint));
//操作时间
SimpleDateFormat sif=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.setDateTime(sif.format(new Date()));
//设置ip地址
log.setIp(httpServletRequest.getRemoteAddr());
//设置请求地址
log.setRequestUrl(httpServletRequest.getRequestURI());
//执行结果
log.setResult("执行失败");
//错误信息
log.setExString(ex.getMessage());
//将数据保存到数据库
Sys_logDao sysLogDao=new Sys_logDao();
sysLogDao.addSys_log(log);
}
/**
* 返回后通知(@AfterReturning):在某连接点(joinpoint)
* 正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回
* 方法执行完毕之后
* 注意在这里不能使用ProceedingJoinPoint
* 不然会报错ProceedingJoinPoint is only supported for around advice
* crmAspect()指向需要控制的方法
* returning 注解返回值
* @param joinPoint
* @param returnValue 返回值
* @throws Exception
*/
@AfterReturning(value = "crmAspect()",returning = "returnValue")
public void doCrmLog(JoinPoint joinPoint,Object returnValue) throws Exception {
HttpServletRequest httpServletRequest = getHttpServletRequest();
//获取管理员用户信息
WebUtil webUtil = new WebUtil();
Map<String, Object> user = webUtil.getUser(httpServletRequest);
CrmLogMessage log=new CrmLogMessage();
String context=getServiceMthodDescription(joinPoint);
String usr_name="";
String rolename="";
if(user!=null){
usr_name = user.get("usr_name").toString();
rolename=user.get("rolename").toString();
}
//管理员姓名
log.setUserName(usr_name);
//角色名
log.setUserRole(rolename);
//日志信息
log.setContent(usr_name+context);
//设置参数集合
log.setRemarks(getServiceMthodParams(joinPoint));
//设置表名
log.setTableName(getServiceMthodTableName(joinPoint));
//操作时间
SimpleDateFormat sif=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.setDateTime(sif.format(new Date()));
//设置ip地址
log.setIp(httpServletRequest.getRemoteAddr());
//设置请求地址
log.setRequestUrl(httpServletRequest.getRequestURI());
if(returnValue!=null){
if(returnValue instanceof List){
List ls= (List) returnValue;
if(ls.size()>0){
log.setResult("执行成功");
}else{
log.setResult("执行成功");
}
}else if(returnValue instanceof Boolean){
Boolean falg= (Boolean) returnValue;
if(falg){
log.setResult("执行成功");
}else{
log.setResult("执行失败");
}
}else if(returnValue instanceof Integer){
Integer i= (Integer) returnValue;
if(i>0){
log.setResult("执行成功");
}else{
log.setResult("执行失败");
}
}else{
log.setResult("执行成功");
}
}
//将数据保存到数据库
Sys_logDao sysLogDao=new Sys_logDao();
sysLogDao.addSys_log(log);
}
/**
*获取自定义注解里的日志描述
* @param joinPoint
* @return 返回注解里面的日志描述
* @throws Exception
*/
private String getServiceMthodDescription(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[] methods = targetClass.getMethods();
String description = "";
for(Method method : methods) {
//判断方法名是不是一样
if(method.getName().equals(methodName)) {
//对比参数数组的长度
Class[] clazzs = method.getParameterTypes();
if(clazzs.length == arguments.length) {
//获取注解里的日志信息
description = method.getAnnotation(SystemCrmlog.class).description();
break;
}
}
}
return description;
}
/**
*获取自定义注解里的表名
* @param joinPoint
* @return 返回注解里的表名字
* @throws Exception
*/
private String getServiceMthodTableName(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[] methods = targetClass.getMethods();
//表名
String tableName = "";
for (Method method : methods) {
//判断方法名是不是一样
if (method.getName().equals(methodName)) {
//对比参数数组的长度
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
//获取注解里的表名
tableName = method.getAnnotation(SystemCrmlog.class).tableName();
break;
}
}
}
return tableName;
}
/**
* 获取json格式的参数用于存储到数据库中
* @param joinPoint
* @return
* @throws Exception
*/
private String getServiceMthodParams(JoinPoint joinPoint)
throws Exception {
Object[] arguments = joinPoint.getArgs();
ObjectMapper om=new ObjectMapper();
return om.writeValueAsString(arguments);
}
/**
* 获取当前的request
* 这里如果报空指针异常是因为单独使用spring获取request
* 需要在配置文件里添加监听
* <listener>
* <listener-class>
* org.springframework.web.context.request.RequestContextListener
* </listener-class>
* </listener>
* @return
*/
public HttpServletRequest getHttpServletRequest(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)ra;
HttpServletRequest request = sra.getRequest();
return request;
}
}
每个切面传递的数据的都不一样,最终决定,获取切面的所有参数,转成json字符串,保存到数据库中。
日志信息类
package com.ywj.log;
/**
* @ClassName CrmLogMessage
* @Author Administrator
* @Describe 数据库日志类
*/
public class CrmLogMessage {
private Integer logid;//日志id
private String UserName;//管理员姓名
private String UserRole;//管理员角色
private String Content;//日志描述
private String Remarks;//参数集合
private String TableName;//表格名称
private String DateTime;//操作时间
private String resultValue;//返回值
private String ip;//ip地址
private String requestUrl;//请求地址
private String result;//操作结果
private String ExString;//错误信息
public CrmLogMessage() {
}
@Override
public String toString() {
return "CrmLogMessage{" +
"logid=" + logid +
", UserName='" + UserName + '\'' +
", UserRole='" + UserRole + '\'' +
", Content='" + Content + '\'' +
", Remarks='" + Remarks + '\'' +
", TableName='" + TableName + '\'' +
", DateTime='" + DateTime + '\'' +
", resultValue='" + resultValue + '\'' +
", ip='" + ip + '\'' +
", requestUrl='" + requestUrl + '\'' +
", result='" + result + '\'' +
", ExString='" + ExString + '\'' +
'}';
}
public CrmLogMessage(Integer logid, String userName, String userRole, String content, String remarks, String tableName, String dateTime, String resultValue, String ip, String requestUrl, String result, String exString) {
this.logid = logid;
UserName = userName;
UserRole = userRole;
Content = content;
Remarks = remarks;
TableName = tableName;
DateTime = dateTime;
this.resultValue = resultValue;
this.ip = ip;
this.requestUrl = requestUrl;
this.result = result;
ExString = exString;
}
public String getExString() {
return ExString;
}
public void setExString(String exString) {
ExString = exString;
}
public Integer getLogid() {
return logid;
}
public void setLogid(Integer logid) {
this.logid = logid;
}
public String getUserName() {
return UserName;
}
public void setUserName(String userName) {
UserName = userName;
}
public String getUserRole() {
return UserRole;
}
public void setUserRole(String userRole) {
UserRole = userRole;
}
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
public String getRemarks() {
return Remarks;
}
public void setRemarks(String remarks) {
Remarks = remarks;
}
public String getTableName() {
return TableName;
}
public void setTableName(String tableName) {
TableName = tableName;
}
public String getDateTime() {
return DateTime;
}
public void setDateTime(String dateTime) {
DateTime = dateTime;
}
public String getResultValue() {
return resultValue;
}
public void setResultValue(String resultValue) {
this.resultValue = resultValue;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getRequestUrl() {
return requestUrl;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
用来获取登录用户信息的帮助类:
package com.ywj.log;
import com.base.web.BaseAction;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @ClassName WebUtil
* @Author Administrator
* @Describe 日志帮助类 用来获取session中的用户信息来存入数据库
*/
public class WebUtil {
/**
* 从session中获取到用户对象
* @return
*/
public Map<String, Object> getUser(HttpServletRequest request){
Map<String, Object> attribute=null;
if(request!=null){
Object user = request.getSession().getAttribute(Constans.USER_KEY);
attribute = (Map<String, Object>) user;}
return attribute;
}
}
在你的spring-context.xml中配置
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 自动扫描包路径 -->
<!--你需要刚才的切面类的包路径-->
<context:component-scan base-package="com.ywj.log" />
<!--你需要注解方法的包路径-->
<context:component-scan base-package="com.*.*.biz.impl" />
然后在你需要记录的方法上加上注解
@SystemCrmlog(description = "进行了登录操作",tableName =Constans.USER_TABLENAME)
效果这里表名使用了常量类 对于一些表的信息可以写一个常量类 然后执行登录操作数据库记录为:
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8