How #ControlAdvice captures exceptions? - java

I'm on learning phase of Spring boot
I've code where its written like below to handle exception in whole application. Not sure how its working, but I have NoDataFoundException class in code and its being used at place where no data found issues are happening.
#ControlAdvice
class ControllerAdvisor {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(NoDataFoundException.class)
#ResponseBody
public ResponseEntity<Object> handleNodataFoundException(
NoDataFoundException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", "No cities found");
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
Want to know, how and when handleNodataFoundException method automatically gets called when NoDataFoundException instance gets created ?
Does spring calls this method handleNodataFoundException on the basis of #ExceptionHandler(NoDataFoundException.class) which is bind to method itself and moreover irrespective of name of the method ?
how spring looks for parameters required for above method ? what if it has more parameters in it ?

This is done by proxying (Proxy.class).
Proxying is a mechanism in which a kind of pseudo class is created dynamically and mimics your own class (same methods) and is used to intercept all the method calls. This way it can act before and after the method calls as it please, and in the middle call the real method that you developed.
When you create a #Service in Spring, or a #Stateless in EJB, you never actually create the instances, you delegate the instance creation on the framework (Spring or EJB). Those frameworks proxy your classes and intercept every call.
In your case, and putting it simple, the proxy has a catch around the real call to your method, that catch captures exceptions, and acts upon the framework configuration built based on all the annotations that you created (#ControlAdvice, #ExceptionHandler and so on). And so it can call the handleNodataFoundException(...) in the cases that you defined with annotations.
Update visualization via stacktrace
For instance, if you have two Spring #Component (or #Service, #Repository or whatever), and one calls the other one in a plain call, and you get an exception, in the stacktrace you see plenty of method calls (involving all kind of different classes) between your two component classes, all those are proxies and framework classes that take care of proxying, invoking, configuration and all the magic that the framework does. And everything is triggered by the proxy of your second component just before calling the real code that you developed, because the first component, at execution time, doesn't really call an instance of your class, but an instance of the proxy of your class that the framework created.
If you run a plain main, with two classes calling one to the other, instantiated by you with new, you will only see 3 lines in the stacktrace, because there the instances are plain instances created by you.

Related

Run annotation processor before every method invocation in a service

I am building a service with RestAPI's. I want to put a custom annotation as mentioned below.
#CustomAnnnotation
public APIResponse apiMethod(APIRequest request) {
}
Functionality of this custom annotation :
Whenever there is a request to this apiMethod, before the execution of this method , i want to call an API in different server with some of the request parameters from this function. Example mentioned below. Basically for every method invocation i want to call a different server.
Instead of doing this
public APIResponse apiMethod(APIRequest request) {
newServiceClient.newAPI(request.getName())
}
I want to do this functionality by using a custom annotation. I know that i can use interceptors to intercept this request and call the API. Is there any other way ?
Edit :
To summarise this question. There is a method(This might not be API start point. It can also be a normal method in your application) in my java code. Whenever i annotate this method, for every invocation of this method in application, i want do some functionality. I want to have this in annotation because i am thinking of providing a library for this annotation so that any function can be annotated

Can a method annotation handle errors thrown by this method?

I recently started learning annotations and I want to know can a method annotation handle errors thrown by this method? Or to know the code of this exception/error.
P.S. if it can, next step is to retry this method in dependence of error code
P.S.S. I know about spring Retryable, but i can't use it. I tried to find info about my question in the google, but i didn't find.
Annotation on its own does nothing. It is just to mark code. You need to have some handlers, that scan your classes and react in case of annotation.
Most frameworks already have handlers and scanners, so developer include proper framework, add proper annotations, and thanks to that frameworks will perform some work for developer or application.
btw, for error handling, I recommend using a simple proxy like this:
Invoke method in another class's try catch block
You are probably more thinking about Aspect Oriented Programming.
Meaning: java annotations aren't about adding functionality "into" methods. They are markers that are "evaluated" by some sort of component (either the compiler at compile time, or some framework at run time) and trigger activity in that component.
In order to really manipulate the behavior a method (like: add automated tracing/logging code), you need something like AOP. Of course: the whole purpose of compiler-annotations are about generating code based upon the annotation. Project Lombok is a good example for such things: you put annotations into your source code, and the compiled class file contains many things inserted by Lombok during compile.
Actually,One of the basic things in OOP is IoC(Inversion of Control).We need to care this approach when building a professional application.
https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring
For example,
we can write try/catch blocks in each class in the project.this is bad practice.
instead of this way ,we can use #ControllerAdvice annotation.
Just define a particular exception,JVM catch it in all the classes/requests for you.This is IoC.
You can catch exceptions in every request in project,If you define the exception in the Class which you put on the #ControllerAdvice annotation.
Simple Usage Example :
#ControllerAdvice
#RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public final ResponseEntity httpRequestMethodNotSupportedException(Exception ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), "there isn’t an URL like that",
request.getDescription(false));
return new ResponseEntity<>(exceptionResponse, HttpStatus.METHOD_NOT_ALLOWED);
}
Here is the useful link about #ControllerAdvice:
https://medium.com/#jovannypcg/understanding-springs-controlleradvice-cd96a364033f

AspectJ with Spring : intercepted methods lose their parameters annotations

I recently added AOP with aspectJ and spring-aop to my existent spring project. The goal was to actually intercept controller calls to modify the response they send back, in order to bind some values to this response I didn't want to add manually to each and everyone of my controllers, for example the expiration date of the actual token used by the end-user (which I wasn't even able to showcase within my controller in any case). I actually managed to get it working until I started my unit tests :
In my unit tests I call directly my controller methods using Reflection feature from java, then replicate usual process (calling the filter chain, pre handler and post handlers, and the controller method itself which is first manually validated using spring validator when annotation #Valid is present on one of my parameters. All this process works fine and gets executed properly). The problem is that now that the controller method is intercepted by spring-aop, it's mentionned as coming from the proxy controller created, and all of my parameters annotations disapear. Here is a controller example :
#Override
public ResponseEntity<Object> editPassword(#Valid #RequestBody PasswordEditForm passwordEditForm, HttpServletRequest request) {
return factorizedUserBaseController.editPassword(passwordEditForm, request, User.class);
}
the parameter PasswordEditForm has the annotation #Valid so in my test cases it was first validated before any other step, but now as I double checked it, the #Valid annotation is not present on the proxy method, and therefore the parameter doesn't get validated, any clue for how to fix this and make my parameters annotation still understandable from my test point of view?
Note : when running the spring through mvn spring-boot:run, parameters with #Valid annotation gets correctly validated and then goes to my error handler method properly.
Problem Solved : from several other stackoverflow posts I understand that CGLIB (aop proxy lib used by Spring) doesn't support annotations. ( see Retain annotations on CGLIB proxies?). But my problem wasn't here, I was literally sure I was finding the method using the controller class itself (the one I coded) but what I was wrong about is that I was giving the controller instance as a parameter to some other parts of my code which in turn would use this controller class to find the method which of course wasn't working because thanks to Spring proxies, it wasn't anymore my controller itself but a proxy class extending my own controller class. Instead, I just had to replace :
Class<?> controllerClass = controllerInstanciationContainer
.getController()
.getClass();
with
Class<?> controllerClass = controllerInstanciationContainer
.getController()
.getClass()
.getSuperclass();

Does Spring generate code for validaiton?

lately I was trying to understand how spring processes #Valid annotation. For example look at the following controller's method:
#RequestMapping(value = "/create", method = RequestMethod.POST)
public ModelAndView createEmployee(#Valid EmployeeForm form, Errors errors) {
if(errors.hasErrors()) {
//validation errors
}
//method code
}
I am struggling to understand how errors instance is getting populated with validation errors in real-time. Does Spring, during compilation of the controller, inject code responsible for validation at the beginning of the createEmployee method? If so how this code would look?
I really tried to find an example of how this validation is performed in real life but it's just impossible. Help me please.
Everything happens at runtime. See the reference for more details on doing validation or this post for extra explanations.
Basically this is part of how Spring works internally. When you start your application Spring registers some beans, bean processors, can scan your classpath for annotated classes, registers those found annotated classes, builds proxies for some of them etc and uses all of them to build a context.
When handling a request, the request is handled on some predetermined execution path that starts with the DispatcherServlet, picking up other beans from the context as needed to handle the request (like validation for example) then forwarding to you controller in the createEmployee (which was registered as startup because Spring found your #RequestMapping annotations on your controller). When you return from the method the flow continues by building a model, selecting a view to display and then generating the response to the client.
For your example, Spring basically finds the #Valid annotation, looks for an already configured validator (configured by you or by a provided implementation for e.g. JSR-303), runs the validator and stores the validation result inside the Errors object. It does this when the request is processed, as mentioned above, it does not generate code.
If your question is to know exactly how Spring does this, in all it's details, you could take the Spring source code and have a look/debug it.

How to set objects on Spring 3.1 MVC controller from an interceptor?

I'm having a couple of issues related to interceptors since upgrading to 3.1. In version 3.0.x and earlier I used the following pattern to intercept Spring MVC controllers:
Create an interface called something like RoleAware which defines one or more setters.
Have one or more controllers implement the interface
Register a new global interceptor which does a "handler instanceof RoleAware" check in the preHandle
If the interceptor is an instanceof RoleAware, then set one or more objects on the implementing controller
The first issue is that something changed in 3.1 so the instanceof check fails. I've fixed this by using the new explicit elements in my servlet context configuration. Not a big deal and a bit cleaner approach than the instanceof check.
The second issue is that when I attempt to cast the handler (Controller) to my RoleAware interface I get a ClassCastException.
I'd like a solution that will enable me to continue to arbitrarily set objects on the intercepted controllers. For example, set a Role object on any Controllers that are intercepted. Also, I'd like to know more about the changes in 3.1 that is causing this to break.
The reference doc says:
When using the RequestMappingHandlerMapping the actual handler is an
instance of HandlerMethod which identifies the specific controller
method that will be invoked.
So I guess that you should just cast the handler to HandlerMethod, call its getBean() method, and check is the returned bean is an instance of RoleAware.
Not tested though. You could try using a debugger to inspect the handler argument and see what it is if it is neither the handler itself, nor the HandlerMethod.

Categories