springboot笔记03——quickstart程序原理

一、前言

一个quickstart程序仅仅让我们初步了解一个框架,我们还需要透过现象看本质才能学好一个框架。所以这篇文章分析一下我上次写的springboot的入门程序。

二、原理分析

1、依赖分析

1.1、父模块的parent结点

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath/> 
</parent>

1.2、点进去spring-boot-starter-parent

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.1.6.RELEASE</version>
  <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

1.3、点进去spring-boot-dependencies

<properties>
  <activemq.version>5.15.9</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
    ...
  <commons-pool.version>1.6</commons-pool.version>
</properties>
<dependencyManagement>
  <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.1.6.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.6.RELEASE</version>
      </dependency>
      ...
  </dependencies>
</dependencyManagement>

在spring-boot-dependencies中对大部分的jar包进行版本管理,再点进去每一个所引用到的starter里面就能看见每个jar包具体的版本。在spring-boot-dependencies已经定义了版本号的jar包,在其他地方引用就不需再写版本号了,而在spring-boot-dependencies没有被依赖的jar包,自行引入的时候就需要写版本号。 特别之处就是spring-boot-dependencies中使用了< dependencyManagement >,dependencyManagement 用于版本管理,声明jar,并不实际引入jar,子模块默认不继承,子模块需要的jar需要在子模块引入,并不需重新写版本号,若子模块定义了新的版本号,则优先使用子模块中的版本。而使用 < dependencies >就是直接引入jar。

(我的入门程序只引用到了spring-boot-starter-web和spring-boot-starter-test)


1.4、Tomcat在哪里

以往的spring程序都需要把项目发布在Tomcat中,然后手动启动Tomcat服务器进行测试。Springboot的入门程序只需要Run启动类就可以访问url了。这是为什么呢?

我们先点进去spring-boot-starter-web(这里没有写版本号)

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

再点进去spring-boot-starter-tomcat

<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-core</artifactId>
  <version>9.0.21</version>
  <scope>compile</scope>
  <exclusions>
    <exclusion>
      <artifactId>tomcat-annotations-api</artifactId>
      <groupId>org.apache.tomcat</groupId>
    </exclusion>
  </exclusions>
</dependency>
...
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-websocket</artifactId>
  <version>9.0.21</version>
  <scope>compile</scope>
</dependency>

**Springboot内嵌了一个Tomcat ! **Springboot2.1.6内嵌的Tomcat版本号为9.0.21。


2、注解分析

2.1、主启动类中的 @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、@ComponentScan。下面来分析一下这三个注解。


2.1.1、@SpringBootConfiguration

Springboot配置类。标注在某个类上,表示这是一个Springboot的配置类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

用了Spring的@Configuration,表明这是一个配置类,相当于配置文件


2.1.2、@EnableAutoConfiguration

自动配置

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

两个关键注解:@AutoConfigurationPackage、@Import。


2.1.2.1 、@AutoConfigurationPackage
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

@Import({Registrar.class}) 引入了Registrar组件。一个很关键的步骤:将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器。 就是由这一句注解完成的。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }

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

    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    }
}

2.1.2.2 、@Import({AutoConfigurationImportSelector.class})

将一些快速地导入到Spring容器中

我们可以看getCandidateConfigurations方法中引用的SpringFactoriesLoader.loadFactoryNames方法。

Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");

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


2.1.3、@ComponentScan

相当于Spring配置文件中的,作用就是将不需要扫描的组件在这里声明。

<context:component-scan base-package="xxx">
    <context:exclude-filter type="annotation"  expression="xxxx"/>
</context:component-scan>

总结

知所以然才能知其所以。学习原理非常重要。

posted @ 2019-07-16 13:53  Jotal  阅读(292)  评论(0编辑  收藏  举报