Aop Annotation at Spring Controllers Doesn't Work - java

I have made an annotation for aop. When I use it at any method rather than controller methods it works well. However when I use it at my controller's methods my controller stops working. It starts to give 404 not found error for mappings. I found a similar question here: Spring 3 MVC #Controller with AOP interceptors? but I don' know how to do it. My method at my controller is that:
#WebAuditable // This is my annotation that works at other methods
#Override
#RequestMapping(value = "/ad", method = RequestMethod.POST, headers = "Accept=application/json")
public
#ResponseBody
Cd create(HttpServletResponse response, #RequestBody Cd cd) {
...
}
My interface that my controller implements is that:
public interface BaseController<T> {
public List<T> getAll(HttpServletResponse response);
public T getByName(HttpServletResponse response, String id);
public T create(HttpServletResponse response, T t);
public T update(HttpServletResponse response, T t);
}
Any advices?
PS: #SeanPatrickFloyd says that:
Note When using controller interfaces (e.g. for AOP proxying), make
sure to consistently put all your mapping annotations - such as
#RequestMapping and #SessionAttributes - on the controller interface
rather than on the implementation class

The thing is: controller mapping is done at runtime, and if you use AOP proxies, the proxy objects don't have annotations at runtime, only their interfaces do. I can think of two possible strategies to work around this limitation.
Either annotate the generic interface methods, or (if you don't want to advise all controllers) create a sub-interface per implementation type, explicitly annotating their methods. I know that's a lot of rewritten code and contrary to what AOP is about, but I don't know a better way when sticking with interface based proxies.
Another way would be to switch to CGLib proxies using proxy-target-class="true". That way the proxy classes should (I'm not sure about this) retain the annotations.
Update: annotating your interface should work like this (if it works)
public interface BaseController<T> {
#WebAuditable
public List<T> getAll(HttpServletResponse response);
#WebAuditable
public T getByName(HttpServletResponse response, String id);
#WebAuditable
public T create(HttpServletResponse response, T t);
#WebAuditable
public T update(HttpServletResponse response, T t);
}
Annotating a base class won't work, because JDK proxies don't expose any information that's not backed by interfaces.

Related

Spring boot change override exception responses

I am trying to customize exception responses and use my own response structure, I am using below way :
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler
{
#ExceptionHandler(RuntimeException.class)
#ResponseBody
public ResponseEntity<String> handle(Exception ex, HttpServletRequest request)
{
...
}
}
But I have not accessed to the status code, I need status code that defined in exceptions via ResponseStatus:
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public class ExtendSubscriptionReminderNotExistException extends RuntimeException
{
}
With java reflection mechanism, you can do it like so:
#ExceptionHandler(RuntimeException.class)
#ResponseBody
public ResponseEntity<String> handle(Exception ex, HttpServletRequest request) {
if (ex instanceOf ExtendSubscriptionReminderNotExistException) {
ResponseStatus status = ExtendSubscriptionReminderNotExistException.class.getAnnotation(ResponseStatus.class);
return ResponseEntity.status(status.value()).body(ex.getMessage());
}else{
//if it's not ExtendSubscriptionReminderNotExistException, do sth different
}
}
Here is an useful article on how to read annotation in java: Java Reflection - Annotations
If you want to override ResponseStatusExceptionResolver, then you should extends AbstractHandlerExceptionResolver and implement your own doResolveException like ResponseStatusExceptionResolver did, then create a configuration extending WebMvcConfigurationSupport and override configureHandlerExceptionResolvers, then spring will pick up your own exception resolver over the default one. The logic behind this is here.
we cannot change exception messages. However determine we can change the code and class, and throw a new one by overriding the same class with the same code and different message.
I may be wrong on this one, but to me it doesn't really make sense to use #ResponseStatus annotation and a custom ErrorHandler at the same time.
Annotations are supposed to make your code easier to understand and to avoid using such handlers.
If you really want to use the handler, I'd suggest to drop the annotation and store the corresponding status code in each exception (as a final static attribute for example).

Conflict in Using #RepositoryRestController and #RepositoryRestResource For a Single Resource in Spring Boot

I have a Payment entity in my spring boot application. Considering all possible CRUD operations, I'm using spring data rest for read and want to implement a custom create operation. Also delete and update are not allowed for this entity.
So this is my desired URLs and resoponsible component for each one:
GET /payments : PaymentRepository
GET /payments/{id} : PaymentRepository
POST /payments : PaymentController
This is my repository:
#RepositoryRestResource
public interface PaymentRepository extends PagingAndSortingRepository<Payment, Long> {
// disable create and update
#Override
#RestResource(exported = false)
Payment save(Payment entity);
// disable delete
#Override
#RestResource(exported = false)
void delete(Payment entity);
}
And this is my controller:
#RepositoryRestController
#RequestMapping("/payments")
public class PaymentController {
#PostMapping("")
#ResponseBody
public Payment create() {
// some code...
}
}
If I map create operation to a url like POST /payments/create, everything works fine, but If I use the above code and map create to POST /payments, the GET /payments url does not work any more and I get 405 Method Not Allowed error. (GET /payments/{id} is still working)
It seems in this case presence of #PostMapping("") annotation, cause the PaymentController to responsd the GET /payments request and it fails.
I hope my explanations were clear. How can I solve this problem?
The Spring Data REST reference states that:
Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the #RepositoryRestController annotation instead of a standard Spring MVC #Controller or #RestController.
It is not explicitly mentionned, but annotating your controller with #RepositoryRestController also allows you to define a custom behavior for one endpoint while keeping all the other endpoints that Spring automatically generates... On one condition: the #RequestMapping annotation can only be used at the method level (this is actually what is done in the example of the reference documentation).
Your example becomes:
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
With this, you get your custom endpoint mapped to POST /payments requests, plus all endpoints automatically generated by Spring, minus the ones annotated with #RestResource(exported = false).
#BasePathAwareController
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
You should modify your controller in the above way. #BasePathAwareController enables the custom REST URI's to get registered under your base URI.
With the above modification : both API's can work fine.

JAX-RS #Path in Composed Annotation

This one seems relatively straightforward. I'm messing around with composed annotations, and I'm trying to do the following:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Path("")
public #interface TestAnnotation {
#AliasFor(annotation = Path.class, attribute = "value")
String path();
}
This does not work. When I use it like so:
#Path("")
public class MyResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#TestAnnotation(path = "/things")
public void postIt(Thing myThing) {
// Do various things and then return a Response
}
}
...I receive a 405 in return. If I do this:
// Remove class-level #Path
// #Path("")
public class MyResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#TestAnnotation(path = "/things")
public void postIt(Thing myThing) {
// Do various things and then return a Response
}
}
...I receive a 404 in return.
There is just something about #Path or the fact that #Path has a required value attribute that results in this just not functioning, and I have no idea how to remedy it.
After further experimentation and research, it would appear that what I am trying to do is literally not possible.
A follow-up attempt was made to utilize Jackson's #JsonView and expose it through my composed annotation via Spring's #AliasFor, and this also failed to function.
I spent some time thinking about how annotations work and considering peeskillet's comments about compilation vs. processing, and I have come to the conclusion that both Jersey and Jackson must use annotation processors that basically just call "Method.isAnnotationPresent()" for detection of relevant annotations. Spring's #AliasFor most likely does not compile nor weave the aliased meta-annotations into the byte-code of the target methods, and thus they are not found by the processors.
My personal solution to this problem was to drop JAX-RS entirely and use Spring's #RequestMapping, which can be aliased in a composed annotation, and to just accept that Jackson's #JsonView simply can't be integrated into a composed annotation.
Obviously, this is not an ideal solution for most people, especially those with large, already established systems. In such situations, it is more likely that the idea of composed annotations will be abandoned long before JAX-RS.
So if anyone has some more insight into the problem, or someone directly from the Spring team wants to chime in, feel free.

Difference between handleRequestInternal and handleRequest

I have just started spring, I found that somewhere we are using handlerequest() method in controller and somewhere we are using handlerequestinternal() method.
I have tried google-ing this, but did not find any specific point.
Can any one explain what is the difference between these two functions and when we should implement each of them?
As I know spring framework will call by default handlerequest() function, so we can put our service layer there itself.
I am sure handlerequestinternal() must be providing some extra feature, but not sure.
Please help me to understand this.
Both handleRequest and handleRequestInternal are used by the old Spring 2.0 controller framework.
handleRequestInternal is used when you're extending one of the pre-supplied base support classes (e.g. AbstractController, SimpleFormController, etc). These use the Template design pattern, and you supply your business logic in that method.
handleRequest is the method specified on the Controller interface itself. If you directly implement that interface, rather than extending one of the above base classes, then you need to implement handleRequest directly.
Both are obsolete, and not used in controllers written for Spring 2.5 and later.
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.checkAndPrepare(request, response, this instanceof LastModified);
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
return this.handleRequestInternal(request, response);
}
}
}
return this.handleRequestInternal(request, response);
}
protected abstract ModelAndView handleRequestInternal(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
if in controller template classes like AbstractController and ParameterizableViewConterollers in this it will the child classes of Controller interface so spring peoples internally override the handleRequest method and call the abstract handleRequestInternal method so whenever we are using template classes we use this method otherwise we use the handleRequest() method only this the difference i think!.

Spring SimpleFormController in 3.0

i notice that this controller has now been deprecated in the latest spring and was wondering what the alternative controller is?
In Spring 3.0 you should use simple classes annotated by #Controller. Such controller can handle more than one request. Each request is handled by its own method. These methods are annotated by #RequestMapping.
One thing you need to rethink is the fact, that a old school SimpleFormController handle a lot of different requests (at least: one to get the form and a second to submit the form). You have to handle this now by hand. But believe me it is easier.
For example this Controller in REST Style, will handle two requests:
/book - POST: to create a book
/book/form - GET: to get the form for creation
Java Code:
#RequestMapping("/book/**")
#Controller
public class BookController {
#RequestMapping(value = "/book", method = RequestMethod.POST)
public String create(
#ModelAttribute("bookCommand") final BookCommand bookCommand) {
Book book = createBookFromBookCommand(bookCommand);
return "redirect:/book/" + book.getId();
}
#RequestMapping(value = "/book/form", method = RequestMethod.GET)
public String createForm(final ModelMap modelMap) {
modelMap.addAttribute("all", "what you need");
return "book/create"; //book/create.jsp
}
}
Annotated POJOs can act as controllers; see #Controller.
In Spring 3.0, your Controllers should no longer inherit from a base class.
The standard way is to use annotated controllers.

Categories