欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot 源码解析 (一)----- SpringBoot核心原理入门

程序员文章站 2022-10-28 08:32:11
Spring Boot 概述 Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed t ......

spring boot 概述

build anything with spring boot:spring boot is the starting point for building all spring-based applications. spring boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of spring.

上面是引自官网的一段话,大概是说: spring boot 是所有基于 spring 开发的项目的起点。spring boot 的设计是为了让你尽可能快的跑起来 spring 应用程序并且尽可能减少你的配置文件。

什么是 spring boot

  • 它使用 “习惯优于配置” (项目中存在大量的配置,此外还内置一个习惯性的配置,让你无须手动配置)的理念让你的项目快速运行起来。
  • 它并不是什么新的框架,而是默认配置了很多框架的使用方式,就像 maven 整合了所有的 jar 包一样,spring boot 整合了所有框架

使用 spring boot 有什么好处

回顾我们之前的 ssm 项目,搭建过程还是比较繁琐的,需要:

  • 1)配置 web.xml,加载 spring 和 spring mvc
  • 2)配置数据库连接、配置日志文件
  • 3)配置家在配置文件的读取,开启注解
  • 4)配置mapper文件
  • .....

而使用 spring boot 来开发项目则只需要非常少的几个配置就可以搭建起来一个 web 项目,并且利用 idea 可以自动生成生成

  • 划重点:简单、快速、方便地搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率。

spring boot helloworld

导入依赖spring boot相关的依赖

<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>

    <groupid>cn.chenhao</groupid>
    <artifactid>springboot</artifactid>
    <version>1.0.0-snapshot</version>
    <packaging>jar</packaging>

    <name>springboot</name>
    <description>demo project for spring boot</description>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.0.1.release</version>
        <relativepath/>
    </parent>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>
</project>

编写主程序

/**
 * @springbootapplication来标注一个主程序类,说明这是一个springboot应用
 */
@springbootapplication
public class helloworldmainapplication {

    public static void main(string[] args) {
        //spring应用启动
        springapplication.run(helloworldmainapplication.class, args);
    }
}

编写controller、service

@restcontroller
public class hellocontroller {

    @requestmapping("/hello")
    public string hello(){
        return "hello world";
    }
}

运行主程序测试

使用maven打包命令将其打包成jar包后,直接使用命令:

java -jar xxx.jar

hello world探究

pom文件

父项目

<parent>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-parent</artifactid>
    <version>2.0.1.release</version>
    <relativepath/>
</parent>

其父项目是

<parent>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-dependencies</artifactid>
    <version>2.0.1.release</version>
    <relativepath>../../spring-boot-dependencies</relativepath>
</parent>

该父项目是真正管理spring boot应用里面的所有依赖的版本:spring boot的版本仲裁中心,所以以后导入的依赖默认是不需要版本号。如下

SpringBoot 源码解析 (一)----- SpringBoot核心原理入门

还有很多版本号没有截图出来

启动器

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-web</artifactid>
</dependency>

spring-boot-starter : spring boot场景启动器;帮助导入web模块正常运行所依赖的组件;

​ spring boot将所有的功能场景抽取出来,做成一个个的starter(启动器),只需要在项目中引入这些starter,那么相关的场景的所有依赖都会导入进项目中。要用什么功能就导入什么场景的启动器。

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-tomcat</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-web</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-webmvc</artifactid>
</dependency>

添加了 spring-boot-starter-web 依赖,会自动添加 tomcat 和 spring mvc 的依赖

spring-boot-starter-web中又引入了spring-boot-starter-tomcat

主程序类(主入口类)

@springbootapplication
public class helloworldmainapplication {

    public static void main(string[] args) {
        //spring应用启动
        springapplication.run(helloworldmainapplication.class, args);
    }
}

@springbootapplication

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

注解定义如下:

@springbootconfiguration
@enableautoconfiguration
@componentscan(excludefilters = {
        @filter(type = filtertype.custom, classes = typeexcludefilter.class),
        @filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) })
public @interface springbootapplication {}

@springbootconfiguration

  • spring boot的配置类
  • 标注在某个类上,表示这是一个spring boot的配置类

注解定义如下:

@configuration
public @interface springbootconfiguration {}

其实就是一个configuration配置类,意思是helloworldmainapplication最终会被注册到spring容器中

@enableautoconfiguration

  • 开启自动配置功能
  • 以前使用spring需要配置的信息,spring boot帮助自动配置;
  • @enableautoconfiguration通知springboot开启自动配置功能,这样自动配置才能生效。

注解定义如下:

@autoconfigurationpackage
@import(enableautoconfigurationimportselector.class)
public @interface enableautoconfiguration {}

@autoconfigurationpackage

  • 自动配置包注解
@import(autoconfigurationpackages.registrar.class)
public @interface autoconfigurationpackage {}
@import(autoconfigurationpackages.registrar.class):默认将主配置类(@springbootapplication)所在的包及其子包里面的所有组件扫描到spring容器中。如下
@order(ordered.highest_precedence)
static class registrar implements importbeandefinitionregistrar, determinableimports {

    @override
    public void registerbeandefinitions(annotationmetadata metadata,
            beandefinitionregistry registry) {
          //默认将会扫描@springbootapplication标注的主配置类所在的包及其子包下所有组件
        register(registry, new packageimport(metadata).getpackagename());
    }

    @override
    public set<object> determineimports(annotationmetadata metadata) {
        return collections.<object>singleton(new packageimport(metadata));
    }
}

@import(enableautoconfigurationimportselector.class)

enableautoconfigurationimportselector: 导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。

 1 //enableautoconfigurationimportselector的父类:autoconfigurationimportselector
 2 @override
 3 public string[] selectimports(annotationmetadata annotationmetadata) {
 4     if (!isenabled(annotationmetadata)) {
 5         return no_imports;
 6     }
 7     try {
 8         autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader
 9             .loadmetadata(this.beanclassloader);
10         annotationattributes attributes = getattributes(annotationmetadata);
11         list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes);
12         configurations = removeduplicates(configurations);
13         configurations = sort(configurations, autoconfigurationmetadata);
14         set<string> exclusions = getexclusions(annotationmetadata, attributes);
15         checkexcludedclasses(configurations, exclusions);
16         configurations.removeall(exclusions);
17         configurations = filter(configurations, autoconfigurationmetadata);
18         fireautoconfigurationimportevents(configurations, exclusions);
19         return configurations.toarray(new string[configurations.size()]);
20     }
21     catch (ioexception ex) {
22         throw new illegalstateexception(ex);
23     }
24 }

我们主要看第11行list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes);会给容器中注入众多的自动配置类(xxxautoconfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件。我们跟进去看看

protected list<string> getcandidateconfigurations(annotationmetadata metadata,
            annotationattributes attributes) {
    list<string> configurations = springfactoriesloader.loadfactorynames(
        getspringfactoriesloaderfactoryclass(), getbeanclassloader());
    //...
    return configurations;
}

protected class<?> getspringfactoriesloaderfactoryclass() {
    return enableautoconfiguration.class;
}

public static final string factories_resource_location = "meta-inf/spring.factories";

public static list<string> loadfactorynames(class<?> factoryclass, classloader classloader) {
    string factoryclassname = factoryclass.getname();
    try {
        //从类路径的meta-inf/spring.factories中加载所有默认的自动配置类
        enumeration<url> urls = (classloader != null ? classloader.getresources(factories_resource_location) :
                                 classloader.getsystemresources(factories_resource_location));
        list<string> result = new arraylist<string>();
        while (urls.hasmoreelements()) {
            url url = urls.nextelement();
            properties properties = propertiesloaderutils.loadproperties(new urlresource(url));
            //获取enableautoconfiguration指定的所有值,也就是enableautoconfiguration.class的值
            string factoryclassnames = properties.getproperty(factoryclassname);
            result.addall(arrays.aslist(stringutils.commadelimitedlisttostringarray(factoryclassnames)));
        }
        return result;
    }
    catch (ioexception ex) {
        throw new illegalargumentexception("unable to load [" + factoryclass.getname() + "] factories from location [" + factories_resource_location + "]", ex);
    }
}
springboot启动的时候从类路径下的 meta-inf/spring.factories中获取enableautoconfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。enableautoconfiguration默认在spring-boot-autoconfigure这个包中,如下图

SpringBoot 源码解析 (一)----- SpringBoot核心原理入门

最终有96个自动配置类被加载并注册进spring容器中

SpringBoot 源码解析 (一)----- SpringBoot核心原理入门

j2ee的整体整合解决方案和自动配置都在spring-boot-autoconfigure-xxx.jar中。在这些自动配置类中会通过@conditionalonclass等条件注解判断是否导入了某些依赖包,从而通过@bean注册相应的对象进行自动配置。后面我们会有单独文章讲自动配置的内容