西部开源-秦疆老师:基于SpringBoot 2.1.6 的博客教程

秦老师交流Q群号: 664386224

未授权禁止转载!编辑不易 , 转发请注明出处!防君子不防小人,共勉!

SpringBoot:运行原理探究

pom.xml

我们之前写的HelloSpring,到底是怎么运行的呢

我们来看pom.xml文件

进入父项目,这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot-boot-starter:就是spring-boot的场景启动器

这里的 spring-boot-starter-web 帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;

主程序

package com.kuang.springbootdemo02;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication 来标注一个主程序类 , 说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {
        //将Spring应用启动起来
        SpringApplication.run(SpringbootDemo02Application.class, args);
    }

}

一个简单的启动类并不简单!

SpringBootApplication

@SpringBootApplication :SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;进入这个注解:



 @ComponentScan

这个注解在Spring中很重要 , 它对应XML配置中的元素。@ComponentScan的功能就是自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中 ;

@SpringBootConfiguration

SpringBoot的配置类 ;标注在某个类上 , 表示这是一个SpringBoot的配置类;我们继续进去这个注解查看

@Configuration:配置类上来标注这个注解 ,配置类-----配置文件;我们继续点进去,发现配置类也是容器中的一个组件。@Component

EnableAutoConfiguration

我们回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration :开启自动配置功能

以前我们需要配置的东西,SpringBoot可以自动帮我们配置 ; @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

我们点击去查看

@AutoConfigurationPackage : 自动配置包 , 点进去看到一个 @Import({Registrar.class})

@import :Spring底层注解@import , 给容器中导入一个组件 ,导入的组件由 {Registrar.class} 将主配置类 【即@SpringBootApplication标注的类】的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

然后回来继续看:@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

AutoConfigurationImportSelector : 导入哪些组件的选择器 ;

它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;

它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

//AutoConfigurationImportSelector中获取配置文件信息;
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),         getBeanClassLoader());   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "         + "are using a custom packaging, make sure that file is correct.");   return configurations;}

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguation.class,classLoader);

这里的getSpringFactoriesLoaderFactoryClass()方法返回的就是我们最开始看的启动自动导入配置文件的注解类;

进入 SpringFactoriesLoader 查看;

找到对应的方法:发现最终会去读取一个配置文件 : META-INF/Spring.factories 的文件 。

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

我们打开spring.factories的配置文件 , 看到了很多自动配置的文件;这就是自动配置根源所在!

SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

以前我们需要自己配置的东西 , 自动配置类都帮我们解决了

整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

我们找一个打开看看 : WebMvcAutoConfiguration

 

所以,真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

Run

我最初以为就是运行了一个main方法,没想到却开启了一个服务;

@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {        //该方法返回一个ConfigurableApplicationContext对象        //参数一:应用入口的类     参数类:命令行参数
        SpringApplication.run(SpringbootDemo02Application.class, args);
    }

}

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication的实例化

1. 推断应用的类型是普通的项目还是Web项目

2.查找并加载所有可用初始化器 , 设置到initializers属性中

3.找出所有的应用程序监听器,设置到listeners属性中

4.推断并设置main方法的定义类,找到运行的主类

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

run方法的执行

最新文章

  1. 【hadoop2.2(yarn)】基于yarn成功执行分布式map-reduce,记录问题解决过程。
  2. [Asp.net 5] Logging-日志系统的基本架构(下)
  3. java web学习总结(二十) -------------------监听器属性详解
  4. solarium atomic update
  5. 深入理解SELinux SEAndroid
  6. gridView使用
  7. Effective Java 19 Use interfaces only to define types
  8. 读《JavaScript语言精粹》的一些感言
  9. WIN7 如何关闭Aero
  10. Java琐记
  11. Ajax页面逻辑
  12. Android仿人人客户端(v5.7.1)——个人主页(三)
  13. Oracle中的EXCEPTION
  14. jstl标签经典
  15. BZOJ_2124_等差子序列_线段树+Hash
  16. 六大设计原则(三)DIP依赖倒置原则
  17. 事务,mybatis
  18. python--第二十三天总结(一对多和多对多)
  19. 【oracle】oracle REGEXP_SUBSTR分割字符串
  20. 巧用css的border属性完成对图片编辑功能的性能优化

热门文章

  1. Mac-peizhi
  2. Python内置函数compile
  3. springboot swagger教程&#128512;
  4. 关于微信H5页面开发中音乐不自动播放的解决方法
  5. python常量 (最全常量解析)
  6. 第7天:Django模板使用与表单
  7. TCP/IP四层协议
  8. socket 服务器向指定的客户端发消息
  9. 用HTTP核心模块配置一个静态Web服务器
  10. 个人智能家居系统 - MQTT服务器搭建(centOS7.3)