zoukankan      html  css  js  c++  java
  • 【Spring】原来SpringBoot是这样玩的

    菜瓜:我自己去调Mvc的源码差点没给Spring的逻辑秀死。。。难受

    水稻:那今天咱们看一个简单易用的SpringBoot吧

    菜瓜:可以,这个我熟悉

    水稻:嘿嘿嘿。平时工作中用多了SpringBoot。咱们今天带着几个问题来看看它的操作吧

    1. 如何启动Spring容器
    2. 如何内嵌Tomcat容器
    3. 如何完成自动装配,就是0配置

    菜瓜:???小问号,你是否有很多朋友

    水稻:。。。看过来

    • 启动类点进去
    • public ConfigurableApplicationContext run(String... args) {
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         ...
         listeners.starting();
         try {
            ...
            // ①创建Spring上下文容器对象 - 默认Servlet容器
            context = createApplicationContext();
            ...
            // ②调用refresh方法 - 回到熟悉的容器启动流程
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            ...   
         ...
         return context;
      }
    • ① 创建上下文对象
      • protected ConfigurableApplicationContext createApplicationContext() {
           Class<?> contextClass = this.applicationContextClass;
           if (contextClass == null) {
              try {
                 switch (this.webApplicationType) {
                 case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                 case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                 default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                 }
              }
              catch (ClassNotFoundException ex) {
                 ...
              }
           }
           return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    • ②启动容器
      •     @Override
            public void refresh() throws BeansException, IllegalStateException {
                synchronized (this.startupShutdownMonitor) {
                    prepareRefresh();
                    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
                    // Prepare the bean factory for use in this context.
                    prepareBeanFactory(beanFactory);
        
                    try {
                        ...
                        // ①springboot 内嵌tomcat容器
                        onRefresh();
                        ...
        
            }
        
            @Override
            protected void onRefresh() {
                super.onRefresh();
                try {
              // ②创建Servlet容器 默认tomcat
                    createWebServer();
                }
                ...
            }      
             
              
            private void createWebServer() {
                WebServer webServer = this.webServer;
                ServletContext servletContext = getServletContext();
                if (webServer == null && servletContext == null) {
                    ServletWebServerFactory factory = getWebServerFactory();
              // ③看进去 回到mvc集成tomcat的场景
                    this.webServer = factory.getWebServer(getSelfInitializer());
                }
                ...
                initPropertySources();
            }      
                 
            @Override
            public WebServer getWebServer(ServletContextInitializer... initializers) {
                Tomcat tomcat = new Tomcat();
                File baseDir = (this.baseDirectory != null) ? this.baseDirectory
                        : createTempDir("tomcat");
                tomcat.setBaseDir(baseDir.getAbsolutePath());
                Connector connector = new Connector(this.protocol);
                tomcat.getService().addConnector(connector);
                customizeConnector(connector);
                tomcat.setConnector(connector);
                tomcat.getHost().setAutoDeploy(false);
                configureEngine(tomcat.getEngine());
                for (Connector additionalConnector : this.additionalTomcatConnectors) {
                    tomcat.getService().addConnector(additionalConnector);
                }
                prepareContext(tomcat.getHost(), initializers);
                return getTomcatWebServer(tomcat);
            }      

    水稻:好了,第一步和第二步完成了

    菜瓜:就这???

    水稻:是不是极其简单,令人发指。重头戏是后面的自动装配

    • 回到咱们启动类的注解上
    • ...
      // 标记自身被扫描  
      @SpringBootConfiguration
      // 下一步 - 自动装配入口 
      @EnableAutoConfiguration
      // 扫描bean路径 - 约定是启动类所在的包:所以没事别把启动类挪走(都是泪)
      @ComponentScan(excludeFilters = {
              @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
              @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      public @interface SpringBootApplication 
        
      ->   
      @Import(AutoConfigurationImportSelector.class)
      public @interface EnableAutoConfiguration 
        
      -> 重要
      public class AutoConfigurationImportSelector ... {
        @Override
        public void process(AnnotationMetadata annotationMetadata,
              DeferredImportSelector deferredImportSelector) {
           ...
           // 获取以EnableAutoConfiguration命名的/META-INF/Spring.factories文件中的value去重 
           AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(),            annotationMetadata);
           // 启动的时候断点可以看到
      this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } }
    • AutoConfigurationImportSelector 中的process是被ConfigurationClassPostProcessor通过processConfigBeanDefinitions方法调用(调用链如下)
      1. this.processConfigBeanDefinitions(registry);
      2. parser.parse(candidates);
      3. this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
      4. sourceClass = this.doProcessConfigurationClass(configClass, sourceClass);
      5. this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
      6. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);  
      7. handler.processGroupImports();
      8. grouping.getImports().forEach...
      9. this.group.process(...);
      -- 
      搜集到需要自动装配的类,封装成BeanDefinition后续实例化,实现自动装配功能
      譬如引入WebMvcAutoConfiguration类 - webmvc功能自动集成
      org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration 

    菜瓜:原来如此。你把调用链拎出来就简单了很多。自动装配就是通过SPI加载org.springframework.boot.autoconfigure包下的class,封装成BeanDefinition后交给容器加载

    水稻:所以,所谓自动装配,就是通过一个工具包,将很多默认的配置给引入了进来。妈妈再也不用担心我记不住复杂的配置了

    总结:SpringBoot只需要一行代码便能启动一个Java应用。完全解放开发者复杂的配置

    • 内嵌Servlet容器,默认tomcat
    • 启动SpringWeb容器
    • 自动装配了简单web应用需要的工具和组建
  • 相关阅读:
    团队冲刺1 5.2
    梦断代码03
    梦断代码02
    梦断代码01
    软工超越日报-paddle目标检测 数据集标注(1) 5/23
    软工超越日报-paddle目标检测 数据清洗(3) 5/22
    软工超越日报-paddle目标检测 数据清洗(2) 5/21
    软工超越日报-paddle目标检测 数据清洗(1) 5/20
    软工超越日报-paddleYOLO预测——数据集图片爬取 5/19
    软工超越日报-团队绩效评分 5/18
  • 原文地址:https://www.cnblogs.com/nightOfStreet/p/13211045.html
Copyright © 2011-2022 走看看