SpringBoot底层


SpringBoot

脚手架

自动装配

通过@Import导入AutoConfigurationImportSelector.class 实现DeffredImportSelector,然后在process方法扫描所有jar包中的spring.factories,
把扫描到的完整包名的字符串放到list里面,然后排序返回给spring
其中使用DeffredImportSelector是必要的,它在@bean,@Component等加载完成后加载,spring加载前会判断是否加载了这个bean,有才加载(conditionalOnBean)所以先来的先加载,DeffredImportSelector最后加载保证用户自定义的先加载

编写starter

编写装配类

  • 自动装配类
    @Configuration
    // 让ConfigurationProperties注解生效,
    @EnableConfigurationProperties(IndexProperties.class)
    // 配置文件配置了com.zwq.index.words这个属性,才让这个配置类生效
    @ConditionalOnProperty("com.zwq.index.words")
    public class IndexAutoConfiguration {
        @Autowired
        IndexProperties indexProperties;
        @Bean
        public IndexController indexController() {
            return new IndexController(indexProperties);
        }
    }
  • 配置文件读取到类
    @ConfigurationProperties(prefix = "com.zwq.index")
    public class IndexProperties {
        private String words;
        public String getWords() {
            return words;
        }
        public void setWords(String words) {
            this.words = words;
        }
    }
  • 配个接口
    @RestController
    public class IndexController {
    
        IndexProperties indexProperties;
        public IndexController(IndexProperties indexProperties) {
            this.indexProperties = indexProperties;
        }
        @RequestMapping("/")
        public String index() {
            return indexProperties.getWords();
        }
    }

编写spring.factories

在resources下新建META-INF文件夹 -> spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.zwq.test.IndexAutoConfiguration

引用

在其它项目引用

<dependency>
       <groupId>com.zwq</groupId>
       <artifactId>index-stater</artifactId>
       <version>0.0.1-SNAPSHOT</version>
   </dependency>

启动流程

  • new SpringApplication(初始化一些信息)

    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 启动类放入primarySources
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // 根据classpath,推算当前应用类型是servle还是webFlux
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 获取引导器 去spring.factories 文件中找 org.springframework.boot.Bootstrapper
    this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
    // 去spring.factories获取所有key(ApplicationContextInitializer)对外扩展
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 去spring.factories获取所有key(ApplicationListener)监听器,解耦,例:读取全局配置文件
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    // 根据main推算所在类
    this.mainApplicationClass = this.deduceMainApplicationClass();
    
    1. 获取启动类,根据启动类加载IOC
    2. 获取web应用类型
    3. 在spring.factories读取对外扩展的ApplicationContextInitializer,ApplicationListener扩展,解耦(全局配置文件,热部署插件)
    4. 根据main推算所在类
  • run

    // 记录开始时间
    StopWatch stopWatch = new StopWatch();
       stopWatch.start();
    // 创建引导上下文环境
       DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    // 任何spring上下文接口
       ConfigurableApplicationContext context = null;
    // 开启HeadLess模式
       this.configureHeadlessProperty();
    // 用来发布事件,运行监听器
       SpringApplicationRunListeners listeners = this.getRunListeners(args);
    // 发布事件:容器加载开始
       listeners.starting(bootstrapContext, this.mainApplicationClass);
       try {
    	// 封装命令行参数ApplicationArguments
           ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    	// 读取环境配置信息,例:读取全局配置文件
           ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    	// 忽略beaninfo的bean
           this.configureIgnoreBeanInfo(environment);
           Banner printedBanner = this.printBanner(environment);
    	// 根据webApplicationType创建Spring上下文
           context = this.createApplicationContext();
           context.setApplicationStartup(this.applicationStartup);
    	// 预初始化spring上下文(读取注册配置类)
           this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    	// 同spring的refresh方法:加载ioc容器,注册bean定义,加载bean,这里在onRefresh创建了servlet容器  
           this.refreshContext(context);
    	// 留给用户自定义容器刷新完成后的处理逻辑
           this.afterRefresh(context, applicationArguments);
           stopWatch.stop();
           if (this.logStartupInfo) {
               (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
           }
           listeners.started(context);
           this.callRunners(context, applicationArguments);
       } catch (Throwable var10) {
           this.handleRunFailure(context, var10, listeners);
           throw new IllegalStateException(var10);
       }
  • 内嵌Tomcat

    this.refreshContext(context); ->
    this.onRefresh(); -> 
    this.createWebServer();
    // 去容器获取ServletWebServerFactory.class bean
    ServletWebServerFactory factory = this.getWebServerFactory();
    // new tomcat 启动tomcat 挂起tomcat
    this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
    // 回调getSelfInitializer
    	private void selfInitialize(ServletContext servletContext) throws ServletException {
            this.prepareWebApplicationContext(servletContext);
            this.registerApplicationScope(servletContext);
            WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
    		// 能够拿到DispatcherServlet
            Iterator var2 = this.getServletContextInitializerBeans().iterator();
    
            while(var2.hasNext()) {
                ServletContextInitializer beans = (ServletContextInitializer)var2.next();
    			// 注册servlet
                beans.onStartup(servletContext);
            }
    
        }

    this.createWebServer():
    判断servletContext是否存在,不存在就说明使用内嵌servlet容器,就去创建servlet容器

  • 外置Tomcat

  • SPI:Service Provider Interface,服务发现机制,在ClassPath的META-INF/services查找文件,自动加载文件里定义的类

打包

当使用spring-boot-maven-plugin打包的时候,
会把jar包打好,生成一个MANIFEST.MF
运行java -jar会找manifest文件,找到启动类Main-Class,使用JarLauncher自定义加载器去加载jar文件,实现fat jar(jar里面放着jar)启动
之后拿到Start-Class属性值:springboot启动类,然后通过反射执行main方法


  目录