本篇内容介绍了“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:
@FeignClient(name = "user-service") public interface UserClient { @GetMapping("/users") List<User> getUsers();
写一个实现类:
@RestController public class UserController implements UserClient { @Autowired UserService userService; @OverRide List<User> getUsers(){ return userService.getUsers(); }
启动工程,浏览器访问接口localhost:8008/users,竟然能正确访问?!明明我在UserController类的getUsers方法没有加RequestMapping这样的注解。为何能正确的映射?!
带着这样的疑问,我进行了一番的分析和探索!
首先就是自己写了一个demo,首先创建一个接口类:
public interface ITest { @GetMapping("/test/hi") public String hi(); }
写一个Controller类TestController
@RestController public class TestController implements ITest { @Override public String hi() { return "hi you !"; }
启动工程,浏览器访问:http://localhost:8762/test/hi,浏览器显示:
hi you !
我去,TestController类的方法 hi()能够得到ITest的方法hi()的 @GetMapping(“/test/hi”)注解吗? 答案肯定是获取不到的。
特意编译了TestController字节码文件:
javap -c TestController
public class com.example.demo.web.TestController implements com.example.demo.web.ITest { public com.example.demo.web.TestController(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public java.lang.String hi(); Code: 0: ldc #2 // String hi you ! 2: areturn }
上面的字节码没有任何关于@GetMapping(“/test/hi”)的信息,可见TestController直接获取不到@GetMapping(“/test/hi”)的信息。
那应该是Spring MVC在启动时在向容器注入Controller的Bean(HandlerAdapter)时做了处理。初步判断应该是通过反射获取到这些信息,并组装到Controller的Bean中。首先看通过反射能不能获取ITest的注解信息:
public static void main(String[] args) throws ClassNotFoundException { Class c = Class.forName("com.example.demo.web.TestController"); Class[] i=c.getInterfaces(); System.out.println("start interfaces.." ); for(Class clz:i){ System.out.println(clz.getSimpleName()); Method[] methods = clz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(GetMapping.class)) { GetMapping w = method.getAnnotation(GetMapping.class); System.out.println("value:" + w.value()[0] ); } } } System.out.println("end interfaces.." ); Method[] methods = c.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(GetMapping.class)) { GetMapping w = method.getAnnotation(GetMapping.class); System.out.println("value:" + w.value()); } } }
允运行上面的代码:
start interfaces..
ITest
value:/test/hi
end interfaces..
可见通过反射是TestController类是可以获取其实现的接口的注解信息的。为了验证Spring Mvc 在注入Controller的bean时通过反射获取了其实现的接口的注解信息,并作为urlMapping进行了映射。于是查看了Spring Mvc 的源码,经过一系列的跟踪在RequestMappingHandlerMapping.java类找到了以下的方法:
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; }
继续跟踪源码在AnnotatedElementUtils 类的searchWithFindSemantics()方法中发现了如下代码片段:
// Search on methods in interfaces declared locally Class<?>[] ifcs = method.getDeclaringClass().getInterfaces(); result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor, visited, metaDepth, ifcs); if (result != null) { return result; }
这就是我要寻找的代码片段,验证了我的猜测。
“RequestMapping要写在Controller类里吗”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/opensource/224003.html