Spring AOP日志记录线程方法

Spring AOP logging thread method

是否有任何方法可以通过 ExecutorService 将 AOP 日志记录到 class 的 public 方法,即 implements Runnable 和 运行?

线程class

@Component
@Scope("prototype")
public class FileProcessor implements Runnable {

  private final LinkedBlockingQueue<File> filesQueue;
  private final GiftCertificateMapper certificateMapper;
  private final File errorFolder;
  private static final ReentrantLock LOCK = new ReentrantLock();

  private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessor.class);

  public FileProcessor(LinkedBlockingQueue<File> filesQueue, GiftCertificateMapper certificateMapper,
      File errorFolder) {
    this.filesQueue = filesQueue;
    this.certificateMapper = certificateMapper;
    this.errorFolder = errorFolder;
  }

  @Override
  public void run() {
    File file = null;
    try {
      while ((file = filesQueue.poll(100, TimeUnit.MILLISECONDS)) != null) {
        processFile(file);
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      LOGGER.warn("File thread was interrupted");
    } catch (IOException e) {
      LOGGER.error("Error processing file {} \n{}", file.getAbsolutePath(), e);
    }
  }

  public void processFile(File file) throws IOException {
    if (file != null) {
      try {
        ObjectMapper objectMapper = new ObjectMapper();
        List<GiftCertificate> certificates = Arrays.asList(objectMapper.readValue(file, GiftCertificate[].class));
        certificateMapper.insertList(certificates);
        file.delete();
      } catch (JsonParseException | UnrecognizedPropertyException | InvalidFormatException | DataIntegrityViolationException e) {
        moveFileToErrorFolder(file);
      }
    }
  }

  private void moveFileToErrorFolder(File file) throws IOException {
    try {
      LOCK.lock();
      Files.move(Paths.get(file.getAbsolutePath()), getPathForMovingFile(file), StandardCopyOption.ATOMIC_MOVE);
    } finally {
      LOCK.unlock();
    }
  }

  private Path getPathForMovingFile(File fileForMove) {
    File fileList[] = errorFolder.listFiles();
    int filesWithSameNameCounter = 0;
    if (fileList != null && fileList.length > 0) {
      for (File file : fileList) {
        if (file.getName().contains(fileForMove.getName())) {
          filesWithSameNameCounter++;
        }
      }
    }
    return filesWithSameNameCounter > 0 ?
        Paths.get(errorFolder.getAbsolutePath(), "(" + filesWithSameNameCounter + ")" + fileForMove.getName()) :
        Paths.get(errorFolder.getAbsolutePath(), fileForMove.getName());
  }
}

看点

@Aspect
@Component
@ConditionalOnProperty(
    value = "file-processing.logging.enabled",
    havingValue = "true",
    matchIfMissing = true)
public class FileProcessingLoggingAspect {

  private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessingLoggingAspect.class);

  @Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))")
  public void processFilePointcut() {
  }

  @Around("processFilePointcut()")
  public Object logFileProcessing(ProceedingJoinPoint joinPoint) throws Throwable {
//    File file = (File) joinPoint.getArgs()[0];
//    long time = System.currentTimeMillis();
    Object object = joinPoint.proceed();
//    long resultTime = System.currentTimeMillis() - time;
    LOGGER.info("Processing of file took  milliseconds");
    return object;
  }
}

在SpringAOP中,无法拦截内部方法调用

在共享代码中,即使方法 processFile() 是 public,它也是从 run() 调用的。这是一个自引用/内部方法调用,无法拦截。

详情请见documentation

Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are, by definition, not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted

拦截对实现Runnable的class的所有外部方法调用的切入点表达式如下

@Around("this(java.lang.Runnable) && within(com.epam.esm.processor..*)")
public Object logFileProcessing(ProceedingJoinPoint pjp) throws Throwable {

    try {
        return pjp.proceed();
    } finally {
        //log
        System.out.println("****Logged");
    }
}

范围指示符within()限制应用建议的范围。

切点 @Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))") 是有效的,如果发生外部方法调用,它将起作用。

希望这对您有所帮助。