前言
这里分享一下我遇到的一个挺有意思的 Controller 形式, 内容涉及 @RequestMapping 注解的原理.
实际案例
一, 基本描述
项目甲中有多个模块, 其中就有模块 A 和 B.(这里的模块指的是 Maven 的多模块子项目), 项目乙, 丙, 丁可以引用模块 A 来访问独立部署的模块 B
模块 A => 关于与模块 B 通信的协议定义
模块 B => 可以独立部署的项目
其中模块 A 中定义了一个 FeignClient 的接口用于访问模块 B 的服务.
- @FeignClient(name = "xx-service", url="http://xx-service:8080")
- public interface XXXService {
- @PostMapping("/some-url")
- public SomeResponse someOperation(@RequestBody SomeParam param);
- ...
- }
我们知道只要打上了 @FeignClient 注解, 我们就可以直接在类中引入这个 XXXService, 然后调用它的方法. 如果我们调用 someOperation 方法, 那它就会根据服务名获取到 IP 端口信息, 然后发 HTTP 请求到指定服务 xx-service.
二, 问题出现
按理说, 模块 B 肯定有一个 Controller 来处理这个 "/som-url" 的请求. 于是我全局搜索了模块 B, 发现怎么找也找不到 "/some-url".
后来发现模块 B 中依赖于模块 A, 且有一个类实现了 XXXService 这个接口. 这个类叫 XXXServiceImpl. 这个类既不叫 xxxController, 也不带 @Controller 注解.
- @ResponseBody
- @RequestMapping
- @Service
- public class XXXServiceImpl implements XXXService {
- @Override
- public SomeResponse someOperation(SomeParam param) {
- SomeResponse someResponse = new SomeResponse();
- ...
- return someResponse;
- }
- ...
- }
看到 @ResponseBody,@RequestMapping,@Service. 在我看来这就是一个 @RestController 了. 但是问题来了, 还是没有 Mapping.
三, 我的一个猜想
我想有没有可能实现类继承了接口方法上的注解, 于是我看了下 @PostMapping 的元注解, 发现并没有 @Inherited 的注解. 我也尝试打印了 XXXServiceImpl 类 someOperation 方法上的注解, 发现确实没有.
四, 问题解答
XXXServiceImpl 确实充当了 Controller. 关键在于针对 @RequestMapping 的扫描, 会向上扫描类的所有父类和接口. 只要在接口或者父类的上方法上发现了 @RequestMapping 的注解, 就认定这个方法是某个请求关联的处理方法.
简化版本
可能有人不是很理解前面的实际案例, 这里提供一个简化的版本.
一, 新建一个只带 web 的 Spring Boot 项目
二, 定义一个接口
这个接口有一个方法, 带有 @GetMapping 注解.
- public interface ControllerInterface {
- @GetMapping("/hello")
- String hello();
- }
三, 添加实现类
- @RequestMapping
- @ResponseBody
- @Service
- public class ControllerInterfaceImpl implements ControllerInterface {
- @Override
- public String hello() {
- return "hello world";
- }
- }
四, 启动并测试
启动项目, 访问 http://localhost:8080/hello
发现确实可以访问. 实际上组合方式还有很多, 你可以把注解都写在接口上.
@RequestMapping 原理简述
一, 搜寻处理类
一个类上如果有 @Controller 或者 @ReqeustMapping 注解, 会被认定为是请求的 Handler.
二, 搜寻处理方法
找到处理类后, 第二步就是在处理类中查找处理方法, 即标注有 @RequestMapping(或者其他变种) 的方法, 注意这里扫描的过程中, 会向上扫描父类或者接口是否带有此注解. 这个操作由 HandlerMethodSelector 来完成 (Spring 4 有这个类, 后面的版本可能名称有所变更)
三, 整合 URL 生成 RequestMappingInfo
如果处理类上有 @RequestMpping 注解, 且有设置 url, 则会将类上和方法上的 URL 拼接起来. 最终形成一个 RequestMappingInfo.
四, 生成映射关系
每个 RequestMappingInfo 会映射到一个处理方法 HandlerMethod.
参考资料
1.@RestMapping 原理讲解 - 1
2.HandlerMethodSelector
来源: https://www.cnblogs.com/longfurcat/p/10741369.html