Я работаю над проектом A, где добавляю аспекты вокруг AdminController для проверки этих URL-адресов при вызове, но у меня есть все пользовательские аннотации в отдельном репозитории B, который я добавил в качестве внешнего jar в A в качестве формы зависимости.
Ниже приведены файлы проекта B, в которых я определил собственные аннотации.
Проект Б
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAuth {
}
@Component
@Aspect
public class AuthAspect {
@Before(value = "@annotation(com.company.services.annotations.CustomAuth)")
public void doBefore(final JoinPoint joinPoint) {
System.out.println("Validating the admin url");
}
@Around(value = "@annotation(com.company.services.annotations.CustomAuth)")
public Object doAuthorize(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("During this");
long start = System.currentTimeMillis();
Object proceed = proceedingJoinPoint.proceed(); // Continue with the method execution
long executionTime = System.currentTimeMillis() - start;
System.out.println(proceedingJoinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
pom.xml из B
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Ниже приведен набор файлов, которые у меня есть в проекте A, где находится AdminController.
Админконтроллер.java
@RestController
@RequestMapping("/{version}/admin")
public interface AdminController {
@CustomAuth
@GetMapping(value = "/node/status",
produces = {MediaType.APPLICATION_JSON})
void getStatus();
}
pom.xml от A (наследовать зависимость от B)
<dependencies>
<dependency>
<groupId>com.company.service</groupId>
<artifactId>filter</artifactId>
<version>0.0.18</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</exclusion>
<exclusion>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>com.company.service</groupId>
<artifactId>filter</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
</plugin>
<plugins>
Основной файл SpringBoot
@Configuration
@SpringBootApplication
@EnableAspectJAutoProxy
@ComponentScan(value = {"com.company"})
@EntityScan({"com.company"})
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
}
В этой конфигурации я не могу выполнять переплетение времени загрузки для аспекта пользовательской аннотации. Есть идеи, почему это не работает или чего мне здесь не хватает?
@M.Deinum Я не могу аспектировать свою пользовательскую аннотацию на контроллере. Это основная проблема. Это из-за переплетения времени загрузки, как я видел во многих других местах, где его использование не является стандартной практикой.
Вы не используете плетение во время загрузки, так почему вы так думаете? Проблема здесь в том, что ваш метод не является общедоступным.
@M.Deinum Для интерфейса все методы по умолчанию являются общедоступными. Я тоже пытался сделать то же самое, но безуспешно. Это похоже на то, что динамический прокси-сервер JDK не может вызвать этот аспект.
Аннотации не наследуются, они должны быть в фактической реализации.
@M.Deinum Я считаю, что это все еще возможно, поскольку АОП ставит условие для этой аннотации. Если это какой-либо метод, абстрактный или неабстрактный, мы можем придать ему аспект. Но спасибо за ваши комментарии. Все еще похоже на то, что мне не хватает зависимости или ее отладки.
@M.Deinum Spring AOP использует либо динамические прокси JDK, либо CGLIB для создания прокси для ваших целевых объектов. Согласно документации Spring, если ваша цель реализует хотя бы один интерфейс, будет использоваться динамический прокси-сервер JDK. Однако, если ваш целевой объект не реализует никаких интерфейсов, будет создан прокси-сервер CGLIB. Здесь он создаст прокси-сервер JDK, который реализует AdminController, а затем проксирует все вызовы к вашему bean-компоненту AdminServiceImpl, добавляя функциональные возможности аспекта, где это необходимо.
Это не имеет ничего общего с интерфейсами, а связано с видимостью аннотаций. Пожалуйста, не пытайтесь объяснить, как работает Spring AOP (я знаю). Проблема всё равно в том, что дело не в классе, но удачи в начинаниях, если не верите на слово специалистам...
@M.Deinum Извиняюсь, если это звучит так. Кстати, это работает. Спасибо за ваш совет. Интерфейс - это проблема, с которой я столкнулся. И только сейчас понял, что смешивал две вещи. Изначально и в первую очередь я хотел использовать АОП, а не аспектJ. Если мы хотим применить его к интерфейсам, я думаю, нам нужно использовать его через интерфейс маркера.
Если не использовать прокси, это может работать с переплетением времени компиляции (и/или времени загрузки, но в моем опыте это неприятно). AspectJ для АОП — это просто язык, поэтому у вас все еще есть AspectJ, но только в виде определений. Необходимость размещения аннотаций к реализации является ограничением АОП на основе прокси. Сам Spring работает с этим для своих собственных аннотаций, таких как @Transactional
и т. д., но это встроено в перехватчики.
В поддержку того, что уже сказал @M.Deinum, я хочу упомянуть, что ваш подход «много (медицина) очень помогает» сочетает в себе не только 2, но и 3 типа АОП: Spring AOP, собственное плетение во время компиляции AspectJ (через AspectJ Maven) и встроенное переплетение времени загрузки AspectJ (которое, как вы сказали, вы хотите использовать). В каждом из них есть способы заставить это работать. Просто примите решение, каким оно должно быть, и тогда, возможно, кто-нибудь поможет вам решить вашу проблему.
@M.Deinum Пожалуйста, проверьте эту ссылку, чтобы увидеть пользовательские аннотации, примененные к интерфейсу.
Что явно устанавливает inherited
в true
, что означает, что он будет сканировать иерархию. Что невозможно при использовании простых @Aspect
и @POintcut
. Добиться этого можно только без использования аннотаций. Теряется преимущество обычного способа предоставления аспектов. Spring внутри использует тот же обходной путь.
@M.Deinum Спасибо за ваши идеи. Без использования унаследованной аннотации. Есть ли способ применить пользовательскую аннотацию ко всем методам класса? Я хотел добавить рекомендации по аспектам ко всем методам класса обслуживания администратора. Если я добавляю пользовательскую аннотацию на уровне класса, она не применяется ко всем его методам. Но индивидуальное применение метода работает абсолютно нормально.
Добавление его в класс должно помочь, однако оно будет применяться только к методам этого класса, а не к методам расширенных классов.
Но это другой вопрос.
@M.Deinum Если мы будем использовать AspectJ вместо Spring AOP, это может сработать. AspectJ не использует прокси, он быстрее, мощнее и прекрасно интегрируется со Spring.
Это применимо только в том случае, если вы используете полноценный аспектj, означающий время загрузки или переплетение времени компиляции. Если вы используете его так же, как здесь, то он все еще использует прокси. Поскольку используется только язык (выражения pointcut)
@M.Deinum Большое спасибо за понимание этого вопроса, я добавил ответ на этот вопрос согласно вашему предложению.
Проблема здесь не в AspectJ или AOP, а в JVM. В Java аннотации к
никогда не наследуются
Наследование аннотаций работает только от классов к подклассам, но только если тип аннотации, используемый в суперклассе, содержит метааннотацию @Inherited.
Вы ничего не добавили для переплетения во время загрузки, так почему это должно работать? Вы не добавили
@EnableLoadTimeWeaving
, чтобы включить его, и не добавили специализированный Java-агент. ВЫ включили прокси, которые не будут работать, поскольку метод не является общедоступным. Кроме того, вашSpringBootApplication
должен иметь только аннотацию@SpringBootApplication
(при условии, что она находится в пакетеcom.company
), все остальное не требуется.