Click on the blue letters above to follow us

01

preface

The cause of the matter is that microservice A calls one of the interfaces of microservice B through feign and reports an exception in the following shape

Xiao Zhang, the engineer in charge of microservice A, found Xiao Li, the engineer responsible for providing the interface, and asked Xiao Li if he had changed the interface, Xiao Li said with an innocent face that he had not made any changes to this interface recently, but Xiao Li still said that he checked it.

02

Troubleshooting process

Xiao Li’s troubleshooting process is as follows, he first checks whether the interface he provides to A service exists through swagger, and he finds that he cannot see the interface he provides to A service on swagger. So he doubted whether someone had moved his code, he went to look up the recent git commit record, found that no one touched his code, because the project has not yet been released, all in the testing stage, he will be based on the project integration of git-commit-id-maven-plugin plugin to test which version of the current release is. (ps: Friends who are interested in git-commit-id-maven-plugin can check out the previous article on how to verify that the online version is in line with the expected version). Then he put the version of the code down to the local debugging, he found that the interface provided to A in the code is still there, the class under the target also has an interface class provided to A, but the strange thing is that swagger is not showing the interface he provided out, he once thought that swagger had a problem, so he used postman to directly ask him to provide A’s interface, found that 404. Then he called Xiao Wang, a colleague in charge of the same microservice B, to help try it out, and found that the result was 404. After the recruitment, Xiao Li went to ask for help from their senior colleague Xiao Lin.

Kobayashi’s troubleshooting idea is as follows, he first looked up Xiao Li’s interface code, and found that he added a @Async to the method of the interface implementation layer he provided, and the example is as follows

Xiao Lin intuitively told Xiao Li with years of experience that it should be caused by @Async. Xiao Li said very categorically that it was impossible, he @Async added very early, before the interface can be accessed, Xiao Lin saw Xiao Li said so sure, he is not good at hitting Xiao Li. So he next did the following operation, first configure the following parameters in the project yml, and open the springweb log

Then add code in the project that looks like the following to track the type of the interface bean

Start the console and see the log shape as follows

It was found that the relevant requestMapping mapping information was indeed not printed, which can explain that Xiao Li’s interface was not bound to the springmvc mapping, which is the reason for the emergence of 404. Then observe the bean printed by the console, which looks like this

It is obvious that this interface bean has been replaced by the jdk dynamic proxy. Xiao Li saw the information printed on the console, thought about it, and then said, I removed the @Async and tried it. Xiao Li removed the @Async and then looked at the console

Through the console, it can be found that the interface has been bound to the springmvc mapping, and the printed bean type is a real object bean. Xiao Li saw this phenomenon and was puzzled, he said that he had indeed added @Async before, and the interface could be accessed normally. So Xiaolin asked, are you sure that you added @Async, did asynchronous take effect, Xiao Li said to open spring asynchrony, isn’t it all @Async. Xiaolin asked again, you open asynchronous in the project, in addition to adding @Async, what else to do, Xiao Li said no, he used asynchronous in the project before he added @Async, can also use a good one, Xiaolin listened, basically know why Xiao Li @Async before, the interface can be accessed normally, Xiaolin in order to verify the idea, just ask Xiao Wang who is responsible for the project, said that you have recently added any asynchronous operations, Xiao Wang said there is, Xiao Lin further asked, how do you do it, Xiao Wang said, He first adds @EnabledAsyn, turns on asynchrony, and then adds @Async annotations to the methods at the business logic layer. Xiao Li listened and said that he had to cooperate with @Async when he used @EnabledAsyn, he didn’t know it before

Then Xiao Li said that in the controller can not use @Async annotations? , Kobayashi said that it is best to move the logic of adding @Async to the service layer to deal with, but it is not that the controller cannot use the @Async annotation, and then Kobayashi in order to verify this idea, he removed the original implementation of the interface class, the shape is as follows

After launching, view the console

At this point, the type of bean is as follows

Provider, print the following contents

From the console, it can be found that it is triggered by the http-nio-8080-exec-1 thread, indicating that the async does not take effect, that is, the @Async fails. Later, the controller was modified as follows

Provider, print the following contents

This shows that in the controller, you can actually use @Async, but you have to do extra processing. So the suggestion is to take the @Async out of the controller and process it in a new class, as shown below

Provider, print content

Description Asynchronous effect

03

Analysis of troubleshooting results

01

Interface 404

Log from mvc

We can know that the controller mapping processing is in the RequestMappingHandlerMapping class, but which method is the specific method to process, we can reverse the information printed by the log, or we can add breakpoint debugging based on the spring feature, such as debugging through the afterPropertiesSet extension point. You will find that the mapping processing of RequestMappingHandlerMapping is in

Processing, specifically through processCandidateBean

Ultimately, it is handled by detectHandlerMethods

This is the actual registration. The premise of implementing detectHandlerMethods is

That is, only the class with the added @Controller or @RequestMapping will be processed, and why @RestController is also processed, click @RestController discovery

He is essentially @Controller. But we look for annotations through reflection, and normally only look for one layer, for example

He finds the @RestController layer instead of continuing to look for @RestController inside the @Controller, and AnnotatedElementUtils.hasAnnotation, this annotation method is different, he can find the merged annotation, even if it is @RestController, he will continue to find the @Controller inside. Therefore, this method is useful for finding composite annotations

When we use the jdk dynamic proxy, because there is no @Controller or @RequestMapping on the parent class, it will not be mapped by mvc, resulting in 404. When using cglib, because he inherits the target class as a subclass, he inherits the annotations on the target class, so when it is a cglib proxy, he will be mapped normally by mvc

02

Why is the controller added @Asyn asynchronous invalid

This is because with the addition of @Async, the controller becomes a proxy, and when it comes to processing methods asynchronously, it uses the target object, not the proxy object. This is basically a routine with the current interview affairs why the affairs are invalid

04

summary

This article focuses @Async causes the controller 404 to fail @Async. The recommended solution is to extract the @Async out of the controller and create a new service class for processing.