读spring mvc 源码

先看看关键servlet,我的ide用的是eclipse的快捷键,这里是f4:

查看类的结构图快捷键是:ctrl+alt+u  或者右键diagrams->show.

DispatcherServlet的继承结构图

 

 我们先从GenericServlet这个类看起。

1.这个类实现了servlet接口

2.看关键代码

 public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }

 这里init(ServletConfig config)是实现servlet的方法,init()是新建的方法。

  而由于实现的方法里面,调用了init()这个方法,所以后续继承了GenericServlet 这个类的类,只要重写init()方法,即可对servlet的初始化部分进行修改。

 

我们再来看HttpServletBean这个类。

1.看关键代码

public final void init() throws ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
        }

        try {
            PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            this.initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
            throw var4;
        }

        this.initServletBean();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
        }

    }

 这里用final修饰了init()方法(表示方法不可以被重写),那么继承了当前类的servlet,初始化的时候,必须调用这里的init()方法。也就是说,DispatcherServlet初始化的时候,是调用这个方法进行初始化的。

   这里的关键代码是:this.initServletBean();

 

   方法在frameServlet类里重写。

    

protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            this.webApplicationContext = this.initWebApplicationContext();
            this.initFrameworkServlet();
        } catch (ServletException var5) {
            this.logger.error("Context initialization failed", var5);
            throw var5;
        } catch (RuntimeException var6) {
            this.logger.error("Context initialization failed", var6);
            throw var6;
        }

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

    }

 然后看这个方法:initWebApplicationContext

源码:

 1 protected WebApplicationContext initWebApplicationContext() {
 2         WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
 3         WebApplicationContext wac = null;
 4         if (this.webApplicationContext != null) {
 5             wac = this.webApplicationContext;
 6             if (wac instanceof ConfigurableWebApplicationContext) {
 7                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
 8                 if (!cwac.isActive()) {
 9                     if (cwac.getParent() == null) {
10                         cwac.setParent(rootContext);
11                     }
12 
13                     this.configureAndRefreshWebApplicationContext(cwac);
14                 }
15             }
16         }
17 
18         if (wac == null) {
19             wac = this.findWebApplicationContext();
20         }
21 
22         if (wac == null) {
23             wac = this.createWebApplicationContext(rootContext);
24         }
25 
26         if (!this.refreshEventReceived) {
27             this.onRefresh(wac);
28         }
29 
30         if (this.publishContext) {
31             String attrName = this.getServletContextAttributeName();
32             this.getServletContext().setAttribute(attrName, wac);
33             if (this.logger.isDebugEnabled()) {
34                 this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
35             }
36         }
37 
38         return wac;
39     }

 看27行的this.onRefresh(wac);   这个方法在DispatcherServlet类里被重写。

  看代码:

 1 protected void onRefresh(ApplicationContext context) {
 2         this.initStrategies(context);
 3     }
 4 
 5     protected void initStrategies(ApplicationContext context) {
 6         this.initMultipartResolver(context);
 7         this.initLocaleResolver(context);
 8         this.initThemeResolver(context);
 9         this.initHandlerMappings(context);
10         this.initHandlerAdapters(context);
11         this.initHandlerExceptionResolvers(context);
12         this.initRequestToViewNameTranslator(context);
13         this.initViewResolvers(context);
14         this.initFlashMapManager(context);
15     }

 这个refresh里面,有一个initStrategies方法,初始化策略,这里就讲所有处理需要的准备工具准备完毕。

 

前面所有的代码部分都是servlet的init()相关的,也就是DispatcherServlet的初始化过程,这个初始化过程是tomcat容器加载应用的时候进行的。

 

 

 

应用部署完毕,客户端向服务器请求的时候,则tomcat会将用户的请求封装成httpServletRequest,然后找到service()方法进行处理,下面来说说service方法。

service方法在FrameworkServlet重写。

代码:

1  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2         if (HttpMethod.PATCH.matches(request.getMethod())) {
3             this.processRequest(request, response);
4         } else {
5             super.service(request, response);
6         }
7 
8     }


protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
this.initContextHolders(request, localeContext, requestAttributes);

try {
this.doService(request, response);
} catch (ServletException var17) {
failureCause = var17;
throw var17;
} catch (IOException var18) {
failureCause = var18;
throw var18;
} catch (Throwable var19) {
failureCause = var19;
throw new NestedServletException("Request processing failed", var19);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}

if (this.logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", (Throwable)failureCause);
} else if (asyncManager.isConcurrentHandlingStarted()) {
this.logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
}

this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}

}

 

从这里可以看到processRequest方法有调用this.doService(request, response); ,而doService()方法,在DispatcherServlet里面实现。

servlet初始化init()方法只在应用加载的时候,调用一次,而service方法,用户每次请求的时候,都会调用,所以,doService方法,才是关键。

 

 

  

posted @ 2020-01-09 09:26  不加班不熬夜的男子  阅读(189)  评论(0编辑  收藏  举报