Я бы дал тот же ответ, что и Нандор при обычных обстоятельствах.Привязки AspectJ к параметрам из разных ветвей ||
неоднозначны, поскольку обе ветви могут совпадать, так что это не разрешено.
Что касается @RequestMapping
, все остальные аннотации @*Mapping
являются синтаксическим сахароми задокументировано, что составные аннотации действуют как ярлыки, см., например, @GetMapping
:
В частности, @GetMapping
является составной аннотацией, которая действует как ярлык для @RequestMapping(method = RequestMethod.GET)
.
Т.е. сам тип GetMapping
аннотируется @RequestMapping(method = RequestMethod.GET)
.То же самое относится и к другим составным аннотациям (синтаксический сахар).Мы можем использовать это обстоятельство для нашего аспекта.
AspectJ имеет синтаксис для поиска аннотированной аннотации (также вложенной), см., Например, мой ответ здесь .В этом случае мы можем использовать этот синтаксис для общего соответствия всех аннотаций, аннотированных @RequestMapping
.
. Это все еще оставляет нам два случая, то есть прямую аннотацию и синтаксическую сахарную аннотацию, но это немного упрощает кодтем не мение.Я придумал этот пример приложения на чистом Java + AspectJ, импортировал только JAR spring-web , чтобы иметь доступ к аннотациям.В противном случае я не использую Spring, но pointcut и рекомендации будут выглядеть одинаково в Spring AOP, вы даже можете исключить часть && execution(* *(..))
из первого pointcut, потому что Spring AOP в любом случае не знает ничего, кроме pointcut выполнения (но AspectJ делает и будеттакже соответствует call()
, например).
Приложение драйвера:
package de.scrum_master.app;
import org.springframework.web.bind.annotation.*;
import static org.springframework.web.bind.annotation.RequestMethod.*;
public class Application {
@GetMapping public void get() {}
@PostMapping public void post() {}
@RequestMapping(method = HEAD) public void head() {}
@RequestMapping(method = OPTIONS) public void options() {}
@PutMapping public void put() {}
@PatchMapping public void patch() {}
@DeleteMapping @Deprecated public void delete() {}
@RequestMapping(method = TRACE) public void trace() {}
@RequestMapping(method = { GET, POST, HEAD}) public void mixed() {}
public static void main(String[] args) {
Application application = new Application();
application.get();
application.post();
application.head();
application.options();
application.put();
application.patch();
application.delete();
application.trace();
application.mixed();
}
}
Обратите внимание, как я смешал разные типы аннотаций и как я добавил еще одну аннотацию @Deprecated
к одному методу, чтобы иметь отрицательный контрольный пример для аннотации, которая нас не интересует.
Аспект:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Aspect
public class RequestMappingAspect {
@Before("@annotation(requestMapping) && execution(* *(..))")
public void genericMapping(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
System.out.println(thisJoinPoint);
for (RequestMethod method : requestMapping.method())
System.out.println(" " + method);
}
@Before("execution(@(@org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void metaMapping(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
for (Annotation annotation : ((MethodSignature) thisJoinPoint.getSignature()).getMethod().getAnnotations()) {
RequestMapping requestMapping = annotation.annotationType().getAnnotation(RequestMapping.class);
if (requestMapping == null)
continue;
for (RequestMethod method : requestMapping.method())
System.out.println(" " + method);
}
}
}
Журнал консоли:
execution(void de.scrum_master.app.Application.get())
GET
execution(void de.scrum_master.app.Application.post())
POST
execution(void de.scrum_master.app.Application.head())
HEAD
execution(void de.scrum_master.app.Application.options())
OPTIONS
execution(void de.scrum_master.app.Application.put())
PUT
execution(void de.scrum_master.app.Application.patch())
PATCH
execution(void de.scrum_master.app.Application.delete())
DELETE
execution(void de.scrum_master.app.Application.trace())
TRACE
execution(void de.scrum_master.app.Application.mixed())
GET
POST
HEAD
Это не идеально в отношении СУХОГО, но мы можем идти только настолько далеко, насколько это возможно.Я все еще думаю, что он компактный, читаемый и обслуживаемый, без необходимости перечисления каждого отдельного типа аннотации для сопоставления.
Что вы думаете?
Обновление:
Если вы хотите получить значения для аннотаций отображения запроса «синтаксический сахар», весь код выглядит следующим образом:
package de.scrum_master.app;
import org.springframework.web.bind.annotation.*;
import static org.springframework.web.bind.annotation.RequestMethod.*;
public class Application {
@GetMapping public void get() {}
@PostMapping(value = "foo") public void post() {}
@RequestMapping(value = {"foo", "bar"}, method = HEAD) public void head() {}
@RequestMapping(value = "foo", method = OPTIONS) public void options() {}
@PutMapping(value = "foo") public void put() {}
@PatchMapping(value = "foo") public void patch() {}
@DeleteMapping(value = {"foo", "bar"}) @Deprecated public void delete() {}
@RequestMapping(value = "foo", method = TRACE) public void trace() {}
@RequestMapping(value = "foo", method = { GET, POST, HEAD}) public void mixed() {}
public static void main(String[] args) {
Application application = new Application();
application.get();
application.post();
application.head();
application.options();
application.put();
application.patch();
application.delete();
application.trace();
application.mixed();
}
}
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Aspect
public class RequestMappingAspect {
@Before("@annotation(requestMapping) && execution(* *(..))")
public void genericMapping(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
System.out.println(thisJoinPoint);
for (String value : requestMapping.value())
System.out.println(" value = " + value);
for (RequestMethod method : requestMapping.method())
System.out.println(" method = " + method);
}
@Before("execution(@(@org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void metaMapping(JoinPoint thisJoinPoint) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
System.out.println(thisJoinPoint);
for (Annotation annotation : ((MethodSignature) thisJoinPoint.getSignature()).getMethod().getAnnotations()) {
RequestMapping requestMapping = annotation.annotationType().getAnnotation(RequestMapping.class);
if (requestMapping == null)
continue;
for (String value : (String[]) annotation.annotationType().getDeclaredMethod("value").invoke(annotation))
System.out.println(" value = " + value);
for (RequestMethod method : requestMapping.method())
System.out.println(" method = " + method);
}
}
}
Журнал консоли будет выглядеть следующим образом:
execution(void de.scrum_master.app.Application.get())
method = GET
execution(void de.scrum_master.app.Application.post())
value = foo
method = POST
execution(void de.scrum_master.app.Application.head())
value = foo
value = bar
method = HEAD
execution(void de.scrum_master.app.Application.options())
value = foo
method = OPTIONS
execution(void de.scrum_master.app.Application.put())
value = foo
method = PUT
execution(void de.scrum_master.app.Application.patch())
value = foo
method = PATCH
execution(void de.scrum_master.app.Application.delete())
value = foo
value = bar
method = DELETE
execution(void de.scrum_master.app.Application.trace())
value = foo
method = TRACE
execution(void de.scrum_master.app.Application.mixed())
value = foo
method = GET
method = POST
method = HEAD
Обновление 2:
Если вы хотите скрыть отражающие элементы, используя AnnotatedElementUtils
и AnnotationAttributes
Spring, как первоначально предложено @ M.Прохоров , вы можете использовать тот факт, что с getMergedAnnotationAttributes
вы действительно можете делать покупки за один раз как для оригинальной аннотации RequestMapping
, так и для синтаксиса, такой как GetMapping
, получая информацию как о методе, так и о значении в одном,объединенный атрибут объекта.Это даже позволяет вам исключить два разных случая получения информации и, таким образом, объединить два совета в один, например, так:
package de.scrum_master.aspect;
import static org.springframework.core.annotation.AnnotatedElementUtils.getMergedAnnotationAttributes;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* See https://stackoverflow.com/a/53892842/1082681
*/
@Aspect
public class RequestMappingAspect {
@Before(
"execution(@org.springframework.web.bind.annotation.RequestMapping * *(..)) ||" +
"execution(@(@org.springframework.web.bind.annotation.RequestMapping *) * *(..))"
)
public void metaMapping(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
AnnotationAttributes annotationAttributes = getMergedAnnotationAttributes(
((MethodSignature) thisJoinPoint.getSignature()).getMethod(),
RequestMapping.class
);
for (String value : (String[]) annotationAttributes.get("value"))
System.out.println(" value = " + value);
for (RequestMethod method : (RequestMethod[]) annotationAttributes.get("method"))
System.out.println(" method = " + method);
}
}
Вот, что у вас есть: СУХОЙ, как вы изначально хотели, довольно читабельный и обслуживаемый.код аспекта и простой доступ ко всей (мета) информации аннотации.