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端口等。如果正常输出,说明可以正常使用自动配置类的默认属性值。