Can you change a method's signature in Spring using aspects?
Like effectively transform the following:
@GetMapping("/thing")
@User // custom annotation that should authenticate the user
public ResponseEntity getThing() {
... // user is successfully authenticated, get the "thing" from the database
}
into:
@GetMapping("/thing")
public ResponseEntity getThing(@CookieValue("Session-Token") String sessionToken) {
User user = authenticator.authenticateSessionTokenOrThrow(sessionToken);
... // user is successfully authenticated, get the "thing" from the database
}
With the user
variable also becoming available for use in the method body.
If not, how can I achieve the same result without repeating the code (parameter and authenticator call) everywhere?
Aspects aren't meant for that.
Yes, they can effectively modify .class
files bytecode, with compile time or run time weaving, but they do not override methods' signatures.
Also, the default Spring AOP Aspects are implemented in pure Java, and thus cannot touch the bytecode layer. For that you'd need AspectJ.
Tools for customizing bytecode at run/compile time are ASM, ByteBuddy, CGLIB or Javassist.
However, you can probably accomplish this via an Annotation Processor, which lets you modify the actual sources, instead of the already compiled bytecode.
If not, how can I achieve the same result without repeating the code (parameter and authenticator call) everywhere?
Possible solutions are
Exception
if the user isn't authenticatedException
if the user isn't authenticated1 is pretty easy.
2 is more time-consuming
3 imho, seems the best match for authentication, but it's the most complex, probably
The HandlerInterceptor can choose which methods it applies to?
No, unfortunately. I had a requirement a couple of months ago to "cover" only certain methods with an Interceptor
, and I implemented a custom solution, which simply look for an annotation specified on the method itself.
This is an extract of my custom HandlerInterceptor
, which looks for the CheckInit
annotation, first on the type, and then on the method, for a more specific customization.
@Override
public boolean preHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final Object handler
) throws Exception {
if (handler instanceof HandlerMethod) {
if (shouldCheckInit((HandlerMethod) handler)) {
checkInit();
}
}
return true;
}
private static boolean shouldCheckInit(final HandlerMethod handlerMethod) {
final var typeAnnotation = handlerMethod.getBeanType().getAnnotation(CheckInit.class);
final var shouldCheckInit = typeAnnotation != null && typeAnnotation.value();
final var methodAnnotation = handlerMethod.getMethodAnnotation(CheckInit.class);
return (methodAnnotation == null || methodAnnotation.value()) && shouldCheckInit;
}
private void checkInit() throws Exception {
if (!manager.isActive()) {
throw new NotInitializedException();
}
}
The "Standard Spring AOP advice" seems interesting, do you have a link for that?
Spring AOP documentation - look for the Java-based configuration (I hate XML)
AspectJ really touches the bytecode and can modify signatures as well?
You could make AspectJ modify signatures. Just fork the project and modify its Java Agent or compiler.
AFAIK Annotation Processors cannot modify classes, they can only create new ones.
The thing is, they don't modify .class
files, instead they modify source files, which means they simply edit them. E.g. Lombok uses annotation processing to modify source files.
But yes, the modified sources are written to a new file.