Here is s stand-alone AspectJ example (not a Spring application, but aspect syntax should be the same).
Driver application:
As you can see, there are several methods with and without @ModelAttribute
parameter annotations, one of them even with two annotated parameters (whether it makes sense or not, but it is just an example).
package de.scrum_master.app;
import java.util.Collection;
import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
public class Application {
public String list(Model model, @ModelAttribute("key") boolean key) {
return "x";
}
public String foo(Model model, @ModelAttribute("key") boolean key, @ModelAttribute(name = "key") String text) {
return "x";
}
public String bar(@ModelAttribute(name = "key") int number) {
return "x";
}
public String zot(Model model, @ModelAttribute("XXX") boolean key) {
return "x";
}
public String baz(Model model, boolean key, String text) {
return "x";
}
public String bla(@ModelAttribute("XXX") int number) {
return "x";
}
public static void main(String[] args) {
Model model = new Model() {
@Override public Model mergeAttributes(Map<String, ?> arg0) { return null; }
@Override public boolean containsAttribute(String arg0) { return false; }
@Override public Map<String, Object> asMap() { return null; }
@Override public Model addAttribute(String arg0, Object arg1) { return null; }
@Override public Model addAttribute(Object arg0) { return null; }
@Override public Model addAllAttributes(Map<String, ?> arg0) { return null; }
@Override public Model addAllAttributes(Collection<?> arg0) { return null; }
};
Application application = new Application();
application.list(model, true);
application.foo(model, true, "hey");
application.bar(11);
application.zot(model, true);
application.baz(model, true, "hey");
application.bla(11);
}
}
Aspect:
The aspect matches all method executions having at least one parameter with a @ModelAttribute
annotation. The pointcut would be enough if you only want to finde those methods, regardless of annotation parameter values. But as you said, you only want to match the ones which have value = "key"
, we need to use reflection in order to look into the annotations themselves and filter out the unwanted ones.
Another complication that according to @ModelAttribute
JavaDoc, parameters name
and value
are aliases for each other, i.e. we also need to check both parameters' values in order to get it completely right.
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ModelAttribute;
@Aspect
@Component
public class ModelAttributeInterceptor {
@Pointcut("execution(public * *(.., @org.springframework.web.bind.annotation.ModelAttribute (*), ..))")
public void methodWithAnnotatedParameter() {}
@Around("methodWithAnnotatedParameter()")
public String blahMethod(ProceedingJoinPoint thisJoinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
boolean foundModelAttribute = false;
for (Annotation[] annotations : annotationMatrix) {
for (Annotation annotation : annotations) {
if (!(annotation instanceof ModelAttribute))
continue;
ModelAttribute modelAttribute = (ModelAttribute) annotation;
if ("key".equals(modelAttribute.value()) || "key".equals(modelAttribute.name())) {
if (!foundModelAttribute) {
System.out.println(thisJoinPoint);
foundModelAttribute = true;
}
System.out.println(" " + modelAttribute);
}
}
}
return (String) thisJoinPoint.proceed();
}
}
Console log:
execution(String de.scrum_master.app.Application.list(Model, boolean))
@org.springframework.web.bind.annotation.ModelAttribute(name=, value=key, binding=true)
execution(String de.scrum_master.app.Application.foo(Model, boolean, String))
@org.springframework.web.bind.annotation.ModelAttribute(name=, value=key, binding=true)
@org.springframework.web.bind.annotation.ModelAttribute(name=key, value=, binding=true)
execution(String de.scrum_master.app.Application.bar(int))
@org.springframework.web.bind.annotation.ModelAttribute(name=key, value=, binding=true)