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

我的Spring Boot学习记录(一):自动配置的大致调用过程

程序员文章站 2022-03-13 17:14:06
1. 背景 Spring Boot通过包管理工具引入starter包就可以轻松使用,省去了配置的繁琐工作,这里简要的通过个人的理解说下Spring Boot启动过程中如何去自动加载配置。 本文中使用的Spring Boot版本为2.0.0.RELEASE 这里主要是说自动配置大致调用流程,其他暂不做 ......

1. 背景

spring boot通过包管理工具引入starter包就可以轻松使用,省去了配置的繁琐工作,这里简要的通过个人的理解说下spring boot启动过程中如何去自动加载配置。

本文中使用的spring boot版本为2.0.0.release
这里主要是说自动配置大致调用流程,其他暂不做分析

2. 主要内容

2.1. spring.factories

首先,需要了解一件事,首先得知道有这么一件事,而自动配置这一件事得从meta-inf/spring.factories说起。其本质类似properties文件,一种key-value型的文件。
在spring boot的官方文档中,creating your own auto-configuration里面,它可以扫描加载meta-inf/spring.factories中的enableautoconfiguration为key的配置类。引入一个依赖,例如:

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

这个starter只是引入了其必须的依赖,没有做任何工作,最主要的是有个依赖为spring-boot-starter,这里包含了一个spring.factories,里面就有各种自动配置的enableautoconfiguration

2.2. 怎么加载enableautoconfiguration

2.2.1 springapplication.run

此函数是一个spring boot项目的入口,这里与@springbootapplication有很大的关联。

spring boot项目一般的启动代码如下:

@springbootapplication
public class springbootdemoapplication {
    public static void main(string[] args) {
        //主要提供了一个静态函数run来调用
        springapplication.run(springbootdemoapplication.class, args);
    }
}

再看run函数

    public static configurableapplicationcontext run(class<?> primarysource,
            string... args) {
        return run(new class<?>[] { primarysource }, args); // 
    }

    public static configurableapplicationcontext run(class<?>[] primarysources,
            string[] args) {
        return new springapplication(primarysources).run(args);
    }
    
    // 最终被调用的run函数
    public configurableapplicationcontext run(string... args) { 
        //......        
        context = createapplicationcontext();           
        preparecontext(context, environment, listeners, applicationarguments,
                    printedbanner);
        refreshcontext(context);
        //......    
        return context;
    }

以上中,最终是调用了org.springframework.boot.springapplication#run(java.lang.string...)方法,此方法主要是准备了environment和applicationcontext。而applicationcontext就是spring项目核心的东西,那与自动配置又有什么关系,这里就需要回去看下@springbootapplication注解

2.2.2 @springbootapplication

//....
@enableautoconfiguration
public @interface springbootapplication {
    //.....
}
//在上面@springbootapplication的注解代码中,有个@enableautoconfiguration
//.....
@import(autoconfigurationimportselector.class)
public @interface enableautoconfiguration {
    //...
}

上面为springbootapplication和enableautoconfiguration注解的部分代码,其中,在enableautoconfiguration注解中又有@import(autoconfigurationimportselector.class),这里的@import是spring context的内容,与后面的内容中org.springframework.context.annotation.configurationclassparser类有关联,目前要知道其主要功能就是将autoconfigurationimportselector加载至上下文中。在了解autoconfigurationimportselector源码之前,我们需要先知道springfactoriesloader,这就是一个meta-inf/spring.factories文件加载器。 其源码可看org.springframework.core.io.support.springfactoriesloader

现在,我们看至autoconfigurationimportselector的源码:

/**
 * {@link deferredimportselector} to handle {@link enableautoconfiguration
 * auto-configuration}. this class can also be subclassed if a custom variant of
 * {@link enableautoconfiguration @enableautoconfiguration}. is needed.
 * 
 * 主要的意思为:deferredimportselector能够去处理 enableautoconfiguration自动配置类import工作
 */
public class autoconfigurationimportselector
        implements deferredimportselector,... {
    
    /**
     * 返回一个需要被加载至spring上下文的的类名数组
     */
    @override
    public string[] selectimports(annotationmetadata annotationmetadata) {
        //....
        list<string> configurations = getcandidateconfigurations(annotationmetadata,
                    attributes);
        //....
        return stringutils.tostringarray(configurations);
    }

    protected list<string> getcandidateconfigurations(annotationmetadata metadata,
            annotationattributes attributes) {
            //通过springfactoriesloader加载出所有的enableautoconfiguration类
        list<string> configurations = springfactoriesloader.loadfactorynames(
                getspringfactoriesloaderfactoryclass(), getbeanclassloader());
        return configurations;
    }
    
    /**
     * 返回了一个让springfactoriesloader加载的class,就是enableautoconfiguration
     */
    protected class<?> getspringfactoriesloaderfactoryclass() {
        return enableautoconfiguration.class;
    }
}

那么究竟是谁去调用了org.springframework.boot.autoconfigure.autoconfigurationimportselector#selectimports并加载了那些enableautoconfiguration。这里的步骤比较多,我们就从selectimports倒序说起,这里分为几点来说:

  • 首先,autoconfigurationimportselector 继承了接口importselector
  • org.springframework.context.annotation.configurationclassparser类通过接口org.springframework.context.annotation.importselector调用了selectimports,这里调用的方法分别为#processdeferredimportselectors#processimports,最终的指向都是#parse(set<beandefinitionholder>)方法。这里需要说明的是#processimports方法就是对于处理@import注解的相关方法,该类的源码中注释有说明。
  • org.springframework.context.annotation.configurationclassparser#parse(set<beandefinitionholder>)却是org.springframework.context.annotation.configurationclasspostprocessor#processconfigbeandefinitions调用,而#processconfigbeandefinitions为自身方法所调用。
  • configurationclasspostprocessor调用的源头是类org.springframework.context.support.postprocessorregistrationdelegate,这个类中有两个公共的调用方法。
  • 最后由org.springframework.context.support.abstractapplicationcontext#refresh调用
  • org.springframework.context.support.abstractapplicationcontext#refresh方法在org.springframework.boot.springapplication#run被调用了

因此,这里就与我们上面介绍的springapplication.run产生了联系,就是通过其调用了抽象上下文abstractapplicationcontext的refresh方法,从而产生了上面的一系列步骤。

可以看下uml类图了解它们关系
我的Spring Boot学习记录(一):自动配置的大致调用过程
可能这里说的不清楚,建议使用ide进行debug看源码。而且这里对于spring context的内容没有展开,本人也一知半解(或者说不解,不了解),望见谅,有需要可以参考以下文章

https://docs.spring.io/spring/docs/5.2.0.build-snapshot/spring-framework-reference/core.html#spring-core
https://www.cnblogs.com/davidwang456/p/5717972.html
https://blog.csdn.net/yangyangiud/article/details/79835594

3. 总结

阅读了别人写的代码,看别人为何这么写,这里看到的就是对于接口的活用,对于封装以及工厂模式的应用,对于扩展,文件配置等等,自己能学到的还有很多,继续敲代码,看代码,向人家学习

参考链接:
https://docs.spring.io/spring-boot/docs/2.1.4.release/reference/htmlsingle/
https://www.cnblogs.com/saaav/tag/spring%20boot/