幽兰生空谷
--绝世独自开

SpringBoot快速入门(三)-HelloWorld运行原理探究

运行原理探究

我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;

Pom.xml

父依赖

其中它主要依赖一个父项目,主要是管理项目的资源过滤及插件!

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

点进去,发现还有一个父依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.5.6</version>
</parent>

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

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

启动器spring-boot-starter

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

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

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

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

主启动类

分析完了pom.xml来看看这个启动类

默认的著启动类

//@SpringBootApplication 来标注一个主程序类,说明这是一个springboot应用
@SpringBootApplication
public class HelloWorldApplication {
    public static void main(String[] args) {
        //以为是启动了一个方法,没想到启动了一个服务
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

但是一个简单的启动类并不简单!我们来分析以下这些注解都干了什么

@SpringBootApplication

作用:标注在某个类上说明这个类是springboot的主配置类,springboot就应该运行这个类的main方法来启动springboot应用;

进入这个注解:可以看到上面还有很多其他注解!

@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
@org.springframework.boot.SpringBootConfiguration
@org.springframework.boot.autoconfigure.EnableAutoConfiguration
@org.springframework.context.annotation.ComponentScan(excludeFilters = {
  @org.springframework.context.annotation.ComponentScan.Filter(
    type = org.springframework.context.annotation.FilterType.CUSTOM, 
    classes = {org.springframework.boot.context.TypeExcludeFilter.class}), 
    @org.springframework.context.annotation.ComponentScan.Filter(
      type = org.springframework.context.annotation.FilterType.CUSTOM, 
      classes = {org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter.class})})
public @interface SpringBootApplication {
    \\\....
}

@ComponentScan

这个注解在spring中很重要,它对应XML配置中的元素。

作用:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中

@SpringBootConfiguration

作用:springboot的配置类,标注在某个类上,表示这是一个springboot的配置类;

我们继续进去这个注解查看

// 点进去得到下面的@Component
@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}

这里的 @Configuration,说明这是一个配置类,配置类就是对应spring的xml配置文件;

里面的 @Component,说明启动类本身也是spring中的一个组件而已,负责启动应用!

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

@EnableAutoConfiguration

@EnableAutoConfiguration:开启自动配置功能

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

点进注解继续查看:

@AutoConfigurationPackage:自动配置包

点进去

@Import({Registrar.class})
public @interface AutoConfigurationPackage {}

@Import: Spring底层注解@Import,给容器中导入一个组件

Registrar.class作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到spring容器;

这个分析完了,退到上一步,继续看

@Import({AutoConfigurationImportSelector.class}):给容器导入组件;

AutoConfigurationImportSelector: 自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点进去看这个类的源码:

  1. 这个类中有这样一个方法
// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//这里的getSpringFactoriesLoaderFactoryClass()方法
//返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

2.这个方法有调用了SpringFactoriesLoader类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames()方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    //这里它有调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

3.我们继续点进去查看 loadSpringFactories() 方法

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//获得classLoader, 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();
        try {
        //去获取一个资源 "META-INF/spring.factories"
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();
                //将读取到的资源遍历,封装成为一个Properties
                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;
                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }
            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

4.发现一个多次出现的文件:spring.factories,全局搜索它,并定位到文件位置

spring.factories

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

WebMvcAutoConfiguration

我们在上面的自动配置类随便找一个打开看看,比如:WebMvcAutoConfiguration

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

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

结论:

  1. springboot在启动的时候从类路径下的 META-INF/spring.factories 中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件;
  5. 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;

现在大家应该大概的了解了下,springboot的运行原理,后面我们还会深化一次!

赞(4) 打赏
版权声明:本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
文章名称:《SpringBoot快速入门(三)-HelloWorld运行原理探究》
文章链接:https://www.itheibai.com/archives/480
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途,网站会员捐赠是您喜欢本站而产生的赞助支持行为,仅为维持服务器的开支与维护,全凭自愿无任何强求。

评论 抢沙发

评论前必须登录!

 

养成“打赏”的好习惯,从我做起!

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册