Spring MVC源码剖析 一、DispatcherServlet初始化源码剖析

    xiaoxiao2022-07-14  168

      使用Spring MVC的时候,每次都要在web.xml中初始化一个DispatcherServlet,这是为什么呢?因为我们需要DispatcherServlet来将Spring容器启动起来,启动完成后,由DispatcherServlet来接收请求并分发。

      首先。DispatcherServlet 继承 FrameworkServlet,FrameworkServlet 继承 HttpServletBean ,HttpServletBean 继承 HttpServlet ,即原生的 HttpServlet 。

      我们下面看一下DispatcherServlet的初始化过程:

      系统new了 DispatcherServlet 了之后,会调用其init() 方法,即 HttpServletBean 的init方法:

    @Override public final void init() throws ServletException { // 从配置文件中读取出配置,比如在web.xml中读取的 name为contextConfigLocation,value为 classpath*:servlet-context.xml try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 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) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // 此方法非常非常重要,初始化容器,与web中的相关映射。 initServletBean(); }

    继续看 FrameworkServlet中的 initServletBean() 方法:

    protected final void initServletBean() throws ServletException { long startTime = System.currentTimeMillis(); try { // 此方法非常重要,初始化了一个 ApplicationContext 容器,完成了beanDefinition的生成,根据beanDefinition生成bean的操作 // 在bean生成完成之后,此方法还会初始化 mvc 中的 HanderMapping、handerAdapter等一系列组件 this.webApplicationContext = initWebApplicationContext(); } . . . if (this.logger.isInfoEnabled()) { // 还会计时,我们平常的计时就在这里来完成的 long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }

       再看一下 initWebApplicationContext() 方法:

    protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; . . . if (wac == null) { // 此为创建一个 WebApplicationContext ,并调用其 refresh() 方法来生成容器 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 此为调用自身的 onRefresh() 方法,来初始化 mvc 中的 HanderMapping、handerAdapter等一系列组件 onRefresh(wac); } . . . return wac; }

      首先看一下 createWebApplicationContext() ,看其实如何来生成一个 容器 的:

    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { // 先使用 BeanUtils 生成了一个 ApplicationContext,注意我们生成的是 XmlWebApplicationContext ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); // 此为将 context 文件的路径注入进去,比如我们之前使用的xmlClassPathApplicationContext,就需要一个 xml 文件的路径,现在也是一样的,通过这个将其注入进去 wac.setConfigLocation(getContextConfigLocation()); // 最后调用方法将其初始化完成 configureAndRefreshWebApplicationContext(wac); return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { . . . wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); // 注意,最后非常重要!调用了 applicationContxt的 refresh() 方法,先根据ConfigLocation得到Resource,在根据Resource生成beanDefinition,最后根据 beanDefinition 生成bean。 wac.refresh(); }

      至此,Spring容器启动。

     

     

    最新回复(0)