Возможно, вы должны были упомянуть, что ваше приложение даже не запускается, оно завершается с ошибкой.В общем, это в значительной степени беспорядок.Возможно, вам следует проверить это и просмотреть журналы, прежде чем публиковать их на 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) , который вы можете просто принять, чтобы все мои изменения были внесены в ваш проект без копирования и вставки.