SSM项目中使用Aop实现日志管理

最近在工作中需要给项目添加日志功能,看了项目里面是用了以前项目的日志模块,是通过在不同方法里面通过调用日志新增方法新增的,感觉这个太麻烦了,然后通过网上的一些列子就改了个切面的方式。

我这里要实现是在controller方法上添加一个自定义的注解,声明方法的模块名称,和作用,通过切面类,获取参数,新增日志,保存到数据库。
下面就上代码

首先是我们的自定义注解

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ControllerLog {
    //模块名称
	String Module() default "";
	//方法描述
	String Remark() default "";
}

日志实体类,这里记得写get和set方法,或者使用lombok插件

public class Log {

  //日志ID
  private String lId;
  //操作行为
  private String lAction;
  //备注
  private String lRemark;
  //操作人
  private String lCreator;
  //操作时间
  private Date lCreatetime;
  //模块
  private String lModule;
  //IP
  private String lIp;
  }
  
  //日志详情类
  public class LogDetail {

  private String lId;
  private String lKey;
  private String lValue;
  }

这里就不写service和mapper方法了,其实就是两个新增的方法

然后就是最重要的切面类了

Aspect
@Component
public class SystemLogAspect {
	
	//因为这里使用的线程,所以我使用了ThreadLocal来保存信息
	//保存开始时间
	private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<Date>("ThreadLocal beginTime");
    //保存用户信息
	private static final ThreadLocal<User> currentUser=new NamedThreadLocal<>("ThreadLocal userName");
    //保存要新增的日志信息
	private static final ThreadLocal<Log> logThreadLocal = new NamedThreadLocal<>("ThreadLocal log");
    //保存新增的日志详情
	private static final ThreadLocal<LogDetail> logDetailThreadLocal = new NamedThreadLocal<>("ThreadLocal logDetail");
	
	
	@Autowired(required=false)
	private HttpServletRequest request;
	
	//导入线程池
	@Autowired
	private ThreadPoolTaskExecutor threadPoolTaskExecutor;

	@Autowired
	private LogServiceI logService;

	/**
	 * Controller层切点 注解拦截
	 */
	@Pointcut("@annotation(com.atoz.ytl.server.log.ControllerLog)")
	public void controllerAspect(){}


	/**
	 * 前置通知 用于拦截Controller层记录用户的操作的开始时间
	 * @param joinPoint 切点
	 * @throws InterruptedException 
	 */
	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) throws InterruptedException{
		Date beginTime=new Date();
		beginTimeThreadLocal.set(beginTime);
		//因为项目里面是shiro,所以这里获取用户信息是这样子的
		//如果没有使用,这里可以直接使用session来获取
		User loginUser = (User) SecurityUtils.getSubject().getPrincipal();
		currentUser.set(loginUser);
	}
	
	/**
	 * 后置通知 用于拦截Controller层记录用户的操作
	 * @param joinPoint 切点
	 */
	@SuppressWarnings("unchecked")
	@After("controllerAspect()")
	public void doAfter(JoinPoint joinPoint) {
	    //获取当前用户的登录信息
		User loginUser = currentUser.get();
		if(loginUser == null){
			 loginUser = (User) SecurityUtils.getSubject().getPrincipal();
			if(loginUser==null){
				return;
			}
		}
		String userName=loginUser.getLoginName() + "(" + loginUser.getUserFullName() + ")";
		HashMap<String, String> controllerMethodDescription=null;
    	String remoteAddr=request.getRemoteAddr();//请求的IP
    	String requestUri=request.getRequestURI();//请求的Uri
		try {
		    //调用方法,获取注解参数
    		controllerMethodDescription = getControllerMethodDescription(joinPoint);
		} catch (Exception e) {
    		e.printStackTrace();
    	}

		Log log = logThreadLocal.get();
		if (log == null) {
			log = new Log();
		}
		LogDetail logDetail = logDetailThreadLocal.get();
		if (logDetail == null) {
			logDetail = new LogDetail();
		}
		String id= UUID.randomUUID().toString().replace("-", "");
    	log.setLId(id);
    	log.setLAction(requestUri);
    	log.setLCreator(userName);
    	log.setLIp(remoteAddr);
    	log.setLModule(controllerMethodDescription.get("module"));
    	log.setLRemark(controllerMethodDescription.get("remark"));
    	Date operateDate=beginTimeThreadLocal.get();
    	log.setLCreatetime(operateDate);

		logDetail.setLId(id);
		logDetail.setLKey("content");
		logDetail.setLValue(userName+controllerMethodDescription.get("remark"));
		logThreadLocal.set(log);
		logDetailThreadLocal.set(logDetail);
	}

	@AfterReturning(value = "controllerAspect()", returning = "result")
	public void doReturn(Object result) {
		Log log = logThreadLocal.get();
		if (log == null) {
			log = new Log();
		}
		LogDetail logDetail = logDetailThreadLocal.get();
		if (logDetail == null) {
			logDetail = new LogDetail();
		}
		//通过线程池来执行日志保存
		threadPoolTaskExecutor.execute(new SaveLogThread(log, logDetail,logService));
		currentUser.remove();
		beginTimeThreadLocal.remove();
	}



	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * 
	 * @param joinPoint 切点
	 * @return 方法描述
	 */
	public static HashMap<String,String> getControllerMethodDescription(JoinPoint joinPoint) {
		HashMap<String,String> hashMap=new HashMap<>();
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		ControllerLog controllerLog = method.getAnnotation(ControllerLog.class);
		String module = controllerLog.Module();
		hashMap.put("module",module);
		String remark = controllerLog.Remark();
		hashMap.put("remark",remark);
		return hashMap;
	}

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

		public SaveLogThread(Log log,LogDetail logDetail, LogServiceI logService) {
			this.log = log;
			this.logService = logService;
			this.logDetail=logDetail;
		}
		@Override
		public void run() {
		    //这里是两个新增的方法
			logService.insertSelective(log);
			logService.InsertLogDetail(logDetail);
		}
	}
}

因为这里使用了ThreadPoolTaskExecutor线程池,所以要在spring的xml中配置bean

<bean id="taskExecutor"
		  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<property name="corePoolSize" value="5" />
		<property name="maxPoolSize" value="10" />
		<property name="WaitForTasksToCompleteOnShutdown" value="true" />
	</bean>

最后就是使用注解了,这里是我项目里面的日志删除接口,就在方法上面添加一个注解就完成了

@RequestMapping(value="/deleteLog", method=RequestMethod.POST)
@ControllerLog(Module = "日志管理",Remark = "删除日志")
public void deleteLog( HttpServletRequest req,HttpServletResponse response,@RequestParam(value = "idList") String[] idList) throws BusinessException,IOException {
	
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值