Spring Boot利用自定義註解記錄請求或方法執行日誌

Spring Boot利用自定義註解記錄請求或方法執行日誌

首先,定義日誌註解,註解字段可自行擴展java

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Log {
	
    /**操做名稱*/
    String value() default "";
  	/**模塊名*/
    String moduleName() default "";

}

主要思路是spring

利用AOP攔截被註解標註的方法,進行相關參數獲取。爲了防止日誌保存影響正常的業務執行,所以利用Spring的事件機制,發送事件給監聽器,監聽器收到事件後,異步保存日誌。app

注:在本示例中,使用Lombok註解,請自行了解Lombok註解做用。異步

下面是Aop的實現類,拿到攔截參數後,關鍵的一步是經過spring 的事件機制,將事件廣播出去。ide

LogAop.java工具

@Slf4j
@Aspect
@Component
public class LogAop {

    @Pointcut(value = "@annotation(com.chillax.boot.core.common.annotation.Log)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
        long startTime = System.currentTimeMillis();
        //先執行業務
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        try {
            handle(point, endTime - startTime, null);
        } catch (Exception e) {
            log.error("日誌記錄出錯!", e);
        }
        return result;
    }

    @AfterThrowing(pointcut = "cutService()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) {
        try {
            handle(point, null, e);
        } catch (Exception ex) {
            log.error("日誌記錄出錯!", ex);
        }
    }

    private void handle(JoinPoint point, Long methodProcessTime, Throwable e) throws Exception {
        //獲取攔截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("該註解只能用於方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();
        //獲取攔截方法的參數
        Object[] params = point.getArgs();
        //獲取操做名稱
        Log annotation = currentMethod.getAnnotation(Log.class);
        String moduleName = annotation.moduleName();
        String operationName = annotation.value();
        
        //這裏根據本身的業務需求,封裝本身的業務類
        SysLog sysLog = new SysLog();
        SpringContextUtil.publishEvent(new SysLogEvent(sysLog));
    }

}

日誌事件類,用於事件間的傳遞。能夠擴展此類用作其餘用途。ui

SysLogEvent.javadebug

public class SysLogEvent extends ApplicationEvent {

    public SysLogEvent(Object source) {
        super(source);
    }

}

Spring容器工具類,在項目啓動時,注入Spring上下文,而後封裝一下事件發送方法及其餘經常使用方法。日誌

SpringContextUtil.javacode

@Slf4j
@Service
@Lazy(false)
public class SpringContextUtil implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext;

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

    public static void publishEvent(ApplicationEvent event) {
        if (applicationContext != null) {
            applicationContext.publishEvent(event);
        }
    }

    public static void clearHolder() {
        if (log.isDebugEnabled()) {
            log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
        }
        applicationContext = null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }
    @Override
    public void destroy() throws Exception {
        SpringContextUtil.clearHolder();
    }
}

最後,編寫事件監聽器。

@Async 標註此方法執行爲異步,須要使用**@EnableAsync**註解開啓此功能

@Order標記此監聽器爲最低級別加載

@EventListener(SysLogEvent.class) 標註監聽的事件

比較重要的是,監聽器須要將它加入到Spring容器中。經過event.getSource() 獲取到發送事件時,傳遞的對象。

SysLogListener.java

@AllArgsConstructor
@Slf4j
@Component
public class SysLogListener {

    private final ISysLogService sysLogService;

    @Async
    @Order
    @EventListener(SysLogEvent.class)
    public void saveSysLog(SysLogEvent event) {
        SysLog sysLog = (SysLog) event.getSource();
        sysLogService.save(sysLog);
    }
}
相關文章
相關標籤/搜索