女朋友被公司裁员一个月了,和我一样作为后端工程师,最近一直在找工作,面试了很多家还是没有找到工作,面试官问@Controller的原理,她表示一脸懵,希望我能给她讲清楚。之前我也没有好好整理这块知识,这次借助这个机会把它彻底搞清楚。
我们知道Controller注解的类能够实现接收并处理Http请求,其实在我看Spring mvc模块的源码之前也和我女朋友目前的状态一样,很疑惑,Spring框架是底层是如何实现的,通过使用Controller注解就简单的完成了http请求的接收与处理。
有疑问就好啊,因为兴趣是最好的老师,如果有兴趣才有动力去弄懂这个技术点。
(资料图)
看过前面的文章的同学就会知道,学习Spring的所有组件,脑袋里要有一个思路,那就是解析组件和运用组件两个流程,这是Spring团队实现组件的统一套路,大家可以回忆一下是不是这么回事。
首先我们看看Spring是如何解析Controller注解的,打开源码看看他长啥样??
@Target({ElementType.TYPE})@Componentpublic @interface Controller { String value() default \"\";}复制代码
发现Controller注解打上了Component的注解,这样Spring做类扫描的时候,发现了@Controller标记的类也会当作Bean解析并注册到Spring容器。 我们可以看到Spring的类扫描器,第一个就注册了Component注解的扫描
//org.springframework.context.annotation.ClassPathScanningCandidateComponentProviderprotected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class));}复制代码
这样Spring容器启动完成之后,bean容器中就有了被Controller注解标记的bean实例了。 到这里只是单纯的把Controller标注的类实例化注册到Spring容器,和Http请求接收处理没半毛钱关系,那么他们是怎么关联起来的呢?
这个时候Springmvc组件中的另外一个组件就闪亮登场了
RequestMappingHandlerMapping
RequestMappingHandlerMapping 看这个名就可以知道他的意思,请求映射处理映射器。 这里就是重点了,该类间接实现了InitializingBean方法,bean初始化后执行回调afterPropertiesSet方法,里面调用initHandlerMethods方法进行初始化handlermapping。
//类有没有加Controller的注解protected boolean isHandler(Class>beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));}protected void initHandlerMethods() { //所有的bean String[] beanNames= applicationContext().getBeanNamesForType(Object.class); for (String beanName : beanNames) { Class>beanType = obtainApplicationContext().getType(beanName); //有Controller注解的bean if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods());}复制代码
这里把标注了Controller注解的实例全部找到了,然后调用detectHandlerMethods方法,检测handler方法,也就是解析Controller标注类的方法。
private final Map>registry = new HashMap<>();protected void detectHandlerMethods(final Object handler) { Class>handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { final Class>userType = ClassUtils.getUserClass(handlerType); //查找Controller的方法 Mapmethods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup) method ->getMappingForMethod(method, userType)); methods.forEach((method, mapping) ->{//注册 this.registry.put(mapping,new MappingRegistration<>(mapping,method)); }); }复制代码
到这里为止,Spring将Controller标注的类和类方法已经解析完成。现在再来看RequestMappingHandlerMapping这个类的作用,他就是用来注册所有Controller类的方法。
接着还有一个重要的组件RequestMappingHandlerAdapter 它就是用来将请求转换成HandlerMethod,并且完成请求处理的流程。 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Overridepublic boolean supports(Object handler) { return handler instanceof HandlerMethod;}protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //请求check checkRequest(request); //调用handler方法 mav = invokeHandlerMethod(request, response, handlerMethod); //返回 return mav;}复制代码
看到这里,就知道http请求是如何被处理的了,我们找到DispatcherServlet的doDispatch方法看看,确实是如此!!
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // 从注册表查找handler HandlerExecutionChain mappedHandler = getHandler(request); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 调用 ModelAndView m = ha.handle(processedRequest, response, mappedHandler.getHandler()); // processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}复制代码
DispatcherServlet是Spring mvc的总入口,看到doDispatch方法后,全部都联系起来了。。。 最后我们看看http请求在Spring mvc中的流转流程。
第一次总结SpringMvc模块,理解不到位的麻烦各位大佬指正。
原文链接:https://juejin.cn/post/7222186286564311095
标签: