AspectJ определяет Pointcut для аннотации с соответствующим значением аргумента - PullRequest
0 голосов
/ 23 сентября 2019

У меня есть процесс, определенный с помощью Process аннотации .Эта аннотация имеет свойство под названием name.Процесс содержит задачи в нем.Задачи определяются с помощью другой аннотации , называемой Task.Эта аннотация имеет свойство processName.У меня есть общий процесс с name как Общий .Задачи для этого процесса: Task1 , Task2 и Task3 , все три с processName как Generic .Могу ли я использовать аспектj таким образом, чтобы все задачи с одинаковым processName были сгруппированы в «Процесс»?Также, когда вызывается GenericProcess.execute , все задачи в нем также должны запускаться.Код, который я сейчас пытаюсь найти, находится в github .Спасибо за помощь.

1 Ответ

0 голосов
/ 25 сентября 2019

Возможно, вы должны были упомянуть, что ваше приложение даже не запускается, оно завершается с ошибкой.В общем, это в значительной степени беспорядок.Возможно, вам следует проверить это и просмотреть журналы, прежде чем публиковать их на GitHub.Вы также должны были опубликовать вывод журнала, показывающий ошибки, вместо того, чтобы просто сказать «это не работает».Мне пришлось исправить многие вещи:

  • Сначала я удалил (или, скорее, переименовал) application.yml и logback.xml , потому что у меня тоже нетваша база данных H2 и ваш проект не создают ее, и я не вижу никаких выводов журнала на консоли.Конечно, для вас эти настройки могут работать, для меня они не работают.

  • Тогда в вашем Application есть Process process = context.getBean(Process.class);, который не имеет смысла, поскольку Process не является компонентомкласс, но аннотация.Вы взяли мой код из другого ответа , многое изменили, а потом уже ничего не подходили друг другу.Теперь ваше приложение выглядит следующим образом:

package com.spring.aspect.interfaces;

import com.spring.aspect.interfaces.process.GenericProcess;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {
  private static final Logger log = LoggerFactory.getLogger(Application.class);

  @Bean
  public ModelMapper modelMapper() {
    return new ModelMapper();
  }

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    GenericProcess process = context.getBean(GenericProcess.class);
    log.info("Generic process = {}", process);
    process.execute();
  }
}
  • В аспектах вашей задачи Task{1,2,3} pointcut execution(public com.spring.aspect.interfaces.entity.Job com.spring.aspect.interfaces.process.GenericProcessImpl.process(..)) && args(context) недействителен, потому что нет ни GenericProcessImpl.process(..)Метод также не имеет аргумента Context.вместо этого у вас есть метод void execute() без аргументов.

  • Кроме того, этот метод только из реализованного интерфейса GenericProcess, то есть мы могли бы использовать этот интерфейс вместо определенногореализация как pointcut.

  • Я также не вижу необходимости использовать дорогой @Around совет, если вы просто хотите что-то зарегистрировать, @Before будет достаточно.Так как насчет этого?

package com.spring.aspect.interfaces.task;

import com.spring.aspect.interfaces.annotation.Provided;
import com.spring.aspect.interfaces.annotation.Required;
import com.spring.aspect.interfaces.annotation.Task;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Task(name = "Task1", processName = "Generic")
@Required(values = { "param1" })
@Provided(values = { "param2" })
@Aspect
public class Task1 {
  Logger logger = LoggerFactory.getLogger(Task1.class.getName());

  @Before("execution(public void com.spring.aspect.interfaces.process.GenericProcess+.execute())")
  public void task(JoinPoint thisJoinPoint) {
    logger.info("{}", thisJoinPoint);
  }
}
package com.spring.aspect.interfaces.task;

import com.spring.aspect.interfaces.annotation.Provided;
import com.spring.aspect.interfaces.annotation.Required;
import com.spring.aspect.interfaces.annotation.Task;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Task(name = "Task2", processName = "Generic")
@Required(values = { "param2" })
@Provided(values = { "param3" })
@Aspect
public class Task2 {
  Logger logger = LoggerFactory.getLogger(Task2.class.getName());

  @Before("execution(public void com.spring.aspect.interfaces.process.GenericProcess+.execute())")
  public void task(JoinPoint thisJoinPoint) {
    logger.info("{}", thisJoinPoint);
  }
}
package com.spring.aspect.interfaces.task;

import com.spring.aspect.interfaces.annotation.Required;
import com.spring.aspect.interfaces.annotation.Task;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Task(name = "Task3", processName = "Generic")
@Required(values = { "param1", "param2" })
@Aspect
public class Task3 {
  Logger logger = LoggerFactory.getLogger(Task3.class.getName());

  @Before("execution(public void com.spring.aspect.interfaces.process.GenericProcess+.execute())")
  public void task(JoinPoint thisJoinPoint) {
    logger.info("{}", thisJoinPoint);
  }
}
  • Последнее, но не менее важное: в TaskAspect вы используете @annotation(com.spring.aspect.interfaces.annotation.Task), что соответствует методамс аннотацией @Task.Но у вас есть классы с этой аннотацией, поэтому вы должны использовать @within(com.spring.aspect.interfaces.annotation.Task).

  • Даже проще, чем пытаться связать аннотацию Task с аргументом через target(task), было бывместо этого просто используйте @within(task), получая одновременно и аннотацию, и привязку параметра.

  • Помните, мы изменили контрольные точки задачи с @Around на @Before выше?Для @Before AspectJ не генерирует методы, а вплетает код непосредственно в целевые классы.Вы также не можете рассчитывать на то, что это будет сделано для @Around советов, поэтому мой ответ на ваш предыдущий вопрос работал, но немного нечист.Тем не менее, есть специальное обозначение точки нарезки adviceexecution().Его цель - перехватить выполнение советов из других аспектов (или даже из того же).Это чище и более универсально.В этом случае это даже единственное, что работает.

  • Наконец, вы используете args(proceedingJoinPoint), я понятия не имею, почему.Вы пытались связать pointcut из перехваченных Task{1,2,3} советов с TaskAspect советом?Это не сработает, потому что AspectJ связывает точку соединения исполняемого совета с существующим первым параметром типа JoinPoint или ProceedingJoinPoint.Может быть, это будет работать, связывая его со вторым параметром точки соединения.В любом случае, нам это не нужно, поэтому давайте просто удалим это.Это действительно надумано, вау.

  • Еще одна вещь, почему вы используете task.getClass().getName() вместо недавно введенных свойств аннотации @Task для регистрации информации?

package com.spring.aspect.interfaces.aspect;

import com.spring.aspect.interfaces.annotation.Task;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class TaskAspect {
  static Logger logger = LoggerFactory.getLogger(TaskAspect.class.getName());

  @Around("@within(task) && adviceexecution()")
  public Object groupTasks(ProceedingJoinPoint proceedingJoinPoint, Task task) throws Throwable {
    logger.info("Generalizing the task aspects");
    logger.info("  {}", proceedingJoinPoint);
    logger.info("  Task = {}", task.name());
    logger.info("  Process = {}", task.processName());
    return proceedingJoinPoint.proceed();
  }
}

Теперь, наконец, приложение и аспекты снова работают, по крайней мере, если вы запустите приложение с параметром -javaagent:"/path/to/aspectjweaver.jar".Теперь вы должны увидеть вывод журнала таким образом (удаляя вывод AspectJ Weaver и некоторые ведущие столбцы регистратора):

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.3.RELEASE)

(...)
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
c.spring.aspect.interfaces.Application   : Started Application in 2.948 seconds (JVM running for 4.934)
c.spring.aspect.interfaces.Application   : Generic process = com.spring.aspect.interfaces.process.GenericProcessImpl@25d0cb3a
c.s.aspect.interfaces.aspect.TaskAspect  : Generalizing the task aspects
c.s.aspect.interfaces.aspect.TaskAspect  :   adviceexecution(void com.spring.aspect.interfaces.task.Task3.task(JoinPoint))
c.s.aspect.interfaces.aspect.TaskAspect  :   Task = Task3
c.s.aspect.interfaces.aspect.TaskAspect  :   Process = Generic
com.spring.aspect.interfaces.task.Task3  : execution(void com.spring.aspect.interfaces.process.GenericProcessImpl.execute())
c.s.aspect.interfaces.aspect.TaskAspect  : Generalizing the task aspects
c.s.aspect.interfaces.aspect.TaskAspect  :   adviceexecution(void com.spring.aspect.interfaces.task.Task1.task(JoinPoint))
c.s.aspect.interfaces.aspect.TaskAspect  :   Task = Task1
c.s.aspect.interfaces.aspect.TaskAspect  :   Process = Generic
com.spring.aspect.interfaces.task.Task1  : execution(void com.spring.aspect.interfaces.process.GenericProcessImpl.execute())
c.s.aspect.interfaces.aspect.TaskAspect  : Generalizing the task aspects
c.s.aspect.interfaces.aspect.TaskAspect  :   adviceexecution(void com.spring.aspect.interfaces.task.Task2.task(JoinPoint))
c.s.aspect.interfaces.aspect.TaskAspect  :   Task = Task2
c.s.aspect.interfaces.aspect.TaskAspect  :   Process = Generic
com.spring.aspect.interfaces.task.Task2  : execution(void com.spring.aspect.interfaces.process.GenericProcessImpl.execute())
c.s.a.i.process.GenericProcessImpl       : Generic Process execution is invoked

Обновление:

  • IЯ немного изменил TaskAspect в отношении вывода журнала.
  • Я также сделал область видимости логгера, чтобы я мог ввести макет из теста.
  • Впоследствии я рефакторинг вашего ProcessTestв мой TaskAspectTest и переместил его в тот же пакет, что и TaskAspect.Это выглядит следующим образом:
package com.spring.aspect.interfaces.aspect;

import com.spring.aspect.interfaces.process.GenericProcess;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * Attention: Run this test with JVM parameter -javaagent:/path/to/aspectjweaver.jar
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class TaskAspectTest {
  @Autowired
  private GenericProcess genericProcess;

  @Mock
  private Logger mockLogger;
  private Logger originalLogger;

  @Before
  public void setup() {
    originalLogger = TaskAspect.logger;
    TaskAspect.logger = mockLogger;
  }

  @After
  public void cleanup() {
    TaskAspect.logger = originalLogger;
  }

  /**
   * The way TaskAspect is currently implemented, its only side effect is logging output,
   * so the only way we can check if the aspect is executed as expected is to inject a
   * mock logger and verify if it was called as often as expected, i.e. once for each
   * Task1, Task2, Task3, with 1+3 log lines per execution.
   */
  @Test
  public void testAspectExecution() {
    genericProcess.execute();
    verify(mockLogger, times(3)).info(anyString());
    verify(mockLogger, times(9)).info(anyString(), any(Object.class));
  }
}

Обновление 2:

  • Я также добавил конфигурацию AspectJ LTW в вашу сборку Maven сейчас,см эти коммиты в моей вилке .
  • Я создал для вас запрос pull (PR) , который вы можете просто принять, чтобы все мои изменения были внесены в ваш проект без копирования и вставки.
...