SpringBoot HelloWorld启动简析

SpringBoot是Spring技术栈的一个大整合,J2EE开发的一站式解决方案。官网提供的HelloWorld程序使用了极少的代码与配置即实现了我们以前繁琐的Spring配置。这其中的奥秘是什么呢?今天我们一起探究一下

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
@ResponseBody
@RestController
public class AppController {

    @RequestMapping("/hello")
    public String hello(){
        return "Hello World";
    }
}

在这里插入图片描述

1.1 pom

首先看一下项目的pom文件

1.1.1 parent项目

发现在我们的pom文件中配置了父项目

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

<!‐‐ 这个插件,可以将应用打包成一个可执行的jar包;‐‐>
<build>
	<plugins>
		<plugin>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring‐boot‐maven‐plugin</artifactId>
		</plugin>
	</plugins>
</build>

而我们的父项目中也引入了一个父项目

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath>../../spring‐boot‐dependencies</relativePath>
</parent>
他来真正管理Spring Boot应用里面的所有依赖版本;

也就是我们Spring Boot的版本仲裁中心;

所以以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)

1.1.2 启动器

在我们得pom文件中出了配置父项目,还引入了如下配置

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

我们点进去发现,发现已经帮我们引入了web所需要的依赖

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    ......

spring-boot-starter 也就是spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件

Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter

相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器

1.2 主程序类,主入口类

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

在我们主程序类上标注了@SpringBootApplication 注解
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot
就应该运行这个类的main方法来启动SpringBoot应用

接下来我们看一下SpringBootApplication 注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

可以看到首先他包含@SpringBootConfiguration注解 和 @EnableAutoConfiguration 注解

1.2.1 @SpringBootConfiguration

@SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration 表明这是一个配置类
public @interface SpringBootConfiguration {
}

1.2.2 @EnableAutoConfiguration

我们再看一下@SpringBootApplication中的另一个注解@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

@EnableAutoConfiguration 注解包括@AutoConfigurationPackage 注解 和 @Import({AutoConfigurationImportSelector.class}) 注解

1.2.2.1 @AutoConfigurationPackage

其中@AutoConfigurationPackage注解代码为:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) Spring的底层注解@Import,给容器中导入一个组件
public @interface AutoConfigurationPackage {
}

AutoConfigurationPackage 引入了 AutoConfigurationPackages.Registrar.class 我们点击查看Registrar.class

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

发现new AutoConfigurationPackages.PackageImport(metadata)).getPackageName() 得值正好是我们@SpringBootApplication标注的主程序类所在的包名
在这里插入图片描述

也就是说在主程序类包下面的类会被扫描当做组件注册到spring容器中,这也就是为什么我们没有像以前那样配置包扫描,配置bean,我们的controller也被加入了spring容器

1.2.2.2 AutoConfigurationImportSelector

然后我们接着看 @EnableAutoConfiguration 注解中另一个
@Import({AutoConfigurationImportSelector.class}) (Import XXX 相当于引入了 XXX组件)

在AutoConfigurationImportSelector类getAutoConfigurationEntry方法中

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

可以看到该方法的返回值是 XXXAutoConfigration的 配置的集合
在这里插入图片描述

也就是说springboot 自动帮我们自动注册了这些组件,但是这些组件是在哪获取的值呢?我们接着看getAutoConfigurationEntry方法中调用的getCandidateConfigurations 方法,

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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;
    }

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

在方法中我们调用loadFactoryNames并传入了EnableAutoConfiguration.class 和 类加载器


public static final String FACTORIES_RESOURCE_LOCATION = "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());
    }

也就是在我们jar包中的**“META-INF/spring.factories”** 配置了EnableAutoConfiguration的值,我们去看一下
在这里插入图片描述


# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\

所以我们springBoot项目启动的时候会从**“META-INF/spring.factories”** 中获取EnableAutoConfiguration的值然后帮我们自动配置的组件

因此,我们一个简简单单没有任何配置的HelloWorld的原理就简单解析完了

  1. pom文件中的父项目 负责控制版本,作为版本仲裁
  2. 我们可以引入我们需要的场景启动器,启动器会帮我们引入需要的依赖,也就是封装了一层
  3. SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
  4. @EnableAutoConfiguration将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中

猜你喜欢

转载自blog.csdn.net/yz357823669/article/details/83956725