SpringMVC源码分析(一)

  SpringMVC的简单实用和配置请查看我的一篇博文 SpringMVC配置实例,今天主要分析一下SpringMVC中的Servlet源码,Serlvet的生命周期和使用方法请查看我的另一篇博文 Servlet学习笔记

1.首先来看一下Servlet接口的源码:

import java.io.IOException;  

public interface Servlet {  
    /** 
     * 被servlet引擎调用用于初始化该Servlet,只有在该方法完成之后,才能响应请求 
     * 如果init方法抛出了异常,则一定不能响应请求。init方法在构造方法之后执行。 
     * Servlet引擎会把ServletConfig对象传进来 
     */  
    public void init(ServletConfig config) throws ServletException;  
    /** 
     * 返回ServletConfig对象,其中包含着初始化信息和初始化参数 
     * @see #init 
     */  
    public ServletConfig getServletConfig();  
    /** 
     * 响应请求,必须在init(Servletconfig config)成功后才能执行 
     */  
    public void service(ServletRequest req, ServletResponse res)  
            throws ServletException, IOException;  
    /**返回基本信息*/  
    public String getServletInfo();  

    /** 
     * 被Servlet引擎调用,用来关闭资源,或者做一些清楚工作 
     */  
    public void destroy();  
}  
  • 所有servlet都要实现的接口
  • Servlet是供Servlet引擎调用的Java类,它相当于Servlet引擎的组件
  • 在Servlet接口中定义了三个方法,也被称为Servlet的声明周期:
  • void init(ServletConfig config):Servlet被初始化时被Servlet引擎调用,只执行一次,且后于构造方法
  • void service(ServletRequet,ServletResponse):响应请求,每当有一个请求,Servlet引擎就调用该方法
  • void destroy():Servlet被销毁时被Servlet引擎调用,用于关闭资源等服务
  • 以上三个方法都是被Servlet引擎(容器)调用
  • ServletConfig getServletConfig:一个ServletConfig对象,在init方法被传递进来,包含了Servlet的初始化信息,我们配置的Springmvc的Servlet时指定配置文件位置的contextConfigLocation的参数就是保存在ServletConfig中。
  • String getServletInfo():返回该Servlet的基本信息

2.GenericServlet源码分析

GenericServlet是Servlet的默认实现,主要完成三件事:
* 实现了ServletConfig接口,我们可以直接调用ServletConfig
* 提供了无参init方法
* 提供了log方法

GenericServlet源码:

package javax.servlet;  
import java.io.IOException;  
import java.util.Enumeration;  

public abstract class GenericServlet implements Servlet, ServletConfig,  
        java.io.Serializable {  
    //销毁方法
    @Override  
    public void destroy() {  
        // NOOP by default  
    }  
    //获取指定参数名的初始化参数,不需要自己调用getServletConfig(),已经默认帮我们调用了,直接用就可以。
    @Override  
    public String getInitParameter(String name) {  
        return getServletConfig().getInitParameter(name);  
    }  
    //获取所有初始化参数,不需要自己调用getServletConfig(),已经默认帮我们调用了,直接用就可以。
    @Override  
    public Enumeration<String> getInitParameterNames() {  
        return getServletConfig().getInitParameterNames();  
    }  
    //获取ServletConfig对象
    @Override  
    public ServletConfig getServletConfig() {  
        return config;  
    }  
    //获取ServletContext对象
    @Override  
    public ServletContext getServletContext() {  
        return getServletConfig().getServletContext();  
    }
    //获取Servlet相关信息,默认为空,我们可以重写  
    @Override  
    public String getServletInfo() {  
        return "";  
    }  

    //Servelt初始化的时候调用,并且已经把ServletContext对象传入,我们不需要自己获取ServletContext对象在传入,并且提供了无参的init方法供调用者重写。
    @Override  
    public void init(ServletContext config) throws ServletException {  
        this.config = config;  
        this.init();  
    }  

    //无参的init方法供调用者重写
    public void init() throws ServletException {  
        // NOOP by default  
    }  

    //输出log
    public void log(String msg) {  
        getServletContext().log(getServletName() + ": " + msg);  
    }  

   //输出log
    public void log(String message, Throwable t) {  
        getServletContext().log(getServletName() + ": " + message, t);  
    }  
    //处理Http请求
    @Override  
    public abstract void service(ServletRequest req, ServletResponse res)  
            throws ServletException, IOException;  
    //获取Servlet的名称
    @Override  
    public String getServletName() {  
        return config.getServletName();  
    }  

3.HttpServlet源码分析:

   HttpServlet是用HTTP协议实现的Servlet的基类并继承了GenericServlet,HttpServlet处理了Http请求,我们就分析一下HttpServlet处理请求的过程,其他的方法和GenericServlet是一样的。

HttpServlet 源码:

public abstract class HttpServlet extends GenericServlet {  

    private static final long serialVersionUID = 1L;  
    //HTTP请求方式
    private static final String METHOD_DELETE = "DELETE";  
    private static final String METHOD_HEAD = "HEAD";  
    private static final String METHOD_GET = "GET";  
    private static final String METHOD_OPTIONS = "OPTIONS";  
    private static final String METHOD_POST = "POST";  
    private static final String METHOD_PUT = "PUT";  
    private static final String METHOD_TRACE = "TRACE";  

    private static final String HEADER_IFMODSINCE = "If-Modified-Since"; 
    //最后一次修改 
    private static final String HEADER_LASTMOD = "Last-Modified";  
、
    .........省略部分代码...........
    //get请求方式会调用此方法来处理
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException  
    {  
        .........
    } 
    /*1. -1:直接响应doget
    *2. 小于返回值:说明请求端的页面已经过期,直接响应,且在响应头当中设置LastModified值
    *3.大于返回值:说明请求端的页面已经是最新,客户端直接使用本地缓存页面,不需要服务端重新生成返回。向响应头当中写入状态码304
    */
    protected long getLastModified(HttpServletRequest req) {  
        return -1;  
    }  
    //Head请求方式会调用此方法
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
        ........
    }  
    //post请求方式调用此方法处理
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
       ..............
    }  
    //put请求方式调用此方法处理
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
       ............
    }  
    //Delete请求方式调用此方法处理
    protected void doDelete(HttpServletRequest req,  
                            HttpServletResponse resp)  
        throws ServletException, IOException {  
        ..............
    }  
   //Option请求方式会调用此方法来处理
    protected void doOptions(HttpServletRequest req,  
            HttpServletResponse resp)  
        throws ServletException, IOException {  
       .......... 
    }  
    //Trace请求方式会调用此方法来处理
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException  
    {  
       ............
    }  
    //主要负责处理请求的方法
    protected void service(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
        String method = req.getMethod(); //获取请求的方式
        if (method.equals(METHOD_GET)) {  
            long lastModified = getLastModified(req);  
            if (lastModified == -1) {  
                // servlet doesn't support if-modified-since, no reason  
                // to go through further expensive logic  
                // -1直接响应doget
                doGet(req, resp);  
            } else {  
                long ifModifiedSince;  
                try {  
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);  
                } catch (IllegalArgumentException iae) {  
                    // Invalid date header - proceed as if none was set  
                    ifModifiedSince = -1;  
                }  
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {  
                    //说明请求端的页面已经过期,直接响应,且在响应头当中设置LastModified值 
                    maybeSetLastModified(resp, lastModified); //设置新的LastModified值
                    doGet(req, resp);  
                } else {  
  resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);  
                }  
            }  

        } else if (method.equals(METHOD_HEAD)) {  //Head请求方式
            long lastModified = getLastModified(req);  
            maybeSetLastModified(resp, lastModified);  
            doHead(req, resp);  //处理Head请求

        } else if (method.equals(METHOD_POST)) {  //Post请求方式
            doPost(req, resp);  //处理POST请求

        } else if (method.equals(METHOD_PUT)) {  //Put请求方式
            doPut(req, resp);  //处理PUT请求

        } else if (method.equals(METHOD_DELETE)) { //Delete请求方式 
            doDelete(req, resp);  //处理DELETE请求

        } else if (method.equals(METHOD_OPTIONS)) {  //Option请求方式
            doOptions(req,resp);  //处理OPTIONS请求

        } else if (method.equals(METHOD_TRACE)) { //Trace请求方式 
            doTrace(req,resp);  //处理TRACE请求

        } else {  
            //不支持的请求方式显示错误
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);  
        }  
    }  
    //把lastModified设置到请求头
    private void maybeSetLastModified(HttpServletResponse resp,  
                                      long lastModified) {  
        if (resp.containsHeader(HEADER_LASTMOD))  
            return;  
        if (lastModified >= 0)  
            resp.setDateHeader(HEADER_LASTMOD, lastModified);  
    }  
    //把request和response转成HttpServletRequest,HttpServletResponse,并调用上面的Service方法
    @Override  
    public void service(ServletRequest req, ServletResponse res)  
        throws ServletException, IOException {  

        HttpServletRequest  request;  
        HttpServletResponse response;  

        try {  
            request = (HttpServletRequest) req;  
            response = (HttpServletResponse) res;  
        } catch (ClassCastException e) {  
            throw new ServletException("non-HTTP request or response");  
        }  
        service(request, response);  
    }  
}  

  由于篇幅的关系每种请求方式具体怎么处理的请求就没有列出来,可以自己分析,我们在使用Servlet的时候切记不要自己在service中根据请求方式调用doGet或doPost方法,我们通过源码发现service会自动根据请求的方式调用相应的处理方法,我们只需要重写doPost,doGet等等方法就可以。

3.HttpServletBean源码分析
  HttpServletBean直接实现了EnviromentCapable和EnvironmentAware接口,
xxxAware在Spring中表示对xxx可以感知,换句话说就是如果在某个类里面想要Spring的一些东西就可以通过实现xxxAware告诉Spring,Spring感知后会吧需要的东西送过来。EnviromentCapable就是提供Environment的能力。
HttpServletBean源码:

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
....省略部分源码.........
    //初始化的时候调用
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }
        //把Servlet中配置的参数封装到pvs中,requiredProperties为必须参数,如果没有配置会报异常。
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                //BeanWrapper 是Spring提供用来操作JavaBean属性的一个工具,可以使用它直接修改一个对象的属性。这里操作的javaBean是DispatcherServlet
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                //模板方法在子类中实现,做一些初始化操作
                initBeanWrapper(bw);
                //将配置初始化
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }
        // 模板方法在FrameworkSerlvet中实现
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }
    //空实现
    protected void initBeanWrapper(BeanWrapper bw) throws BeansException {

    }
    // 模板方法在FrameworkSerlvet中实现
    protected void initServletBean() throws ServletException {
    }

}

  通过源码发现HttpServletBean的主要作用就是将Servlet中配置的参数通过BeanWrapper设置到DispatcherServlet的相关属性。然后调用initServletBean方法,子类通过这个方法初始化(比如FrameWorkServlet)。

4.FrameWorkServlet源码分析
  FrameWorkServlet继承HttpServletBean调用initServletBean()方法进行初始化。
FrameWorkServlet源码:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    @Override
    protected final void initServletBean() throws ServletException {

        long startTime = System.currentTimeMillis();

        try {
            //获得WebApplicationContext对象
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();//初始化FrameworkServlet
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
    //初始化WebApplicationContext对象    
    protected WebApplicationContext initWebApplicationContext() {
        //获取Spring的根容器rootContext
        WebApplicationContext rootContext =     WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {

            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {

                    if (cwac.getParent() == null) {

                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            //如果WebApplicationContext对象存在则去查找,通过Servlet的contextAttribute参数获取
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            //如果WebApplicationContext对象不存在则去创建一个,createWebApplicationContext方法会将设置的contextConfigLocation参数传给wac,默认为web-INFO/[servletName]-Servlet.xml
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            onRefresh(wac);
        }

        if (this.publishContext) {
            //把WebApplicationContext设置到ServletContext中
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            }
        }
        return wac;
    }
    }

关于ServletContext和WebApplicationContext的区别和关系:
  首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取。

DispatcherServlet源码分析:

DispatcherServlet继承FrameWorkServlet,onRefresh方法是他的入口方法,onRefresh()方法调用了initStrategies方法初始化SpringMVC9大组件。
DispatcherServlet源码:

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);//初始化文件上传解析器
        initLocaleResolver(context);//本地化解析器
        initThemeResolver(context);//主题解析器
        initHandlerMappings(context);//HandlerMapping
        initHandlerAdapters(context);//HandlerAdapter
        initHandlerExceptionResolvers(context);//HandlerExceptionResolver异常解析器
    initRequestToViewNameTranslator(context);//RequestToViewNameTranslator可以在处理器返回的View为空时使用它根据Request获取viewName。RequestToViewNameTranslator提供的实现类只有一个DefaultRequestToViewNameTranslator,提供了getViewName抽象方法,其实就是根据request请求获取来组装视图名称。
        initViewResolvers(context);//初始化视图解析器
        initFlashMapManager(context);//初始化FlashMapManager,flashMap用于redirect重定向传递数据
    }
}

DispatcherServlet的转发请求的过程源码在后续的SpringMVC请求处理流程中分析SpringMVC源码分析(二)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值