SpringMVC源码分析


#SpringMVC源码分析

SpringMVC 默认有一个 DispatcherServlet 映射所有的请求("/"),然后进行分发(dispatch)处理:

<!-- SpringMVC 默认的 DispatcherServlet -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

除非用户在 web.xml 中,自定义 Servlet映射 **指定路径的请求 **处理,否则所有的请求都会进入DispatcherServlet处理:

<!-- 用户自定义的Servlet -->
<servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>com.yunding.MyServelt</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>/myServlet</url-pattern>
</servlet-mapping>

DispatcherServlet类的继承关系

[外链图片转存失败(img-pB4barTI-1563614868603)(C:\Users\ADMINI~1\AppData\Local\Temp\1561441818709.png)]

DispatcherServlet类 间接地父类实现了 Servlet接口,因此其本质上依旧是一个Servlet

/** * Servlet接口定义了 带参数的init(ServletConfig config)方法 */
public interface Servlet {
    
    void init(ServletConfig var1) throws ServletException;

    void service(ServletRequest var1, ServletResponse var2);
    
	。。。
}
/** * GenericServlet抽象类 */
public abstract class GenericServlet implements Servlet {
    
    /** * 重写了Servlet接口的带参的init(ServletConfig config)方法 * 这里有两步操作: 1.加载servletConfig 2.调用自己声明的无参的init()方法 * 但是没有编写init()的具体代码,这么做的目的: * 如果Servlet业务逻辑部分的开发者继承了GenericServelt, * 那么GenericServelt类的带参的init(ServletConfig config)方***帮开发者加载servletConfig, * 于是开发者只需要专心编写业务逻辑而不用关心ServletConfig相关的部分 * 如果开发者需要在Servlet的init阶段进行一些操作的话,可以自己去重写无参的init()方法 */
	public void init(ServletConfig config) throws ServletException {
        // 加载配置
        this.config = config;
        // 在这里调用了无参的init()方法
        this.init();
    }
    
    // GenericServlet声明的 无参的init()方法(内容是空的!)
    public void init() throws ServletException {
    }
}

HttpServlet类 主要是重写了 Servlet 接口的service()方法(可参考 JavaServlet 3.0规范 请求处理方法),

(有关 Servlet生命周期 的知识也可以参考 JavaServlet 3.0 规范

/** * HttpServlet没有去实现 GenericServlet 类中无参的init()方法,所以, * 如果你的类继承了HttpServlet, * 那么重写他的无参init()方法 其实就是在重写GenericServlet的无参init()方法, * 因为HttpServlet没有改变过无参的init() */
public abstract class HttpServlet extends GenericServlet {
    ...
}

HttpServletjavax.servlet.http 包下的类

HttpServletBeanFrameworkServletDispatcherServletorg.springframework.web.servlet 包下的类

很明显,HttpServletSpring 是用 HttpServletBean 的形式来连接的,

或者说, **Spring **通过把 HttpServlet 包装成 HttpServletBean 来方便自己进一步操作

/** * HttpServletBean抽象类 */
public abstract class HttpServletBean extends HttpServlet {
    
    /** * 如上所说,为了在Servlet初始化阶段做一些事情, * HttpServletBean重写了GenericServlet声明的无参的init()方法 */
	public final void init() throws ServletException {
       
        if (!pvs.isEmpty()) {
            // 把当前对象包装成一个BeanWrapper
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            。。。
       	}
        // 调用了 initServletBean()方法
        this.initServletBean();
	}
    
    /** * 声明了一个protected修饰的 initServletBean()方法(没有实现) * 老规矩:用于提供给子类重写 */
    protected void initServletBean() throws ServletException {
    }
}
/** * FrameworkServlet抽象类 */
public abstract class FrameworkServlet extends HttpServletBean {
    
    /** * initServletBean()的实现来啦。。。 */
    protected final void initServletBean() throws ServletException {
        
        long startTime = System.currentTimeMillis();

        this.webApplicationContext = this.initWebApplicationContext();
        this.initFrameworkServlet();

        // 打印日志
        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + this.getServletName() + 
                             "': initialization completed in " + elapsedTime + " ms");
        }

    }
}

FrameworkServlet 打印的日志在这里,似曾相识否 :

[外链图片转存失败(img-hN1VuIjB-1563614868604)(C:\Users\ADMINI~1\AppData\Local\Temp\1561447040159.png)]

/** * DispatcherServlet类 * DispatcherServlet调用到的方法简单梳理 */
public class DispatcherServlet extends FrameworkServlet {
    
    /** * 从HttpServlet继承的 public 修饰的 service()方法,这个方法是我自己补出来的 * 源码并没有显式地写出来,它本身在HttpServlet中,但是父类的就是子类的,所以没什么毛病 */
    public void service(ServletRequest req, ServletResponse res) {
        // 调用下面的 protected修饰的 service()方法
        this.service(request, response);
    }
    
    /** * 从FrameworkServlet继承的 protected 修饰的 service()方法 */
    protected void service(HttpServletRequest request, HttpServletResponse response) {
        
        // 调用父类的service()方法,其实就是HttpServlet的protected servicce()方法
        super.service(request, response);
    }
    
    /** * 这就是上面的super.service()方法,当然,他已经被上面的protected service()方法重写了 * 这里写出来是不正确的,只是为了展示方法的调用流程 */
    protected void service(HttpServletRequest req, HttpServletResponse resp) {
        String method = req.getMethod();
        if (method.equals("GET")) {
            // 在protected HttpServlet.service()中调用了this.doGet()方法
            this.doGet(req, resp);
        }
        。。。
    }
    
    /** * FrameworkServlet类的doGet()方法 * 虽然是在HttpServlet类中调用的this.doGet()方法,但是真正被调用的不是HttpServlet的doGet方法 * 而是子类FrameworkServlet中的doGet()方法,因为在这条调用流程中, * HttpServlet.service()中的this指针代表的是子类(FrameworkServlet)对象,原因在此代码块结束写 */
    protected final void doGet(HttpServletRequest request, HttpServletResponse response) {
        // 在FrameworkServlet中不管是GET还是POST或者其他的方法全部统一到processRequest中处理
        this.processRequest(request, response);
    }
    
    /** * processRequest方法 */
    protected final void processRequest(HttpServletRequest request, 
                                        HttpServletResponse response) {
        // 调用doService()方法
        this.doService(request, response);
    }
    
    /** * 重写了FrameworkServlet中的doService()方法 */
    protected void doService(HttpServletRequest request, HttpServletResponse response) {
        
        // 调用doDispatch方法
        this.doDispatch(request, response);
    }
    
    /** * 核心方法 doDispatch(request, response); * 里面清楚地展示了Interceptor的三个方法的运作时期及其作用原理 */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
        
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain handlerExecutionChain = null;
        ModelAndView mv = null;

        try {
            /** * 根据request 获取HandlerExecutionChain */
            handlerExecutionChain = this.getHandler(processedRequest);

            /** * 执行所有 Interceptor的 preHandle(request, response, handler)方法 */
            if (!handlerExecutionChain.applyPreHandle(processedRequest, response)) {
                // 如果有preHandle的结果是 false,就不执行之后的步骤
                return;
            }
            
            /** * 从handlerExecutionChain中获取Handler,并获取handler对应的适配器(Adapter) */
            HandlerAdapter ha = this.getHandlerAdapter(handlerExecutionChain.getHandler());
            
            /** * 用handler的适配器执行handler, 处理完成后得到 ModelAndView */
            mv = ha.handle(processedRequest, response, handlerExecutionChain.getHandler());
            
            /** * 执行所有 Interceptor的 postHandle(request, response, handler, modelAndView)方法 * 从这里可以看出interceptor中的postHandle()方法是在渲染视图之前调用的 */
            handlerExecutionChain.applyPostHandle(processedRequest, response, mv);
            
            /** * 处理 ModelAndView 对象: 渲染视图 */
            this.processDispatchResult(processedRequest, 
                                       response, 
                                       mappedHandler, 
                                       mv, 
                                       (Exception)dispatchException);
        } finally {
            
            /** * 执行所有 Interceptor的 afterCompletion(request, response, handler, ex)方法 */
            handlerExecutionChain.applyAfterConcurrentHandlingStarted(processedRequest, 
                                                                      response);
        }
    }
}

this.doGet() 为什么调用的是子类的doGet方法

以下是打的断点:

[外链图片转存失败(img-kzUPHKiT-1563614868606)(C:\Users\ADMINI~1\AppData\Local\Temp\1561509975052.png)]

[外链图片转存失败(img-2kaxkT4h-1563614868607)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517372694.png)]

[外链图片转存失败(img-zwshc12v-1563614868608)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517403644.png)]

[外链图片转存失败(img-SxgkemjM-1563614868609)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517426585.png)]

[外链图片转存失败(img-FIVPa2wD-1563614868610)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517447245.png)]

[外链图片转存失败(img-H4bqr1f7-1563614868612)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517527059.png)]

[外链图片转存失败(img-zFqYObef-1563614868613)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517565053.png)]

[外链图片转存失败(img-gsLXtuok-1563614868615)(C:\Users\ADMINI~1\AppData\Local\Temp\1561517587853.png)]

DispatcherServlet处理流程

第一次请求时需要初始化Servlet(init)

14868613)]

[外链图片转存中…(img-gsLXtuok-1563614868615)]

DispatcherServlet处理流程

第一次请求时需要初始化Servlet(init)

全部评论

相关推荐

头像
11-21 11:39
四川大学 Java
是红鸢啊:忘了还没结束,还有字节的5k 违约金
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务