I need to intercept all the methods of type writer.write(myObj) , and if and only if myObj contains a method annotated with #BeforeWrite, execute this method.
I am kind of confused because I can intercept a method call on the writer, but I don't know how to provide a pointcut that looks if there is a method annotated with #BeforeWriter, this looks similar to how probably a #PostLoad annotation is handled in JPA...
In your interceptor handling method you have the argument of type ProceedingJoinPoint, which has method getArgs(). You can check your argument (myObj) methods reflections and make your decision whether to proceed. Example (call is of type ProceedingJoinPoint):
boolean proceed = false;
for (Method method : call.getArgs()[0].getClass().getMethods()) {
if (method.isAnnotationPresent(BeforeWriter.class)) {
proceed = true;
break;
}
}
Related
I have the following code:
class OrderController {
#AllowedScopes({ORDER_CREATE})
#PostMapping("/create")
public CreateOrderResponse createOrder(#Valid #RequestBody OrderRequest request){
}
}
#Aspect
#Component
public class AllowedScopeAspect {
#Pointcut("#annotation(allowedScopes)")
private void callAtAllowedScopes(AllowedScopes allowedScopes) {
// just a pointcut signature
}
#Before(value = "callAtAllowedScopes(allowedScopes)", argNames = "jp,allowedScopes")
public void validateScope(JoinPoint jp, AllowedScopes allowedScopes) {
...
}
}
Aspect code validates if user have required scope.
The problem is Aspect code is executed after request body validation. If validation is not OKAY, it is returning validation error. if passes, returning 403 error.
How can I execute aspect code before data binding and validation or control handler stage?
You seem to misunderstand how the JVM works. Method parameters always need to be evaluated before calling the method, otherwise the JVM cannot put the parameters on the stack for the method to get access to them. Therefore, also validation takes place before calling the method.
Spring AOP can only intercept method execution, i.e. an aspect is necessarily triggered after validation. The whole point of parameter validation is to not execute the corresponding method, if any parameter is invalid. But if the method is not executed in the first place, there is nothing to intercept for the aspect. 😉
I have a function under class MyController:
#RestController
#RequestMapping(value = "/api/service")
public class MyController {
#PostMapping(value = "add_person")
public MyResponse addPerson(#RequestBody Person person) {
// ...
}
#PostMapping(value = "add_person_2")
public MyResponse addPerson(#PathVariable(value = "person_age") Int age, #RequestBody Person person) {
// ...
}
}
I have setup AspectJ in my project to have a AOP logic to run whenever those two addPerson(...) method above is called:
#Around("execution(public MyResponse addPerson(..))")
public void around(ProceedingJoinPoint joinPoint) {
// NO matter which addPerson(...) is executing, I am only interested in the
// parameter value annotated with #RequestBody.
// How can I access the parameter that passed in addPerson(...) & is annotated with
// #RequestBody through ProceedingJoinPoint ?
}
My question is mentioned in above code comment. I wonder how can I access the parameter annotated with #RequestBody in my AOP function? I don't want to check parameter type or name, but interested to know how to access parameter by checking the annotation through ProceedingJoinPoint. Is it possible?
I do not want to mark this question as a duplicate because it is no exact duplicate, but my answer here should answer the question about how to
match an annotated parameter at any position,
get the annotation + the parameter value itself.
The linked answer uses a #Before advice. If you want to somehow replace the value by another one in an #Around advice when calling proceed() this is also possible, but was not asked here and my request for seeing more of the advice method body was also ignored.
If you want to limit to annotated Person parameters, you would have to use the fully qualified class name my.package.Person instead of the * inside (*) and do the corresponding cast after accessing the parameter in the advice body.
In my comment I also asked if the parameter has a fixed relative position in the parameter list such as first, last or second/third from left/right. If the OP would have confirmed such a fixed relative position, reflection would not be necessary and the corresponding parameter could be bound to an advice method parameter directly via args() pointcut designator. This would be quite elegant and eliminate the need to loop over getArgs() or over a two-dimensional array of parameter annotations.
Is there a way to specify a custom Getter method in SpringMVC binding, rather than the default PropertyDescriptors?
I feel like I'm venturing into outer space with this question, sounds so simple and yet no Google results. I'm doing this because my model has a Boolean has a getter named isSomething() and SpringMVC will only check that for primitive booleans. For the class Boolean it'll complain that a getter is not found, it requires getSomething().
I should be able to just specify custom getters/setters if I need those, isn't that the case?
Since I know people will suggest custom Property Editors etc., I put a breakpoint in these methods, and it doesn't even get here -- the problem happens somewhere up the chain, with PropertyDescriptors:
binder.registerCustomEditor(Boolean.class, new PropertyEditorSupport() {
#Override
public void setValue(Object value) {
// TODO Auto-generated method stub
super.setValue(value);
}
#Override
public Object getValue() {
// TODO Auto-generated method stub
return super.getValue();
}
});
I wanted to play with these custom editors, but the code never gets here. The error is thrown before that.
Invalid property 'test' of bean class [Model]: Bean property 'test' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
How can I achieve this fairly common scenario of custom getters/setters?
I know one can use interceptors before a method call by using the #AroundInvoke annotation.
What I would like to do is execute certain code after the method call, so that I can for example create a log entry before and after a method execution.
Is this possible with EJB3, or do I need to use AOP?
#AroundInvoke interceptor is passed InvocationContext, and proceed() must be called to advance the method. Thus:
#AroundInvoke
public Object log(InvocationContext ic) throws Exception {
logEntry();
try {
return ic.proceed();
} finally {
logExit();
}
}
Depending on your needs, you could also log the return value or exceptions, filter the methods being logged, etc.
I started with an original question on
Need help creating a specific pointcut that utilizes a value from a method annotation
I decided I wanted to ask another question to change the approach I was taking.
I have a method (navigation), that has a call inside of that method to another method which I would like to have #Around advice.
#RequestMapping(method = RequestMethod.GET)
public String navigation(ModelMap model) {
...
// Call Auto Handling
logger.info("Call AutoHandling");
this.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
}
...
return forward(returnView);
}
Is this possible as I cannot seem to get this to work if the method is inside of the same class.
This work if it was not on the object itself:
#Around("execution(* *.processAutoHandling(..)) &&" +
"args(callSession, functionalArea) && " +
"args(functionalArea) && " +
"target(bean)"
)
public Object processAutoHandlingCall2(ProceedingJoinPoint jp,
CallSession callSession,
FunctionalArea functionalArea,
Object bean)
throws Throwable {
logger.debug("processAutoHandleCall");
return jp.proceed();
}
With this call in my controller:
autoHandlingComponent.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
instead of
this.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
It seems that you are using Spring's proxy-based AOP. If so, this is a known limitation. See Understanding AOP Proxies from Spring documentation for more details. You have two ways to solve this issue:
Use the AopContext.currentProxy() approach outlined in the documentation. I will discourage this approach, since your code will now be tied to Spring AOP quite explicitly.
Use AspectJ's byte-code weaving. Since there is no proxy involved with it, you won't have the issue with 'this' pointing to original object and proxy is transparently available only to external objects.