Spring自定义注解AOP,进行记录用户操作日志

数据库设计:


数据库名:log

结构:


generator生成的Log以及LogWithBLOBs修改:

public class Log implements Serializable {
	private String logId;			//日志主键	
	private String type;			//日志类型
	private Date operateDate;			//开始时间

    private static final long serialVersionUID = 1L;

    public String getLogId() {
        return StringUtils.isBlank(logId) ? logId : logId.trim();
    }
    public void setLogId(String logId) {
        this.logId = logId;
    }
    
    
    public String getType() {
        return StringUtils.isBlank(type) ? type : type.trim();
    }
    public void setType(String type) {
        this.type = type;
    }

    public Date getOperateDate() {
        return operateDate;
    }
    public void setOperateDate(Date operateDate) {
        this.operateDate = operateDate;
    }
}
public class LogWithBLOBs extends Log implements Serializable {
	private String title;			//日志标题	
	private String remoteAddr;			//请求地址	
	private String requestUri;			//URI	
	private String method;			//请求方式	
	private String params;			//提交参数	
	private String exception;			//异常	
	private String timeout;			//结束时间	
	private String userId;			//用户ID	

    private static final long serialVersionUID = 1L;

    public String getTitle() {
        return StringUtils.isBlank(title) ? title : title.trim();
    }
    public void setTitle(String title) {
        this.title = title;
    }
    
    
    public String getRemoteAddr() {
        return StringUtils.isBlank(remoteAddr) ? remoteAddr : remoteAddr.trim();
    }
    public void setRemoteAddr(String remoteAddr) {
        this.remoteAddr = remoteAddr;
    }
    
    
    public String getRequestUri() {
        return StringUtils.isBlank(requestUri) ? requestUri : requestUri.trim();
    }
    public void setRequestUri(String requestUri) {
        this.requestUri = requestUri;
    }
    
    
    public String getMethod() {
        return StringUtils.isBlank(method) ? method : method.trim();
    }
    public void setMethod(String method) {
        this.method = method;
    }
    
    
    public String getParams() {
        return StringUtils.isBlank(params) ? params : params.trim();
    }
    public void setParams(String params) {
        this.params = params;
    }
    
	/**
	 * 设置请求参数
	 * @param paramMap
	 */
	public void setMapToParams(Map<String, String[]> paramMap) {
		if (paramMap == null){
			return;
		}
		StringBuilder params = new StringBuilder();
		for (Map.Entry<String, String[]> param : ((Map<String, String[]>)paramMap).entrySet()){
			params.append(("".equals(params.toString()) ? "" : "&") + param.getKey() + "=");
			String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : "");
			params.append(StringUtils.abbr(StringUtils.endsWithIgnoreCase(param.getKey(), "password") ? "" : paramValue, 100));
		}
		this.params = params.toString();
	}
    
    
    public String getException() {
        return StringUtils.isBlank(exception) ? exception : exception.trim();
    }
    public void setException(String exception) {
        this.exception = exception;
    }
    
    

    
    public String getTimeout() {
        return StringUtils.isBlank(timeout) ? timeout : timeout.trim();
    }
    public void setTimeout(String timeout) {
        this.timeout = timeout;
    }
    
    
    public String getUserId() {
        return StringUtils.isBlank(userId) ? userId : userId.trim();
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
	
}

service以及impl:

public interface LogService {
    long countByExample(LogExample example);

    int deleteByExample(LogExample example);

    int deleteByPrimaryKey(String logid);

    int insert(LogWithBLOBs record);

    int insertSelective(LogWithBLOBs record);

    List<LogWithBLOBs> selectByExampleWithBLOBs(LogExample example);

    List<Log> selectByExample(LogExample example);

    LogWithBLOBs selectByPrimaryKey(String logid);

    int updateByExampleSelective(@Param("record") LogWithBLOBs record, @Param("example") LogExample example);

    int updateByExampleWithBLOBs(@Param("record") LogWithBLOBs record, @Param("example") LogExample example);

    int updateByExample(@Param("record") Log record, @Param("example") LogExample example);

    int updateByPrimaryKeySelective(LogWithBLOBs record);

    int updateByPrimaryKeyWithBLOBs(LogWithBLOBs record);

    int updateByPrimaryKey(Log record);
}
@Service("logService")
public class LogServiceImpl  implements LogService{
	

	@Autowired
	private LogMapper dao;

	@Override
	public long countByExample(LogExample example) {
		// TODO Auto-generated method stub
		return dao.countByExample(example);
	}

	@Override
	public int deleteByExample(LogExample example) {
		// TODO Auto-generated method stub
		return dao.deleteByExample(example);
	}

	@Override
	public int deleteByPrimaryKey(String logid) {
		// TODO Auto-generated method stub
		return dao.deleteByPrimaryKey(logid);
	}

	@Override
	public int insert(LogWithBLOBs record) {
		// TODO Auto-generated method stub
		return dao.insert(record);
	}

	@Override
	public int insertSelective(LogWithBLOBs record) {
		// TODO Auto-generated method stub
		return dao.insertSelective(record);
	}

	@Override
	public List<LogWithBLOBs> selectByExampleWithBLOBs(LogExample example) {
		// TODO Auto-generated method stub
		return dao.selectByExampleWithBLOBs(example);
	}

	@Override
	public List<Log> selectByExample(LogExample example) {
		// TODO Auto-generated method stub
		return dao.selectByExample(example);
	}

	@Override
	public LogWithBLOBs selectByPrimaryKey(String logid) {
		// TODO Auto-generated method stub
		return dao.selectByPrimaryKey(logid);
	}

	@Override
	public int updateByExampleSelective(LogWithBLOBs record, LogExample example) {
		// TODO Auto-generated method stub
		return dao.updateByExampleSelective(record, example);
	}

	@Override
	public int updateByExampleWithBLOBs(LogWithBLOBs record, LogExample example) {
		// TODO Auto-generated method stub
		return dao.updateByExampleWithBLOBs(record, example);
	}

	@Override
	public int updateByExample(Log record, LogExample example) {
		// TODO Auto-generated method stub
		return dao.updateByExample(record, example);
	}

	@Override
	public int updateByPrimaryKeySelective(LogWithBLOBs record) {
		// TODO Auto-generated method stub
		return dao.updateByPrimaryKeySelective(record);
	}

	@Override
	public int updateByPrimaryKeyWithBLOBs(LogWithBLOBs record) {
		// TODO Auto-generated method stub
		return dao.updateByPrimaryKeyWithBLOBs(record);
	}

	@Override
	public int updateByPrimaryKey(Log record) {
		// TODO Auto-generated method stub
		return dao.updateByPrimaryKey(record);
	}

}


然后就是注解的接口:

/**
 * 
 * 自定义注解,用于Controller层
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemControllerLog {
	
	String description() default "";
}

切口类:

@Aspect
@Component
public class SystemLogAspect {
	private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);
	
	private static final ThreadLocal<Date> beginTimeThreadLocal_controller =
			new NamedThreadLocal<Date>("ThreadLocal beginTime");
	private static final ThreadLocal<LogWithBLOBs> logThreadLocal = 
			new NamedThreadLocal<LogWithBLOBs>("ThreadLocal log");
	
	private static final ThreadLocal<Managers> currentUser=new NamedThreadLocal<>("ThreadLocal user");
	
	@Autowired(required=false)
	private HttpServletRequest request;
	
	@Autowired
	private ThreadPoolTaskExecutor threadPoolTaskExecutor;

	@Autowired
	private LogService logService;

	/**
	 * Controller层切点 注解拦截
	 */
	@Pointcut("@annotation(com.xx.syslog.SystemControllerLog)")
	public void controllerAspect(){}
	
	/**
	 * 方法规则拦截
	 */
	@Pointcut("execution(* com.xx.web.*.*(..))")
	public void controllerPointerCut(){}
	/**
	 * 前置通知 用于拦截Controller层记录用户的操作的开始时间
	 * @param joinPoint 切点
	 * @throws InterruptedException 
	 */
	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) throws InterruptedException{
		Date beginTime=new Date();
		beginTimeThreadLocal_controller.set(beginTime);
		//读取session中的用户 
		HttpSession session = request.getSession();       
		Managers user = (Managers) session.getAttribute("managers");    
		currentUser.set(user);

	}
	
	/**
	 * 后置通知 用于拦截Controller层记录用户的操作
	 * @param joinPoint 切点
	 */
	@SuppressWarnings("unchecked")
	@After("controllerAspect()")
	public void doAfter(JoinPoint joinPoint) {
		Managers user = currentUser.get();
        //登入login操作 前置通知时用户未校验 所以session中不存在用户信息
        if(user == null){
    		HttpSession session = request.getSession();       
    	    user = (Managers) session.getAttribute("managers");    
    	    if(user==null){
    	    	return;
    	    }
        }
        Object [] args = joinPoint.getArgs();
        
    	String title="";
    	String type="info";						  //日志类型(info:入库,error:错误)
    	String remoteAddr=request.getRemoteAddr();//请求的IP
    	String requestUri=request.getRequestURI();//请求的Uri
    	String method=request.getMethod();		  //请求的方法类型(post/get)
    	Map<String,String[]> params=request.getParameterMap(); //请求提交的参数
    	try {
    		title=getControllerMethodDescription2(joinPoint);
    	} catch (Exception e) {
    		e.printStackTrace();
    	}    
    	// debu模式下打印JVM信息。
    	long beginTime = beginTimeThreadLocal_controller.get().getTime();//得到线程绑定的局部变量(开始时间)  
    	long endTime = System.currentTimeMillis(); 	//2、结束时间  
    	
    	LogWithBLOBs log=new LogWithBLOBs();
    	log.setLogId(UuidUtils.creatUUID());
    	log.setTitle(title);
    	log.setType(type);
    	log.setRemoteAddr(remoteAddr);
    	log.setRequestUri(requestUri);
    	log.setMethod(method);
    	log.setMapToParams(params);
    	log.setUserId(user.getName());
    	Date operateDate=beginTimeThreadLocal_controller.get();
    	log.setOperateDate(operateDate);
    	log.setTimeout(DateUtils.formatDateTime(endTime - beginTime));

    	 //1.直接执行保存操作
        //this.logService.createSystemLog(log);

        //2.优化:异步保存日志
        //new SaveLogThread(log, logService).start();

        //3.再优化:通过线程池来执行日志保存
        threadPoolTaskExecutor.execute(new SaveLogThread(log, logService));
        logThreadLocal.set(log);
       
 
	}
	
	/**
	 *  异常通知 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")  
	public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		LogWithBLOBs log = logThreadLocal.get();
		if(log != null){
			log.setType("error");
			log.setException(e.toString());
			new UpdateLogThread(log, logService).start();			
		}
	}
	

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * 
	 * @param joinPoint 切点
	 * @return 方法描述
	 */
	public static String getControllerMethodDescription2(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		SystemControllerLog controllerLog = method
				.getAnnotation(SystemControllerLog.class);
		String discription = controllerLog.description();
		return discription;
	}

	/**
	 * 保存日志线程
	 * 
	 * @author lin.r.x
	 *
	 */
	private static class SaveLogThread implements Runnable {
		private LogWithBLOBs log;
		private LogService logService;

		public SaveLogThread(LogWithBLOBs log, LogService logService) {
			this.log = log;
			this.logService = logService;
		}

		@Override
		public void run() {
			logService.insertSelective(log);
		}
	}

	/**
	 * 日志更新线程
	 * 
	 * @author lin.r.x
	 *
	 */
	private static class UpdateLogThread extends Thread {
		private LogWithBLOBs log;
		private LogService logService;

		public UpdateLogThread(LogWithBLOBs log, LogService logService) {
			super(UpdateLogThread.class.getSimpleName());
			this.log = log;
			this.logService = logService;
		}

		@Override
		public void run() {
			this.logService.updateByPrimaryKeySelective(log);
		}
	}
}

spring 配置扫描切面,开启@AspectJ注解的支持:

<!-- 启动对@AspectJ注解的支持 -->  
	<aop:aspectj-autoproxy/> 
 <!-- 自定义注解,执行记录系统日志 -->
    <context:component-scan base-package="com.xx.syslog" />
<bean id="taskExecutor"   
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
		<property name="corePoolSize" value="5" />  
		<property name="maxPoolSize" value="10" />  
	</bean> 

使用:



模拟error情况:


然后数据库:


前端显示略;

参考:地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值