<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringMVC 入口及父子容器源碼解析

          共 30534字,需瀏覽 62分鐘

           ·

          2021-04-06 09:23

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          76套java從入門到精通實戰(zhàn)課程分享

          1 工程簡介

          描述:web工程


          1.1 pom

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>

              <packaging>war</packaging>
              <groupId>org.example</groupId>
              <artifactId>rosh-spring</artifactId>
              <version>1.0-SNAPSHOT</version>

              <properties>
                  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                  <maven.compiler.source>1.8</maven.compiler.source>
                  <maven.compiler.target>1.8</maven.compiler.target>
                  <spring.version>5.2.8.RELEASE</spring.version>
              </properties>

              <dependencies>
                  <!--上下文-->
                  <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-context</artifactId>
                      <version>${spring.version}</version>
                  </dependency>
                  <!--切面-->
                  <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-aspects</artifactId>
                      <version>${spring.version}</version>
                  </dependency>
                  <!--事務-->
                  <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-tx</artifactId>
                      <version>${spring.version}</version>
                  </dependency>
                  <!--spring-mvc-->
                  <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-webmvc</artifactId>
                      <version>${spring.version}</version>
                  </dependency>
                  <!--jdbc-->
                  <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-jdbc</artifactId>
                      <version>${spring.version}</version>
                  </dependency>
                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                      <version>1.16.20</version>
                  </dependency>
                  <!--servlet-->
                  <dependency>
                      <groupId>javax.servlet</groupId>
                      <artifactId>javax.servlet-api</artifactId>
                      <version>3.1.0</version>
                      <scope>provided</scope>
                  </dependency>
                  <!-- 日志相關依賴 -->
                  <dependency>
                      <groupId>org.slf4j</groupId>
                      <artifactId>slf4j-api</artifactId>
                      <version>1.7.10</version>
                  </dependency>
                  <dependency>
                      <groupId>ch.qos.logback</groupId>
                      <artifactId>logback-classic</artifactId>
                      <version>1.1.2</version>
                  </dependency>
                  <dependency>
                      <groupId>ch.qos.logback</groupId>
                      <artifactId>logback-core</artifactId>
                      <version>1.1.2</version>
                  </dependency>
                  <!--測試-->
                  <dependency>
                      <groupId>junit</groupId>
                      <artifactId>junit</artifactId>
                      <version>4.13</version>
                  </dependency>
              </dependencies>


          </project>

          1.2 配置文件

          public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
              /**
               *  父容器配置文件
               */
              @Override
              protected Class<?>[] getRootConfigClasses() {
                  System.out.println("RoshWebInitializer invoke getRootConfigClasses");
                  return new Class<?>[]{SpringRootConfig.class};
              }
              /**
               * 子容器配置
               */
              @Override
              protected Class<?>[] getServletConfigClasses() {
                  System.out.println("RoshWebInitializer invoke getServletConfigClasses");
                  return new Class<?>[]{ SpringServletConfig.class};
              }
              /**
               *攔截請求(靜態(tài)資源、js、css.......)
               */
              @Override
              protected String[] getServletMappings() {
                  return new String[]{"/"};
              }
          }


          /**
           * 父容器配置文件,只掃描service
           */
          @Configuration
          @ComponentScan(value = "com.rosh.service")
          public class SpringRootConfig {

          }


          /**
           *  子容器配置文件,僅僅掃描@Controller、@RestController
           */
          @Configuration
          @ComponentScan(value="com.rosh",includeFilters={
                  @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
          },useDefaultFilters=false)
          @EnableWebMvc
          public class SpringServletConfig {

          }


          1.3 HelloController

          @RestController
          @RequestMapping("/hello")
          public class HelloController {


              @Autowired
              private HelloService helloService;


              @GetMapping("")
              public String printHello() {

                  return helloService.printHello();
              }


          }

          1.4 HelloService

          @Service
          public class HelloService {


              public String printHello() {

                  return "Hello World";
              }

          }


          1.5 啟動tomcat


          2 servlet3.0規(guī)范

          在web容器啟動時為提供給第三方組件機會做一些初始化的工作,servlet規(guī)范(JSR356)中通過ServletContainerInitializer實現(xiàn)此功能。每個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄創(chuàng)建一個名為javax.servlet.ServletContainerInitializer的文件,文件內(nèi)容指定具體的ServletContainerInitializer實現(xiàn)類,那么,當web容器啟動時就會運行這個初始化器做一些組件內(nèi)的初始化工作。


          2.1 spring-web包

          /**
           *
           * servlet 初始化時,會加載實現(xiàn)WebApplicationInitializer接口的所有類,調(diào)取onStartup方法。
           *
           */
          @HandlesTypes(WebApplicationInitializer.class)
          public class SpringServletContainerInitializer implements ServletContainerInitializer {

           /**
            * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
            * implementations present on the application classpath.
            * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
            * Servlet 3.0+ containers will automatically scan the classpath for implementations
            * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
            * such types to the {@code webAppInitializerClasses} parameter of this method.
            * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
            * this method is effectively a no-op. An INFO-level log message will be issued notifying
            * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
            * no {@code WebApplicationInitializer} implementations were found.
            * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
            * they will be instantiated (and <em>sorted</em> if the @{@link
            * org.springframework.core.annotation.Order @Order} annotation is present or
            * the {@link org.springframework.core.Ordered Ordered} interface has been
            * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
            * method will be invoked on each instance, delegating the {@code ServletContext} such
            * that each instance may register and configure servlets such as Spring'
          s
            * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
            * or any other Servlet API componentry such as filters.
            * @param webAppInitializerClasses all implementations of
            * {@link WebApplicationInitializer} found on the application classpath
            * @param servletContext the servlet context to be initialized
            * @see WebApplicationInitializer#onStartup(ServletContext)
            * @see AnnotationAwareOrderComparator
            */
           @Override
           public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
             throws ServletException {

            List<WebApplicationInitializer> initializers = new LinkedList<>();

            if (webAppInitializerClasses != null) {
             for (Class<?> waiClass : webAppInitializerClasses) {
              // Be defensive: Some servlet containers provide us with invalid classes,
              // no matter what @HandlesTypes says...
              if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
               try {
                initializers.add((WebApplicationInitializer)
                  ReflectionUtils.accessibleConstructor(waiClass).newInstance());
               }
               catch (Throwable ex) {
                throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
               }
              }
             }
            }

            if (initializers.isEmpty()) {
             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
             return;
            }

            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            for (WebApplicationInitializer initializer : initializers) {
             initializer.onStartup(servletContext);
            }
           }

          }

          2.2 RoshWebInitializer

          描述:查看RoshWebInitializer類圖,發(fā)現(xiàn)該類實現(xiàn)了WebApplicationInitializer接口,所以當servlet容器啟動時,會加載該配置文件,并且執(zhí)行onStartup方法。

          public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
              /**
               *  父容器配置文件
               */
              @Override
              protected Class<?>[] getRootConfigClasses() {
                  System.out.println("RoshWebInitializer invoke getRootConfigClasses");
                  return new Class<?>[]{SpringRootConfig.class};
              }
              /**
               * 子容器配置
               */
              @Override
              protected Class<?>[] getServletConfigClasses() {
                  System.out.println("RoshWebInitializer invoke getServletConfigClasses");
                  return new Class<?>[]{ SpringServletConfig.class};
              }
              /**
               *攔截請求(靜態(tài)資源、js、css.......)
               */
              @Override
              protected String[] getServletMappings() {
                  return new String[]{"/"};
              }
          }

          3 父容器源碼解析

          描述:打斷點,debug。



          3.1 創(chuàng)建父容器

          描述:創(chuàng)建父容器(AnnotationConfigWebApplicationContext)、創(chuàng)建監(jiān)聽器。

           protected void registerContextLoaderListener(ServletContext servletContext) {

            /**
             * 創(chuàng)建上下文
             */
            WebApplicationContext rootAppContext = createRootApplicationContext();
            if (rootAppContext != null) {
             /**
              *創(chuàng)建監(jiān)聽器
              */
             ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
             listener.setContextInitializers(getRootApplicationContextInitializers());

             /**
              * 把listener加入到上下文
              */
             servletContext.addListener(listener);
            }
            else {
             logger.debug("No ContextLoaderListener registered, as " +
               "createRootApplicationContext() did not return an application context");
            }
           }

          描述:創(chuàng)建父容器,直接new AnnotationConfigWebApplicationContext,加載父容器配置文件,然后返回。

          @Override
           @Nullable
           protected WebApplicationContext createRootApplicationContext() {
            
            /**
             * 獲取父容器配置文件
             */   
            Class<?>[] configClasses = getRootConfigClasses();
            if (!ObjectUtils.isEmpty(configClasses)) {
             
             /**
              * 創(chuàng)建父容器設置父容器配置文件
              */   
             AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
             context.register(configClasses);
             return context;
            }
            else {
             return null;
            }
           }


          3.2 父容器初始化

          3.2.1 監(jiān)聽器

          描述:ContextLoaderListener 監(jiān)聽器,實現(xiàn)ServletContextListener接口。ServletContextListener接口包含兩個方法,一個是contextInitialized()方法,用來監(jiān)聽ServletContext的啟動和初始化;一個是contextDestroyed()方法,用來監(jiān)聽ServletContext的銷毀。


          3.2 ContextLoaderListener

          描述:在servletContext容器初始化時,調(diào)用方法contextInitialized



          描述:ioc容器初始化,可以看前面幾篇文章。

          /**
            * 該方法是Spring容器初始化核心方法,采用模板設計模式。根據(jù)不同的上下文對象會調(diào)用不同對象子類方法。
            *
            * 核心上下文子類:
            *   AbstractXmlApplicationContext(XML上下文)
            *   AnnotationConfigApplicationContext(注解上下文)
            *
            *
            */
           @Override
           public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {

             //容器初始化
             prepareRefresh();

             /**
              * AbstractXmlApplicationContext:
              *
              * (1) 創(chuàng)建BeanFacotry
              * (2) 解析Xml
              */
             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

             // 給beanFactory設置一些值
             prepareBeanFactory(beanFactory);

             try {
              // Allows post-processing of the bean factory in context subclasses.
              postProcessBeanFactory(beanFactory);

              //完成對BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口的調(diào)用
              invokeBeanFactoryPostProcessors(beanFactory);

              //實現(xiàn)了BeanPostProcessor接口的實例化,并且加入到BeanFactory中
              registerBeanPostProcessors(beanFactory);

              // 國際化
              initMessageSource();

              // Initialize event multicaster for this context.
              //初始化事件管理器
              initApplicationEventMulticaster();

              // 鉤子方法
              onRefresh();

              // Check for listener beans and register them.
              registerListeners();

              // Instantiate all remaining (non-lazy-init) singletons.
              /**
               *
               * bean的實例化、IOC
               *
               */
              finishBeanFactoryInitialization(beanFactory);

              // Last step: publish corresponding event.
              /**
               * publicsh event 事件
               */
              finishRefresh();
             } catch (BeansException ex) {
              if (logger.isWarnEnabled()) {
               logger.warn("Exception encountered during context initialization - " +
                 "cancelling refresh attempt: " + ex);
              }

              // Destroy already created singletons to avoid dangling resources.
              destroyBeans();

              // Reset 'active' flag.
              cancelRefresh(ex);

              // Propagate exception to caller.
              throw ex;
             } finally {
              // Reset common introspection caches in Spring's core, since we
              // might not ever need metadata for singleton beans anymore...
              resetCommonCaches();
             }
            }
           }


          3.3 子容器初始化


          protected void registerDispatcherServlet(ServletContext servletContext) {
            String servletName = getServletName();
            Assert.hasLength(servletName, "getServletName() must not return null or empty");

            /**
             * 【1】 創(chuàng)建mvc上下文
             */
            WebApplicationContext servletAppContext = createServletApplicationContext();
            Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

            /**
             * 【2】 創(chuàng)建DispatcherServlet對象,把springmvc上下文設置到dispatcherServlet中
             */
            FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
            dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

            /**
             * 【3】 把DispatcherServlet加入到servlet上下文中
             */
            ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
            if (registration == null) {
             throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
               "Check if there is another servlet registered under the same name.");
            }

            registration.setLoadOnStartup(1);
            //攔截器
            registration.addMapping(getServletMappings());
            registration.setAsyncSupported(isAsyncSupported());

            //過濾器
            Filter[] filters = getServletFilters();
            if (!ObjectUtils.isEmpty(filters)) {
             for (Filter filter : filters) {
              registerServletFilter(servletContext, filter);
             }
            }

            customizeRegistration(registration);
           }

          3.3.1 創(chuàng)建子容器

          描述:和父容器一樣,獲取子容器配置文件,創(chuàng)建AnnotationConfigWebApplicationContext,設置配置文件,返回。


          3.3.2 子容器初始化

          結(jié)論:DispatcherServlet繼承了HttpServlet,子容器的初始化在servlet init方法中




          3.3.3 設置父子容器


          protected WebApplicationContext initWebApplicationContext() {

            /**
             * 【1】 從servletContext中獲取父容器
             */
            WebApplicationContext rootContext =
              WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;

            if (this.webApplicationContext != null) {
             // A context instance was injected at construction time -> use it

             /**
              * 【2】 獲取springMvc 容器,子容器
              */
             wac = this.webApplicationContext;
             if (wac instanceof ConfigurableWebApplicationContext) {
              ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
              if (!cwac.isActive()) {
               // The context has not yet been refreshed -> provide services such as
               // setting the parent context, setting the application context id, etc
               if (cwac.getParent() == null) {
                // The context instance was injected without an explicit parent -> set
                // the root application context (if any; may be null) as the parent
                /**
                 *【3】 設置父子關系
                 */
                cwac.setParent(rootContext);
               }
               /**
                * 【4】 啟動容器
                */
               configureAndRefreshWebApplicationContext(cwac);
              }
             }
            }
            if (wac == null) {
             // No context instance was injected at construction time -> see if one
             // has been registered in the servlet context. If one exists, it is assumed
             // that the parent context (if any) has already been set and that the
             // user has performed any initialization such as setting the context id
             wac = findWebApplicationContext();
            }
            if (wac == null) {
             // No context instance is defined for this servlet -> create a local one
             wac = createWebApplicationContext(rootContext);
            }

            if (!this.refreshEventReceived) {
             // Either the context is not a ConfigurableApplicationContext with refresh
             // support or the context injected at construction time had already been
             // refreshed -> trigger initial onRefresh manually here.
             synchronized (this.onRefreshMonitor) {
              onRefresh(wac);
             }
            }

            if (this.publishContext) {
             // Publish the context as a servlet context attribute.
             String attrName = getServletContextAttributeName();
             getServletContext().setAttribute(attrName, wac);
            }

            return wac;
           }


          3.3.4 初始化自容器其它屬性

          描述:當子容器完成bean的創(chuàng)建后,會觸發(fā)publish事件初始化spring mvc 相關屬性。



          ————————————————

          版權聲明:本文為CSDN博主「你攜秋月攬星河丶」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/qq_34125999/article/details/115273102





          粉絲福利:Java從入門到入土學習路線圖

          ??????

          ??長按上方微信二維碼 2 秒


          感謝點贊支持下哈 

          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品麻豆传媒 | 日爽夜爽 | 夜夜撸日日 | 成人日韩AV电影 | 免费毛片a在线看 |