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

        共 30534字,需瀏覽 62分鐘

         ·

        2021-04-06 09:23

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

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

        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實現此功能。每個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄創(chuàng)建一個名為javax.servlet.ServletContainerInitializer的文件,文件內容指定具體的ServletContainerInitializer實現類,那么,當web容器啟動時就會運行這個初始化器做一些組件內的初始化工作。


        2.1 spring-web包

        /**
         *
         * servlet 初始化時,會加載實現WebApplicationInitializer接口的所有類,調取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ā)現該類實現了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)聽器,實現ServletContextListener接口。ServletContextListener接口包含兩個方法,一個是contextInitialized()方法,用來監(jiān)聽ServletContext的啟動和初始化;一個是contextDestroyed()方法,用來監(jiān)聽ServletContext的銷毀。


        3.2 ContextLoaderListener

        描述:在servletContext容器初始化時,調用方法contextInitialized



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

        /**
          * 該方法是Spring容器初始化核心方法,采用模板設計模式。根據不同的上下文對象會調用不同對象子類方法。
          *
          * 核心上下文子類:
          *   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接口的調用
            invokeBeanFactoryPostProcessors(beanFactory);

            //實現了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 子容器初始化

        結論: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é)議,轉載請附上原文出處鏈接及本聲明。

        原文鏈接:

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





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

        ??????

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


        感謝點贊支持下哈 

        瀏覽 54
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
          
          

            1. 1000部毛片免费观看 | 国产欧美日韩在线视频 | 美女操逼91 | heyzo肥臀小早川怜子 | 韩国无码毛片 |