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

Springboot 系列(三)Spring Boot 自动配置

程序员文章站 2022-05-11 14:14:45
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别。 前言 关于配置文件可以配置的内容,在 "Spring Boot 官方网站" 已经提供了完整了配置示例和解释。 可以这么说,Spring Boot 的一 ......

注意:本 spring boot 系列文章基于 spring boot 版本 v2.1.1.release 进行学习分析,版本不同可能会有细微差别。

前言

Springboot 系列(三)Spring Boot 自动配置

关于配置文件可以配置的内容,在 spring boot 官方网站已经提供了完整了配置示例和解释。

可以这么说,spring boot 的一大精髓就是自动配置,为开发省去了大量的配置时间,可以更快的融入业务逻辑的开发,那么自动配置是怎么实现的呢?

1. @springbootapplication

跟着 spring boot 的启动类的注解 @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 {

@enableautoconfiguration 开启自动配置。

@componentscan 开启注解扫描

springbootapplication 我们可以发现,这是一个简便的注解配置,它包含了自动配置,配置类,包扫描等一系列功能。

2. @enableautoconfiguration

继续跟踪,查看@enableautoconfiguration 源码,里面比较重要的是 @import ,导入了一个翻译名为自动配置的选择器的类。这个类其实就是自动配置的加载选择器。

@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 {};
}

继续跟踪 autoconfigurationimportselector.class .在这个类有一个重要的方法 getcandidateconfigurations.用于加载 spring boot 配置的自动配置类。

getautoconfigurationentry 会筛选出有效的自动配置类。

protected autoconfigurationentry getautoconfigurationentry(
            autoconfigurationmetadata autoconfigurationmetadata,
            annotationmetadata annotationmetadata) {
        if (!isenabled(annotationmetadata)) {
            return empty_entry;
        }
        annotationattributes attributes = getattributes(annotationmetadata);
        list<string> configurations = getcandidateconfigurations(annotationmetadata,
                attributes);
        configurations = removeduplicates(configurations);
        set<string> exclusions = getexclusions(annotationmetadata, attributes);
        checkexcludedclasses(configurations, exclusions);
        configurations.removeall(exclusions);
        configurations = filter(configurations, autoconfigurationmetadata);
        fireautoconfigurationimportevents(configurations, exclusions);
        return new autoconfigurationentry(configurations, exclusions);
    }   

protected list<string> getcandidateconfigurations(annotationmetadata metadata,
            annotationattributes attributes) {
        list<string> configurations = springfactoriesloader.loadfactorynames(
                getspringfactoriesloaderfactoryclass(), 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;
    }

下图是 debug 模式下筛选之后的结果,因为我只添加了 web 模块,所以只有 web 相关的自动配置。

Springboot 系列(三)Spring Boot 自动配置

3. xxxautoconfiguration 与 xxxproperties

在上面的 debug 里,我们看到了成功加载的自动配置,目前只看到了配置类,却还没有发现自动配置值,随便选择一个 autoconfiguration 查看源码。

这里选择了 servletwebserverfactoryautoconfiguration.

@configuration
@autoconfigureorder(ordered.highest_precedence)
//判断当前项目有没有这个类
//characterencodingfilter;springmvc中进行乱码解决的过滤器;
@conditionalonclass(servletrequest.class)
//spring底层@conditional注解(spring注解版),根据不同的条件,如果
//满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@conditionalonwebapplication(type = type.servlet)
@enableconfigurationproperties(serverproperties.class)
@import({ servletwebserverfactoryautoconfiguration.beanpostprocessorsregistrar.class,
        servletwebserverfactoryconfiguration.embeddedtomcat.class,
        servletwebserverfactoryconfiguration.embeddedjetty.class,
        servletwebserverfactoryconfiguration.embeddedundertow.class })
public class servletwebserverfactoryautoconfiguration {

需要注意的是 @enableconfigurationproperties(serverproperties.class).他的意思是启动指定类的
configurationproperties功能;将配置文件中对应的值和 serverproperties 绑定起来;并把
serverproperties 加入到 ioc 容器中。

再来看一下 serverproperties .

@configurationproperties(prefix = "server", ignoreunknownfields = true)
public class serverproperties {

    /**
     * server http port.
     */
    private integer port;

显而易见了,这里使用 configurationproperties 绑定属性映射文件中的 server 开头的属性。结合默认配置

# 路径spring-boot-autoconfigure-2.1.1.release.jar
# /meta-inf/spring-configuration-metadata.json

    {
      "name": "server.port",
      "type": "java.lang.integer",
      "description": "server http port.",
      "sourcetype": "org.springframework.boot.autoconfigure.web.serverproperties",
      "defaultvalue": 8080
    }

达到了自动配置的目的。

4. 自动配置总结

  1. springboot 启动的时候加载主配置类,开启了自动配置功能 @enableautoconfiguration 。
  2. @enableautoconfiguration 给容器导入meta-inf/spring.factories 里定义的自动配置类。
  3. 筛选有效的自动配置类。
  4. 每一个自动配置类结合对应的 xxxproperties.java 读取配置文件进行自动配置功能 。

5. 配置类

通过自动配置,我们发现已经帮我们省去了大量的配置文件的编写,那么在自定义配置的时候,我们是不是需要编写xml呢?spring boot 尽管可以使用 springapplicationxml 文件进行配置,但是我们通常会使用 @configuration 类进行代替,这也是官方推荐的方式。

5.1 xml配置

定义 helloservice bean.

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
       xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloservice" class="net.codingme.boot.service.helloservice"></bean>

</beans>

引入配置。

@importresource(value = "classpath:spring-service.xml")
@springbootapplication
public class bootapplication {

    public static void main(string[] args) {
        springapplication.run(bootapplication.class, args);
    }
}

5.2 注解配置

此种方式和上面的xml配置是等效的,也是官方推荐的方式。@configuration 注解的类(要在扫描的包路径中)会被扫描到。

/**
 * <p>
 * 配置类,相当于传统spring 开发中的 xml-> bean的配置
 *
 * @author niujinpeng
 * @date 2018/12/7 0:04
 */
@configuration
public class serviceconfig {

    /**
     * 默认添加到容器中的 id 为方法名(helloservice)
     *
     * @return
     */
    @bean
    public helloservice helloservice() {
        return new helloservice();
    }
}

6. 附录

@conditional扩展注解 作用(判断是否满足当前指定条件)
@conditionalonjava 系统的java版本是否符合要求
@conditionalonbean 容器中存在指定bean;
@conditionalonmissingbean 容器中不存在指定bean;
@conditionalonexpression 满足spel表达式指定
@conditionalonclass 系统中有指定的类
@conditionalonmissingclass 系统中没有指定的类
@conditionalonsinglecandidate 容器中只有一个指定的bean,或者这个bean是首选bean
@conditionalonproperty 系统中指定的属性是否有指定的值
@conditionalonresource 类路径下是否存在指定资源文件
@conditionalonwebapplication 当前是web环境
@conditionalonnotwebapplication 当前不是web环境
@conditionalonjndi jndi存在指定项

文章代码已经上传到 github spring boot 自动配置

<完>
本文原发于个人博客: 转载请注明出处
Springboot 系列(三)Spring Boot 自动配置