SpringBoot自动配置原理和自定义启动器

1、自动配置的原理

(1) 原理总结

(2) 原理详细说明

2、自定义启动器,实现自动配置

(1) 启动器项目目录结构

(2) 引入pom依赖(引用了父项目的版本锁定)

(3) 创建redis配置数据对应的属性类

(4) 编写自动配置的配置类

(5) 编写扫描配置类,目的是交给spring容器管理

         3、启动器测试

(1) 引入自定义启动器依赖

(2) 编写测试代码

(3) 测试结果


1、自动配置的原理
(1) 原理总结

项目在加载上下文时,会根据@SpringBootApplication注解运行。该注解中有一个@CompoentScan注解,会扫描和加载当前启动类所在的目录,以及所有的子目录;还有一个是@EnableAutoConfiguration注解,这个注解会读取扫描到META-INF/spring.factories文件,这个文件里面定义了100多个自动装配依赖的配置类。 尽管能够全部读取到这些配置类,但并不会全部加载这些类以及创建类对象, 而是要根据@ConditionOnClass注解来做决定。ConditionOnClass注解会判断当前配置类对应启动器是否有添加,如果添加了启动器,就说明添加了对应的依赖,就会创建对应的配置类。

  • @CompoentScan注解,扫描文件,重点是扫描到META-INF/spring.factories文件;
  • @EnableAutoConfiguration注解,读取META-INF/spring.factories文件内容;
  • @ConditionOnClass注解, 拍板决定是否要创建META-INF/spring.factories文件里的某个配置类。
(2) 原理详细说明
  • spring.factories文件说明:如果不用SpringBoot,每次引入一个依赖,通常都需要创建一个对应的配置类。 而SpringBoot默认提前写好了130多个的配置类,这些配置类都写好了默认值。 配置类的目录文件可以在spring-boot-autoconfiguration-版本号.jar自动配置包下的META-INF/spring.factories文件中看到。 后面要自定义启动器的原理是类似SpringBoot一样,提前把配置类写好,并且加入到spring.factories文件中,让SpringBoot去进行管理。

  • 运行时,首先找到SpringBootApplication注解,这个注解会加载所有的资源。 这个注解是一个复合注解,其中包含了7个注解,分别做不同的事情。

  • 其中的ComponentScan注解,作用是扫描当前启动类所在的包以及子包下的所有类。
  • EnableAutoConfiguration注解,实现自动配置的核心注解类,同样是复合注解。 其中的Import注解导入了一个AutoConfigurationImportSelector类,这个类就会读取SpringBoot提前写好的所有配置类,读取的关键是通过读取MEAT-INF/spring.factories文件写好的所有配置类。

  • 提前写好的配置类上都有一个ConditionOnClass注解,该注解会判断注解里面定义的类在项目中是否存在,如果存在就会执行配置类的代码。通常只要引入了启动器依赖,加载了对应jar,ConditionOnClass注解里面的类就存在了,对应的配置文件就会被执行。

2、自定义启动器,实现自动配置

编写自动配置的自定义启动器的逻辑是,编写好默认的配置属性和配置类,以及根据配置类创建对应的对象,然后将对象交给Spring容器管理。其他人在引入启动器后,就可以直接注入核心对象使用。

说明:本次要自定义实现的启动器是类似于spring-boot-starter-data-redis,因为spring-boot-starter-data-redis底层用的是Jedis客户端去连接Redis,所以我们也封装Jedis构建一个连接Redis的启动器。 主要是在Jedis的基础上,编写了一个配置类和一个配置属性类,并且交给了SpringBoot进行自动配置管理。 用户如果使用Jedis原生客户端,会需要自己编写配置类和配置属性类, 而使用我们封装好的启动器就不需要写相关的配置类,并且还提前创建了连接Redis的核心对象,直接注入即可使用。

使用自定义的spring-boot-starter-my-jedis而不直接使用jedis的好处

  • 默认的属性配置,主机、端口、最大连接数和最大超时时间都有默认值。
  • 自动创建好Jedis核心连接对象,加入了IOC容器,要使用时直接注入即可。
(1) 启动器项目目录结构

(2) 引入pom依赖(引用了父项目的版本锁定)
<dependencies>
  <!--jedis依赖-->
  <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
  </dependency>
  <!--启动器依赖-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <!--lombok依赖-->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>
</dependencies>

(3) 创建redis配置数据对应的属性类
package com.zhuimengren.myjedis.conf;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * jedis属性类
 */
@Data
@ConfigurationProperties(prefix = "my-jedis")
public class MyJedisProperties {
    /**
     * redis的IP地址
     */
    private String host = "localhost";

    /**
     * redis端口号
     */
    private Integer port = 6379;

    /**
     * redis的最大连接数
     */
    private Integer maxActive = 8;

    /**
     * redis的请求超时最大等待时间
     */
    private Long maxWait = -1L;
}

(4) 编写自动配置的配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import javax.annotation.PostConstruct;

@Configuration // 标识当前是一个配置类
@ConditionalOnClass(MyJedisProperties.class) // 判断开发的项目是否引用了启动器中的类,引用了才会执行当前配置类的代码
@EnableConfigurationProperties(MyJedisProperties.class) // 启用配置属性的读取
public class MyJedisAutoConfiguration {

    @Autowired
    private MyJedisProperties myJedisProperties;

    private JedisPool jedisPool;

    @PostConstruct
    public void init(){
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(myJedisProperties.getMaxActive());
        config.setMaxWaitMillis(myJedisProperties.getMaxWait());
        jedisPool = new JedisPool(config,myJedisProperties.getHost(), myJedisProperties.getPort());
    }

    /**
     * 创建Jedis对象,加入IOC容器
     */

    @Bean
    public Jedis jedis(){
        return jedisPool.getResource();
    }

}

(5) 编写扫描配置类,目的是交给spring容器管理

注意:如果某个类没有放在在启动类的包及其子包下,要想运行就可以将该类添加到META-INF/spring.factories文件

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zhuimengren.myjedis.conf.MyJedisAutoConfiguration

3、启动器测试

说明:引入自定义的spring-boot-starter-my-jedis,不创建配置属性和配置类时,也可使用,同样可以修改默认配置,注入Jedis连接对象。 如果后面写一些公共的资源,可以写成启动器的方式,资源有了,默认配置也有了。

(1) 引入自定义启动器依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.mashibing</groupId>
    <artifactId>spring-boot-starter-my-jedis</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>
(2) 编写测试代码
import com.zhuimengren.myjedis.conf.MyJedisProperties;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;

@SpringBootTest
public class TestDemo {

    @Autowired
    private Jedis jedis;

    @Autowired
    private MyJedisProperties myJedisProperties;

    @Test
    public void test(){
        jedis.set("name", "admin");
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }

}
(3) 测试结果

下面是运行test方法后的输出结果,目的是测试不配置yml文件中的redis属性,使用默认的localhost地址以及默认的6379端口等。如果正常输出,说明可以正常使用自动配置类的默认属性值。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值