一、服务器启动过程中的操作
1、AbstractHandlerMethodMapping注册url和HandlerMethod(处理url与执行方法对应关系)
环境Spirng、SpringMVC
执行时间:当启动tomcat服务器的过程中(接收请求前),当bean被注入到容器后会执行一系列的初始化过程。
这里探讨的是url与handlerMethod对应关系存储的过程,所有的映射关系存储在AbstractHandlerMethodMapping类的内部类MappingRegistry里的registry属性中(是一个HashMap)。
这个过程是在AbstractHandlerMethodMapping抽象类中完成初始化的:
我准备了一个@Controller,并且包含@RequestMapping注解包含url请求地址:
①:在bean注入后会执行初始化方法
//在初始化时执行检测方法 @Override public void afterPropertiesSet() { initHandlerMethods(); //初始化执行器方法 }
②:在①中调用初始化的方法
protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { //getCandidateBeanNames()是获取到所有注册的bean名称即id名 if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { //看<1>,来进行执行处理对应的bean(目的就是自定义控制器类中包含url地址的类) processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
// <1> 即处理对应的bean操作,参数为上面遍历的bean的id名称 protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { //获取指定id名称的bean实例即Class类 beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } //判断该bean类是否符合类型,其中 isHandler(beanType)见 <2> if (beanType != null && isHandler(beanType)) { //见 <3> :一旦符合我们的要求就对这个类进行处理(来对去处理该类中的方法) detectHandlerMethods(beanName); } } // <2> 判断是否是我们要找的处理器,及是否包含注解 @Override protected boolean isHandler(Class<?> beanType) { //看到这里就很明白了,可以猜测上面的循环就是遍历找出含有@Controller、@RequestMapping的注解类,之后来进行获取注解中的url return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } // <3> 找到了执行器,那么就将该执行器中的所有方法来进行遍历取到映射地址(url以及对应handlerMethod绑定) protected void detectHandlerMethods(Object handler) { //获取到执行器类 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); //将方法与映射地址存储到map集合中去,这一步就做完了操作 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } else if (mappingsLogger.isDebugEnabled()) { mappingsLogger.debug(formatMappings(userType, methods)); } //遍历map集合中的映射键值对(执行方法与url地址) methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); //关键!!!将执行器方法与url进行注册(也就是存储到AbstractHandlerMethodMapping.MappingRegistry.registry这个HashMap集合中来) //见3中的代码解析 registerHandlerMethod(handler, invocableMethod, mapping); }); } }
③这些Method类以及url地址应该统一进行保存,当请求来临时就会从一个HashMap中根据url去拿到这个方法类。前面也看到了显示遍历bean容器中的bean,接着筛选真正的执行器并对其中的方法来进行遍历拿到注解中的url地址存储到methods中。
最终是存储到AbstractHandlerMethodMapping.MappingRegistry.registry这个HashMap集合中去的,通过调用registerHandlerMethod()方法来进行统一存储的。
这个handler实际上就是存储着bean的id以及hash值 //注册执行器方法 protected void registerHandlerMethod(Object handler, Method method, T mapping) { // 见<1> this.mappingRegistry.register(mapping, handler, method); } //<1>,进行再处理 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { //拿着handler以及method类创建HandlerMethod实例(其中包含了该方法的bean的相关内容) HandlerMethod handlerMethod = createHandlerMethod(handler, method); validateMethodMapping(handlerMethod, mapping); Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { this.pathLookup.add(path, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { corsConfig.validateAllowCredentials(); this.corsLookup.put(handlerMethod, corsConfig); } //重点:真正放入到registry这个hashmap集合中的是映射地址以及包含了指定方法的一系列信息 this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null)); } finally { this.readWriteLock.writeLock().unlock(); } }
HandlerMethod对象中包含了bean的相关内容,为后面反射做铺垫:
当①中的执行完之后注册url以及handlerMethod的过程就结束了!
总结:在bean容器装配好之后,会进行url以及执行器的装配工作,这些工作都是在AbstractHandlerMethodMapping这个抽象类中完成, afterPropertiesSet()作为入口,简单来说就是遍历在IOC容器中注册的所有bean,筛选出具有@Controller与@RequestMapping注解的类,筛选过后会对指定类的方法依次去遍历搜集并对url以及method进行配对,最终统一存储到AbstractHandlerMethodMapping抽象类中的一个内部类里的register属性(hashmap集合)中,之后方便请求来临时,根据url来去拿到指定的方法!
二、请求来临时
1、为什么DispatcherServlet的doService()方法是入口?
首先明确一点DispatcherServlet它是一个Servlet,在web.xml被读取时创建的Servlet,看下我们初始阶段进行配置的内容
当项目运行前DispatcherServlet会在被读取web.xml时进行实例化,也就是说该servlet已经被注册到tomcat的容器里了。
①证明DispatcherServlet是Servlet。
这里进行一下说明为什么DispatcherServlet本质就是一个Servlet呢?
下面是DispatcherServlet的继承图:
如何证明?注意DispatcherServlet的父类HttpServletBean继承了HttpServlet类(该类是servlet-api这个jar包中拥有的,也就是tomcat中使用的servlet的jar包)
既然其是一个Servlet并且加载web.xml阶段就会被tomcat中的servlet容器所管理!
②发送请求触发DispatcherServlet
之前可以看到web.xml中配置DispatcherServlet的映射地址为/,也就是所有的请求。
这里介绍一下Servlet的生命周期:初始化、init()、service()、destory()。
DispatcherServlet中也重写了其中的方法。
HttpServletBean中重写了init()方法。
FrameworkServlet中重写了Service()方法。关键点
这和我发送一个请求过来触发DispatcherServlet有没有什么关系呢?发送一个请求过来首先会解析请求路径,接着就会去找对应路径的servlet,接着去执行servlet中的Service()方法。
FrameworkServlet类部分:
在FrameworkServlet中的Servcie()方法中,若是一般的get或post请求会走下面黄色框的service方法,该方法调用的就是HttpServlet的Service()方法(也就是原生的Servlet中的方法):
执行HttpServlet的Service()方法中,会根据其中的方法来进行调用指定的doGet()或doPost()方法,很凑巧FrameworkServlet中重写了多个doXXX()方法,那么就会走这些重写的方法:其实重写后的方法调用的都是processRequest()方法
该方法同样属于FrameworkServlet类中的方法,把注意力放在其中的doService()方法,重头来了
DispatcherServlet类部分:
实际上执行该方法时就会进入到DispatcherServlet方法中的doService()方法:
OK,一下子完整舒服了,绕了一大圈依旧是从service()方法中进来最后进入到doService()中去,之后我们再慢慢分析在doService()方法中到底做了些什么事情?
一切开始从中央处理器的doServcie()开始:
2、认识doDispatch
doDispatch:执行请求的分发
①获取请求对应的HandlerExecutionChain 处理器链。
②根据这个处理器得到指定的Handler对象。根据请求路径来配对对应的方法,将这个方法信息进行储存。
③前置处理拦截器。
④真正的调用handler方法,并返回视图。
包含HandlerExecutionChain(处理器链)
doDispatch()是在doService()方法中执行的:主要用于请求的分发
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //1、获得请求对应的 HandlerExecutionChain 对象(即执行器处理链,包含一个执行器与拦截器) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } //2、获得当前 handler 对应的 HandlerAdapter 对象 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //3、前置处理拦截器 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //4、真正的调用handler方法,执行其中的业务操作,以及获取到modelAndView对象 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //5、添加视图 applyDefaultViewName(processedRequest, mv); //6、后置处理拦截器 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } //处理正常和异常的请求调用结果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { //已完成 拦截器 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { //已完成 拦截器 triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }