Spring AOP протоколирование потоков - PullRequest
0 голосов
/ 01 мая 2020

Есть ли способ реализовать ведение журнала AOP для публикации c метода класса, который implements Runnable и выполняется ExecutorService?

Класс потока

@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

@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;
  }
}

1 Ответ

1 голос
/ 02 мая 2020

В Spring AOP внутренние вызовы методов не могут быть перехвачены.

В общем коде, даже если метод processFile() опубликован c, он вызывается из run(). Это внутренний вызов метода / внутренний метод, который не может быть перехвачен.

Подробности можно прочитать в документации

Из-за природы на основе прокси в среде Spring AOP, вызовы внутри целевого объекта по определению не перехватываются. Для JDK-прокси могут быть перехвачены только вызовы метода интерфейса publi c на прокси

Выражение pointcut для перехвата всех вызовов внешнего метода в класс, реализующий Runnable, будет выглядеть следующим образом

@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))") действителен и будет работать, если с ним произойдет внешний вызов метода.

Надеюсь, это поможет.

...