Spring源码分析之Spring MVC

Spring MVC 源码分析

一、Spring MVC运行流程图

微信图片_20200916144854.png

二、源码分析

第一步:发送请求

这里我们需要清楚的知道spring mvc的入口是org.springframework.web.servlet.DispatcherServlet#doDispatch()。

DispatcherServlet是SpringMVC中的前端控制器,负责接收request并将request转发给对应的处理组件。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {}

可以看到参数是HttpServletRequest,HttpServletResponse,专门来处理请求,和相应的。我们可以查看类图清晰的知道其实DispatcherServlet也是一个实现了HtttServlet接口的,是一个标准的Servlet容器。

image-20220712173459673.png

第二步:获取handler

HanlerMapping是SpringMVC中完成url到Controller映射的组件,也就是每个url都有一个对应的Handler去处理,而对应的handler里有对应的Controller。

首先,需要知道的是都有哪些handler,为什么会有这么多handler

  • 有哪些handler
image-20220712172229524.png

首先有这么多的handler,这个是在spring boot下进行的调试,如果只是单单的使用spring mvc,可能会少一个。

  • 为什么有这么多的handler

    因为历史原因,Spring MVC 在发展的过程中有很多种写法,其中最熟悉的写法如下:

    @RestController
    public class UserController {
        @GetMapping("/hello")
        public String sayHello() {
            return "hello";
        }
    
        @PostMapping("/bye")
        public String sayBye(){
            return "bye";
        }
    }
    

    但是在spring boot出现之前,或者在spring的早期版本中,还有如下写法也可以处理客户端请求

    @Component("/demo3")
    public class ControllerDemo3 implements HttpRequestHandler {
        @Override
        public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
            System.out.println("===实现HttpRequestHandler接口===");
        }
    }
    

    亦或是

    @Component("/demo2")
    public class MyController implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            System.out.println("123123");
            return null;
        }
    }
    

    亦或是

    @Component("/demo4")
    public class TemproController {
        public String sayHello(){
            return "demo4";
        }
    }
    

    每种写法的背后都需要一种对应的处理器去处理,因为写法的不通,所以早就了这么多的handler。

其次,需要看spring 是如何查找handler的

  1. 在讲解如何查找handler之前,需要先了解这里的设计结构

    看第三部分HandlerMapping

  1. 具体查找如下,可以看到doDispatch方法如下
image-20220713095828508.png

getHandler()方法如下

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            /**
            * 通过for循环在所有的handlerMappings里查找对应的handler。
            * 怎么查找继续看mapping.getHandler()
            * 
            */
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    ......
}
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
       //这里可以看到lookupPath就是我们请求的路径 如:/demo3
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        this.mappingRegistry.acquireReadLock();
        try {
            //这里lookupHandlerMethod就是查找handler的地方
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
}

重点来了,这里就是最终查找handler的方法

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List matches = new ArrayList<>();
        //①
        List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                Comparator comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

①处调试代码如下:

image-20220713100608789.png

可以看到所有的请求地址都在LinkedMultivalueMap中,这里没有匹配到,如果没有匹配到最后

image-20220713100835501.png

,这里在第二轮的查找中找到了,如下:

image-20220713101127057.png

这里找到handler之后,在看是如何返回的,返回的过程中,有没有对此ControdderDemo3做过什么修改。我们看到最后return了一个buildPathExposingHandler。这个方法具体做了什么,我们接着往下看

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
                                          String pathWithinMapping, @Nullable Map uriTemplateVariables) {
    
    /**
    * 这里使用了HandlerExecutionChain把原有的处理器给包裹了,并暴露请求的路径地址以及uri模板变量。
    * 这里为什么包裹了,后面再说......
    */
    HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
    if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
        chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
    }
    return chain;
}

也就是说这里返回了一个包含原有处理器的HandlerExecutionChain,并且HandlerExecutionChain内新增了了一个会拦截请求的拦截器。

如下:

image-20220713114037028.png

也就是说

image-20220713101127057.png

这个lookupHandler返回了一个包装了原有处理器ControllerDemo3和拦截/demo3的拦截器的一个HandlerExecutionChain对象。

整体获取Handler的流程图如下:

Spring MVC 流程图.jpg

第三步:获取Adapter

核心代码如下:

// Determine handler adapter for the current request.
   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        //匹配适配器
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
   }

这里this.handlerAdapters与上一步中的this.handlerMappings有一定的对应关系。如下图:

image-20220713160541229.png

这里匹配到了一个Adapter。

image-20220713161109616.png

此时的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());返回的ha就是HttpRequestHandlerAdapter

继续往下执行

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

handler方法:

//这个时候的handler参数实际是ControllerDemo3
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //将ControllerDemo3强转为父类型,调用handleRequest方法,实际调用的就是ControllerDemo3的handleRequest方法
        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }

ControllerDemo3就是实现了HttpRequestHandler接口,代码如下:

@Component("/demo3")
public class ControllerDemo3 implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
        System.out.println("===实现HttpRequestHandler接口===");
    }
}

后续的流程这里不在分析了,至此Spring MVC的核心流程分析完毕。

三、HandlerMapping

public interface HandlerMapping {
    获取HandlerExecutionChain
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

一开始我们用了4中方式去实现一个自己的Controller。这4中Controller在spring启动的时候,会被分门别类的去处理。写法不同,处理方式肯定不同。虽然处理凡是不同,但是结果又都是相同的。所以这里抽象出一个接口,多个实现去在不通情况下获取HandlerExecutionChain的。

image-20220713162848151.png

这里以RequestMappinghandlerMapping为例进行分析

image-20220713163423576.png

这里通过查找了解到,AbstractHandlerMethodMapping实现了HandlerMapping接口

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        return executionChain;
}

getHandlerInternal的实现如下,中间省略了部分代码:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List matches = new ArrayList<>();
        //   ①当前这种handler的核心获取方式是在
        List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            // ② 这里会在初始化后的mappingRegistry.getMappings()方法里找到匹配的自定义controller
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (!matches.isEmpty()) {
            //  ③获取match
            Match bestMatch = matches.get(0);
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            //  ④这里bestMatch.handlerMethod方法返回的就是UserController对象
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
image-20220713170031936.png

返回的UserController对象又回被封装成HandlerExecutionChain对象。执行和前面分析的逻辑一样。

四、这里在分析HandlerAdapter接口

public interface HandlerAdapter {
    boolean supports(Object handler);
    
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    long getLastModified(HttpServletRequest request, Object handler);

handlerAdapter的实现:

image-20220714092224604.png

这里针对不通写法的controller,对应不通的xxxAdapter适配器。这里我们以RequestMappingHanderAdapter为例分析一下,这个也是适配如下的controller

@RestController
public class UserController {
    @GetMapping("/hello")
    public String sayHello() {
        return "hello";
    }
}

至于为什么处理的是这种controller,我们可以看下HandlerAdapter接口的supports实现,也就是RequestMappingHanderAdapter实现类中的supports方法即可知道原因。

    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

这里我们只需关注handler instanceof HandlerMethod 这里返回的true,所以也就是RequestMappingHanderAdapter来处理UserContoller了。这里我们也看到了UserContoller类也没有实现任何抽象,那么UserContoller又如何是HandlerMethod类型呢,后面接着说。这里RequestMappingHanderAdapter最后也是通过反射调到了实际的controller#sayHello()方法,反射在这里就不过多赘述了。其他的HandlerAdapter实现通过同样的方式去分析也就清楚了这里为什么要有适配器。

五、Controller和HandlerMethod

RequestMappingHandlerMapping实现了InitializingBean接口,在afterPropertiesSet()方法了,完成UserController到HandlerMethod类型的转变。

    public void afterPropertiesSet() {
        ......
        super.afterPropertiesSet();
    }

父类方法:

    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                //重点在这里
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
       ......
        if (beanType != null && isHandler(beanType)) {
            //进入这里
            detectHandlerMethods(beanName);
        }
    }

    protected void detectHandlerMethods(Object handler) {
        Class handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class userType = ClassUtils.getUserClass(handlerType);
            // 看图3就很清楚当前代码的作用了。不过我们的关注点在第四步
            Map methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                //看这里 第四步
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
image-20220714102418057.png

图3:

图3

接着看registerHandlerMethod(handler, invocableMethod, mapping);

跟下去,会跟到如下代码

AbstractHandlerMethodMapping.java
    

public void register(T mapping, Object handler, Method method) {
            // Assert that the handler method is not a suspending one.
            if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
                Class[] parameterTypes = method.getParameterTypes();
                if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
                    throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
                }
            }
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                ......
            }
            ......
        }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //                                      重点                                                                 //
    //                                      重点                                                                 //
    //                                      重点                                                                 //
    //                                      重点                                                                 //
    //                                      重点                                                                 //
    //这个方法就是把handler封装一下,最后返回HandlerMethod,这里最后也就说明了为什么我们自定义的Controller的类型是HandlerMethod了//
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        if (handler instanceof String) {
            //看这里,明白了一切
            return new HandlerMethod((String) handler,
                    obtainApplicationContext().getAutowireCapableBeanFactory(), method);
        }
        return new HandlerMethod(handler, method);
    }

这里在提一个问题,为什么要把Controller封装成HandlerMethod?这里是因为controller的方法有很多,如果不用HandlerMethod这个通用的类去封装的话,一些其他功能实现起来可能就比较麻烦了。比如HandlerInterceptor接口,比如我们实现了这么一个接口

@Component
public class MyInterceptor extends HandlerInterceptorAdapter {

    //如果这里不使用handler,传入的事controller,那么到了调用父类的super.preHandle方法就没法适配其他controller的方法了
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("handler is executed!!!!!!!");
        return super.preHandle(request, response, handler);
    }
   ......
}

看下HandlerMethod的注释

/**
 * Encapsulates information about a handler method consisting of a
 * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}.
 * Provides convenient access to method parameters, the method return value,
 * method annotations, etc.
 *
 * 

The class may be created with a bean instance or with a bean name * (e.g. lazy-init bean, prototype bean). Use {@link #createWithResolvedBean()} * to obtain a {@code HandlerMethod} instance with a bean instance resolved * through the associated {@link BeanFactory}. */ public class HandlerMethod {}

大意就是:

HandlerMethod封装了很多属性,在访问请求方法的时候可以方便的访问到方法、方法参数、方法上的注解、所属类等并且对方法参数封装处理,也可以方便的访问到方法参数的注解等信息

你可能感兴趣的:(Spring源码分析之Spring MVC)