文章目录

  • 为什么pom.xml文件中导入依赖并不需要版本?
  • SpringBoot是如何实现自动配置的?
  • 结论:
  • 它是如何启动运行的?


为什么pom.xml文件中导入依赖并不需要版本?

  • SpringBoot归根结底是一个Maven项目,我们一般从pom.xml文件探究起;在以前无论是Javaweb还是SSM我们在pom.xml文件中导入依赖都需要实现其版本, 但是在SpringBoot项目中没有版本, 但是他有了一个父依赖
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
  • 点进去,发现还有一个父依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.11.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
  </parent>
  • 我们再点进行就会发现秘密
  • pom依赖会导致java文件改吧j pom依赖不写版本_maven

  • 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
  • 在SpringBoot导入依赖默认是不需要写版本, 它会默认匹配自带的版本号;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
  • 在pom.xml中还有一个最重要的web启动器
<dependencies>
        <!--核心依赖, 内涵tomcat和dispatchServlet和xml配置都是他帮我们做的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  • springboot-boot-starter-xxx:就是spring-boot的场景启动器
  • spring-boot-starter-web:当我们创建SpringBoot项目选择了web依赖的时候, 系统就帮我们导入了web启动器正常运行所依赖的组件;
  • SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可

SpringBoot是如何实现自动配置的?

  • 我们接下来看主启动类
//通过这个注解, 标注这个类是一个SpringBoot的应用
@SpringBootApplication
public class DemoApplication {

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

}
  • 他主要是通过一个注解 @SpringBootApplication 来表示这是主启动类, 还表明这是个SpringBoot项目
  • 进入这个注解:可以看到上面还有很多其他注解!
@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}
)}
)
  • @ComponentScan
    这个注解在Spring中很重要 ,它对应XML配置中的元素。作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
  • @SpringBootConfiguration
    作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

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

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • 进去之后我们发现这里有一个 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;也就是这也只是一个SpringBoot的配置类, 我们再进去
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}
  • 里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用的时候去加载一些配置文件!
  • 我们回到 SpringBootApplication 注解中继续看。
  • 有一个 @EnableAutoConfiguration 我们顾名思义这应该是实现自动装配的配置注解, @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效, 我们点进去看看
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
  • 发现有一个@AutoConfigurationPackage :自动配置包, 我们再点进去
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
  • @import :Spring底层注解@import , 作用就是让Spring识别给容器中导入一个组件
  • Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
  • 这个分析完了,退到上一步,继续看
  • @Import({AutoConfigurationImportSelector.class}) :给容器导入组件 AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
  • 进入这个类后我们更具方法名字很容易判断出这个类是在干什么, 若果想要知道更多可以继续点进行看, 但是我们会发现一个很重要的方法 getAutoConfigurationEntry 这个方法是获得自动配置的入口, 里面有一个方法 getCandidateConfigurations 获取候选配置
  • pom依赖会导致java文件改吧j pom依赖不写版本_java_02

  • 我们进入 getCandidateConfigurations 获取候选配置这个方法
  • pom依赖会导致java文件改吧j pom依赖不写版本_pom依赖会导致java文件改吧j_03

  • 我们再进去SpringFactoriesLoader 加载器中

pom依赖会导致java文件改吧j pom依赖不写版本_pom依赖会导致java文件改吧j_04

  • 我们发现很多加载的地方都用到了一个字符串路径spring.factories, 好 我们搜索这个路径,就会发发现终极秘密
  • 我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
  • 我们打开我们使用的web的配置 WebMvcAutoConfiguration
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
    public static final String DEFAULT_PREFIX = "";
    public static final String DEFAULT_SUFFIX = "";
    private static final String[] SERVLET_LOCATIONS = new String[]{"/"};

    public WebMvcAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
    @ConditionalOnProperty(
        prefix = "spring.mvc.hiddenmethod.filter",
        name = {"enabled"},
        matchIfMissing = false
    )
  • 可以看到最开始使用了@Configuration表明这是一个JavaConfig配置类,而且个个组件都使用了@Bean注入到了Spring中
  • 这时我们就会发现貌似是这样的, 自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化 为对应标注了 @Configuration的JavaConfig形式的配置类 , 然后将这些都汇总成为一个实例并以Bean的形式加载到IOC容器中。
  • 那我们点开我们没有导入启动器的配置类又会如何
  • 这个配置文件是不会生效的, 因为在其内部的注解需要的类是找不到, 那我们也可以这样想, 这个spring.factories文件中的所有配置类不会都实现, 只有导入了对应的启动器才会生效.

结论:

SpringBoot所有的自动配置 都是在启动的时候扫描并加载,META-INF/spring.factories中所有的内容, 但并不是所有的自动配置类都生效, 其内部会有一个判断条件, 判断条件是 是否导入了这个配置对应的start启动器, 如果导入了有了启动器才会进行自动配置

  1. SpringBoot在启动的时候, 从类路径下/META-INF/spring-factories获取指定的值
  2. 将这些自动配置的类导入容器,自动配置类就会生效, 帮我们进行自动配置
  3. 以前我们配置的那一堆东西, 现在都是springBoot帮我们做了
  4. 整合整个javaEE的所有解决方案和自动配置都在spring.autoconfigure-2.2.11.RELEASE.jar这个包下
  5. 他会把所有需要导入的组件一类名的方式返回 ,这些组件就会被添加到容器中
  6. 容器中又会有很多的XXXAutoConfiguation的文件(@Bean), 就是这些类给容器导入了某个场景需要的组件, 并自动配置 @Configuation
  7. 有了自动配置就省去了我们自动配置的麻烦事

它是如何启动运行的?

  • SpringApplication.run分析
  • SpringBoot的启动主要是通过实例化SpringApplication来启动的,启动过程主要做了以下几件事情
  1. 使用构造函数加载初初始化, 在这里他会判断是不是web项目, 如果是web项目就不会自动中断程序, 如果不是那等程序main结束就会结束这个程序
  2. 获取监听器,发布应用开始启动事件
  3. 初始化输入参数、配置环境,
  4. 输出banner、创建上下文、预处理上下文、刷新上下文、再刷新上下文、发布应用已经启动事件、发布应用启动完成事件。

pom依赖会导致java文件改吧j pom依赖不写版本_java_05

  • 在SpringBoot中启动tomcat的工作在刷新上下这一步。而tomcat的启动主要是实例化两个组件:Connector、Container,一个tomcat实例就是一个Server,一个Server包含多个Service,也就是多个应用程序,每个Service包含多个Connector和一个Container,而一个Container下又包含多个子容器。
  • tomcat本身就是一个web容器, 他使用socket编程实现处理请求和返回响应的数据封装, 所以在springboot中就使用tomcat源码实现这个逻辑
  • 而其中我们常用的有两个:TomcatServletWebServerFactory和JettyServletWebServerFactory。
  • 开发阶段对我们来说使用内置的tomcat是非常够用了,当然也可以使用jetty。
  • getWebServer这个方法创建了Tomcat对象, 返回的就是一个TomcatWebServer。
  • tomcat结构图
  • pom依赖会导致java文件改吧j pom依赖不写版本_spring_06

  • tomcat最顶层容器是Server,代表着整个服务器,一个Server包含多个Service。从上图可以看除Service主要包括多个Connector和一个Container。Connector用来处理连接相关的事情,并提供Socket到Request和Response相关转化。Container用于封装和管理Servlet,以及处理具体的Request请求。
  • 综上所述,一个tomcat只包含一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但有多个Connector,这样一个服务可以处理多个连接。
  • 多个Connector和一个Container就形成了一个Service,有了Service就可以对外提供服务了,但是Service要提供服务又必须提供一个宿主环境,那就非Server莫属了,所以整个tomcat的声明周期都由Server控制。