Controller类的方法上的RequestMapping一定要写在Controller类里吗?

使用Spring Cloud做项目的同学会使用Feign这个组件进行远程服务的调用,Feign这个组件采用模板的方式,有着优雅的代码书写规范。核心原理对Feign等相关注解进行解析,并提取信息,在Spring Boot工程启动时,通过反射生产Request的bean,并将提取的信息,设置到bean中,最后注入到ioc容器中。

现在有这样的场景,服务A提高RestApi接口,服务B、C、D等服务需要调用服务A提供的RestApi接口,这时最常见的做法是在服务B、C、D分别写一个FeignClient,并需要写RestApi接口的接收参数的实体和接收响应的实体DTo类。这样的做法就是需要不停复制代码。

有没有办法简洁上面的操作呢?有一种最常见的做法是将将服务A进行模块拆分,将FeignClient和常见的model、dto对外输出的类单独写一个模块,可以类似于取名a-service-open_share。这样将服务A服务分为两个模块,即A服务的业务模块和A服务需要被其他服务引用的公共类的模块。服务B、C、D只需要引用服务A的a-service-open_share就具备调用服务A的能力。

笔者在这里遇到一个有趣的其问题。首先看问题:

写一个FeignClient:

1 @FeignClient(name = "user-service")
2 public interface UserClient {
3
4     @GetMapping("/users")
5     List<User> getUsers();
6 }

写一个实现类:

 1 @RestController
 2 public class UserController implements UserClient {
 3     @Autowired
 4     UserService      userService;
 5
 6     @OverRide
 7     List<User> getUsers(){
 8        return userService.getUsers();
 9     }
10 }

启动工程,浏览器访问接口localhost:8008/users,竟然能正确访问?!明明我在UserController类的getUsers方法没有加RequestMapping这样的注解。为何能正确的映射?!

带着这样的疑问,我进行了一番的分析和探索!

首先就是自己写了一个demo,首先创建一个接口类:

1 public interface ITest {
2     @GetMapping("/test/hi")
3     public String hi();
4 }

写一个Controller类TestController

1 @RestController
2 public class TestController implements ITest {
3     @Override
4     public String hi() {
5         return "hi you !";
6     }
7 }

启动工程,浏览器访问:http://localhost:8762/test/hi,浏览器显示:

hi you !

我去,TestController类的方法 hi()能够得到ITest的方法hi()的 @GetMapping("/test/hi")注解吗? 答案肯定是获取不到的。

特意编译了TestController字节码文件:
javap -c TestController

 1  public class com.example.demo.web.TestController implements com.example.demo.web.ITest {
 2   public com.example.demo.web.TestController();
 3     Code:
 4        0: aload_0
 5        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 6        4: return
 7
 8   public java.lang.String hi();
 9     Code:
10        0: ldc           #2                  // String hi you !
11        2: areturn
12 }

上面的字节码没有任何关于@GetMapping("/test/hi")的信息,可见TestController直接获取不到@GetMapping("/test/hi")的信息。

那应该是Spring MVC在启动时在向容器注入Controller的Bean(HandlerAdapter)时做了处理。初步判断应该是通过反射获取到这些信息,并组装到Controller的Bean中。首先看通过反射能不能获取ITest的注解信息:

 1 public static void main(String[] args) throws ClassNotFoundException {
 2     Class c = Class.forName("com.example.demo.web.TestController");
 3     Class[] i=c.getInterfaces();
 4     System.out.println("start interfaces.."  );
 5     for(Class clz:i){
 6         System.out.println(clz.getSimpleName());
 7         Method[] methods = clz.getMethods();
 8         for (Method method : methods) {
 9             if (method.isAnnotationPresent(GetMapping.class)) {
10                 GetMapping w = method.getAnnotation(GetMapping.class);
11                 System.out.println("value:" + w.value()[0]  );
12             }
13         }
14     }
15     System.out.println("end interfaces.."  );
16
17     Method[] methods = c.getMethods();
18     for (Method method : methods) {
19         if (method.isAnnotationPresent(GetMapping.class)) {
20             GetMapping w = method.getAnnotation(GetMapping.class);
21             System.out.println("value:" + w.value());
22         }
23     }
24 }

允运行上面的代码:

start interfaces…

ITest

value:/test/hi

end interfaces…

可见通过反射是TestController类是可以获取其实现的接口的注解信息的。为了验证Spring Mvc 在注入Controller的bean时通过反射获取了其实现的接口的注解信息,并作为urlMapping进行了映射。于是查看了Spring Mvc 的源码,经过一系列的跟踪在RequestMappingHandlerMapping.java类找到了以下的方法:

 1 protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
 2    RequestMappingInfo info = createRequestMappingInfo(method);
 3    if (info != null) {
 4       RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
 5       if (typeInfo != null) {
 6          info = typeInfo.combine(info);
 7       }
 8    }
 9    return info;
10 }

继续跟踪源码在AnnotatedElementUtils 类的searchWithFindSemantics()方法中发现了如下代码片段:

1 // Search on methods in interfaces declared locally
2 Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
3 result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor,
4       visited, metaDepth, ifcs);
5 if (result != null) {
6    return result;
7 }

这就是我要寻找的代码片段,验证了我的猜测。

写这篇文章我想告诉读者两件事:

  • 可以将服务的对外类进行一个模块的拆分,比如很多服务都需要用的FeignClient、model、dto、常量信息等,这些信息单独打Jar,其他服务需要使用,引用下即可。
  • url映射不一定要写在Contreller类的方法上,也可以写在它实现的接口里面。貌似并没有是luan用,哈。

原文链接:https://blog.csdn.net/forezp/article/details/80069961

原文地址:https://www.cnblogs.com/blwy-zmh/p/11994522.html

时间: 2024-10-11 18:23:07

Controller类的方法上的RequestMapping一定要写在Controller类里吗?的相关文章

fastjson简单使用demo,@JSONField注解属性字段上与set、get方法上。实体类toString(),实体类转json的区别;_下划线识别

一.demo代码 @JSONField注解属性字段上与set.get方法上.使用@Data注解(lombok插件安装最下方),对属性“笔名”[pseudonym]手动重写setter/getter方法 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; @Data

Spring4-自动装配Beans-通过注解@Autowired在构造方法上

1.创建Maven项目,项目名称springdemo19,如图所示 2.配置Maven,修改项目中的pom.xml文件,修改内容如下 <project xmlns="http://maven.apache.org/POM/4.0.0"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

Spring的annotation用在set方法上 hibernate的annotation用get方法上

1.Spring的annotation用在set方法上 2.hibernate的annotation用在get方法上

vs2010 C# 如何将类做成DLL 再从另一个项目中使用这个类

vs2010 C# 如何将类做成DLL 再从另一个项目中使用这个类 2011-10-20 12:00 486人阅读 评论(0) 收藏 举报 一.将类做成DLL 方法一: 你可以通过在命令行下用命令将以 xxx.cs的文件直接生成为DLL文件 方法二:通过visual studio2010 新建立一个项目,选择为“类库”,然后在这里可以写用户自己的类. 二.引用DLL 在项目中,打开“解决方案资源管理器”,右键“引用”-->“添加引用”-->“浏览” 然后从主机的目录中找到DLL 之后在你的项目

UI(UGUI)框架(二)-------------UIManager单例模式与开发BasePanel面板基类/UIManage统一管理UI面板的实例化/开发字典扩展类

UIManage单实例: 1 /// 单例模式的核心 2 /// 1,定义一个静态的对象 在外界访问 在内部构造 3 /// 2,构造方法私有化 4 5 private static UIManager _instance; 6 7 public static UIManager Instance 8 { 9 get 10 { 11 if (_instance == null) 12 { 13 _instance = new UIManager(); 14 } 15 return _instan

java中IO写文件工具类

下面是一些根据常用java类进行组装的对文件进行操作的类,平时,我更喜欢使用Jodd.io中提供的一些对文件的操作类,里面的方法写的简单易懂. 其中jodd中提供的JavaUtil类中提供的方法足够我们使用,里面的方法写的非常简练,例如append,read等方法,封装更好,更符合面向对象, 这里面我写的一些方法可多都是模仿jodd,从里面进行抽取出来的. /** * 获取路径文件夹下的所有文件 * @param path * @return */ public static File[] ge

MVC5 Controller简要创建过程(2):由ControllerFactory创建Controller

上文已经完成了ControllerFactory的创建,接下来就是调用其CreateController()方法创建Controller了. DefaultControllerFactory中CreateController()的实现: public virtual IController CreateController(RequestContext requestContext, string controllerName) { //... Type controllerType = Get

至于Nim和Crystal这类语言最大的用途,就是活在脑残粉的理想里(发人警醒)

lisp,haskell 有很多人在用,而且是编程经验在 10 年以上,包括 Rust 的作者.Nim 的作者.因此,你不知道有人用,那是因为你的开发阅历太低. 人工智能这个领域从来没有脱离 lisp,Prolog,MIT 等等其国家级科学实验室一直从事着 lisp 研究,这种语言之所以重要是因为其是表描述的语言,可以最容易为机器所识别和生成. Erlang 已经在 facebook 的 what's app 实践了流量的价值,并且并为商业化应用,Go 和 docker 至今未能达到商业化,并且

Java基础-继承-编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight。小车类Car是Vehicle的子类,其中包含的属性有载人数 loader。卡车类Truck是Car类的子类,其中包含的属性有载重量payload。每个 类都有构造方法和输出相关数据的方法。最后,写一个测试类来测试这些类的功 能。

#29.编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight.小车类Car是Vehicle的子类,其中包含的属性有载人数 loader.卡车类Truck是Car类的子类,其中包含的属性有载重量payload.每个 类都有构造方法和输出相关数据的方法.最后,写一个测试类来测试这些类的功 能. package hanqi; public class Vehicle { private int wheels; private int weight