第八章 自动配置原理及自定义starter

飞一样的编程
飞一样的编程
擅长邻域:Java,MySQL,Linux,nginx,springboot,mongodb,微信小程序,vue

分类: springboot 专栏: springboot3.0新教材 标签: 自定义starter

2024-01-17 00:20:56 1710浏览

自定义starter

前期回顾

  • redis有几种数据类型
  • redis常用的命令有哪些
  • redis的应用场景有哪些
  • linux中redis启动命令
  • springboot访问redis的操作步骤
  • 定时任务是啥,应用场景
  • 文章浏览量,点赞量,评论量这种功能怎么实现的?
  • redis集群有几种?怎么搭建的,简单说说流程
  • 什么时候需要用到分布式锁?

    自动配置原理

    在第一个 Spring Boot 项目中,用户只须导入 spring-boot-starter-web 依赖,无须配置Web 的功能就可进行 Web 开发,这是因为 Spring Boot 提供了自动配置的功能。
    自动配置的分为两种情况:
  • SpringBoot 官方已经收录的第三方应用,下面称为内置自动配置类,如 Redis。

  • Spring Boot官方未收录的第三方应用,下面称为外部自动配置类,如 MyBatis。

    @SpringBootApplication

    作用:启动类上加的注解,这个类的同级别或者子包下面的组件才会被扫描到
    是一个复合注解,等价于
    @SpringBootConfiguration :表示这是一个 Spring Boot 的配置类,
    @ComponentScan:表示扫描当前包下的所有组件,
    @EnableAutoConfiguration:表示开启自动配置功能,显然这个注解就是要研究的重点

元注解

@Target:这是一个描述注解
image.png
image.png
@Retention:说明注解的生命周期以及作用域。有些注解是可以改变你代码运行结果的,有些注解不能(比如重写。@Override)
RetentionPolicy.SOURCE:只是在代码中有,编译后的class文件里没有,所以不会造成任何影响
RetentionPolicy.RUNTIME:对代码有影响
@Documented:注解标准文档

@EnableAutoConfiguration

最核心的就是@EnableAutoConfiguration
image.png
其中注解

  • @AutoConfigurationPackage 用于自动配置包扫描。它会根据启动类的位置,自动扫描并加载该类所在包及其子包中的所有组件。
  • @Import({AutoConfigurationImportSelector.class})作用是给容器导入自动配置类,导入哪些,需要进一步研究 AutoConfigurationImportSelector 类

image.png
我们关注下AutoConfigurationImportSelector 类的 selectImports方法,该方法用于获得所有自动配置类的全路径名称作为元素的数组,再将该数组中的所有这些自动配置类来加载到 Spring 容器中。image.png
搞清楚 getAutoConfigurationEntry 方法是如何获得包括了所有自动配置类的全路径名称的 List集合的。selectImports 方法内部找到 getAutoConfigurationEntry 方法
image.png
它关键是调用了getCandidateConfigurations(annotationMetadata, attributes)方法来获取包括了所有自动配置类的全路径名称的 List集合的,获取到这个集合后,还进行了必要的过滤、排除与筛选,然后封装到 AutoConfigurationEntry 对象中。
image.pngimage.png
这个方法调用了ImportCandidates 类的load方法加载编译在 classes 目录下的 META-INF/spring/下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,当类加载器读取此文件时,ImportCandidates 类将获取此文件中列出来的所有自动配置类的路径,再通过反射实例化对象,这样此文件中列出来的所有自动配置类就能加载到容器中了。
主要是找到org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
可以看看官方自动配置类(以redis为例)里的这个文件,再看看非官方的(以mybatis为例)
image.png
image.png
可见无论是官方的还是非官方的,这个文件名字都是一样的,打开看看
image.png

不过加载这些自动配置类的时候,并不会全部创建这些自动配置类的 Bean,因为这些自动配置类中一般都做了条件注解,只有符合条件的 Bean 才会创建,这些常用的条件包括类路径下有哪个类存在时才加载,所以通常需要先导入第三方库的依赖,一旦第三方库的依赖导入,条件中要求的类就存在了,当 Spring Boot 启动时,相关的 Bean 就会创建并加载到容器中,否则就不会创建 Bean。
典型的就是只有导入 spring-boot-starter-data-redis 依赖时,自动配置类 RedisAutoConfiguration 才会创建 RedisTemplate 的 Bean 放入容器中,程序中以下注入RedisTemplate 的代码才会生效:image.png

什么是条件注解

每一个 XxxxAutoConfiguration 自动配置类都是在某些条件之下才会生效的,这些条件的限制在 Spring Boot 中以注解的形式体现
拿Redis举例,先找到redis的自动配置类
image.png
该自动配置类里面包含了创建 RedisTemplate 实例的方法,生成一个 Bean 纳入 Spring容器管理。注解@ConditionalOnClass({RedisOperations.class})的作用是就是当 RedisOperations 类存在时才会创建这个自动配置类中的 Bean。所以简单来说,只要导入了清单中的某个依赖(jar 包)Spring Boot 启动时就会帮你自动配置好。
常见的条件注解如下:
@ConditionalOnBean: 系统的ioc容器里如果存在某个bean的情况下才会实例化这个bean
@ConditionalOnClass: 系统中如果存在某个类的情况下,才会实例化一个Bean.
@ConditionalOnExpression: 当表达式为true的时候, 才会实例化一个Bean.
@ConditionalOnMissingBean: 系统的ioc容器里如果不存在某个bean的情况下才会实例化这个bean
@ConditionalOnMissingClass: 系统中如果不存在某个类的情况下,才会实例化一个Bean.
@ConditionalOnNotWebApplication: 不是web应用,才会实例化一个Bean。
@ConditionalOnBean: 当容器中有指定Bean的条件下进行实例化。
@ConditionalohMissingBean: 当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass: 当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass: 当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication: 当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication: 当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression:基于SpEL表达式的条件判断。
@Condit ionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。

启动流程分析

当SpringApplication创建完毕后,就开始执行run方法。这个方法会执行以下关键步骤:

  1. 准备应用程序上下文的环境:首先,run() 方法会根据当前的环境(如开发环境、生产环境)加载适当的配置文件,并准备应用程序上下文所需的环境变量。
  2. 执行应用程序上下文的刷新:接下来,run() 方法会调用 refresh() 方法来刷新应用程序上下文。在这个阶段,Spring 会完成所有 Bean 的实例化、依赖注入、属性设置等操作,并触发各个 Bean 的初始化回调方法。
  3. 处理应用程序启动事件:一旦应用程序上下文刷新完成,run() 方法会发布一个 ApplicationStartedEvent 事件,表示应用程序已经启动完毕。这个事件可以被其他组件监听到,进行相应的处理。
  4. 运行应用程序:最后,run() 方法会调用应用程序上下文中的 start() 方法,启动应用程序。具体的启动逻辑会根据应用程序的类型和配置而有所不同。例如,对于 Web 应用程序,会启动内嵌的 Web 服务器,并监听指定的端口。

总体而言,ConfigurableApplicationContext 的 run() 方法是 Spring 应用程序启动的入口点,它负责完成应用程序上下文的初始化、刷新和启动等关键操作。通过该方法的调用,可以实现 Spring 应用程序的快速启动和运行。
请注意,以上描述是基于 Spring Boot 2.x 版本的启动流程。Spring Boot 3.x 版本,可能会有一些改变。

image.png
image.png
我们可以在resources目录下新建一个banner.txt来修改默认的banner
自定义banner.txt

 
//							_ooOoo_								  //
//						   o8888888o							  //	
//						   88" . "88							  //	
//						   (| ^_^ |)							  //	
//						   O\  =  /O							  //
//						____/`---'\____							  //						
//					  .'  \\|     |//  `.						  //
//					 /  \\|||  :  |||//  \						  //	
//				    /  _||||| -:- |||||-  \						  //
//				    |   | \\\  -  /// |   |						  //
//					| \_|  ''\---/''  |   |						  //		
//					\  .-\__  `-`  ___/-. /						  //		
//				  ___`. .'  /--.--\  `. . ___					  //	
//				."" '<  `.___\_<|>_/___.'  >'"".				  //
//			  | | :  `- \`.;`\ _ /`;.`/ - ` : | |				  //	
//			  \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//		========`-.____`-.___\_____/___.-`____.-'========		  //	
//				             `=---='                              //
//		^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//         佛祖保佑       永无BUG		永不修改				  //

其实咱们可以类比替代web.xml文件的那个类
image.png

总结:我认为自动配置就是干掉之前ssm框架里的application.xml的配置
run启动说白了就是拉起spring框架 拉起springmvc框架,相当于干掉之前的web.xml(也会读取相关配置,决定是加载dev环境还是加载prod环境),启动的过程中肯定还有很多事情做,比如打印日志啥的,打印banner,打印整个项目启动时长,端口啥的

启动器starter

在 Spring Boot 中,启动器(Starter)是一种用于简化依赖项管理的方式。每个启动器都包含了一组预定义的依赖项,以及适当的版本号和传递依赖项,可以快速地将所需的依赖项添加到项目中。
启动器通常使用 “spring-boot-starter-” 前缀命名,例如 spring-boot-starter-web、spring-boot-starter-data-jpa 等。每个启动器都包含一个或多个 Maven 依赖项,这些依赖项包含了在特定场景下使用 Spring Boot 所需的所有依赖项。
例如,如果使用 spring-boot-starter-web 启动器,它会自动包含以下依赖项:

  • spring-boot-starter
  • spring-boot-starter-json
  • spring-boot-starter-tomcat
  • spring-web
  • spring-webmvc

这些依赖项涵盖了使用 Spring Boot 创建 Web 应用程序所需的所有基本依赖项。通过使用启动器,我们可以轻松地添加这些依赖项,而不需要手动添加每个依赖项。
除了标准的启动器之外,还可以创建自定义启动器,以便在项目中添加自定义依赖项。自定义启动器可以基于现有的启动器进行扩展,也可以添加新的依赖项和配置文件,并为它们提供默认值。
通过使用启动器,Spring Boot 可以简化依赖项管理,并提供更加一致和可靠的依赖项版本。这样,我们就可以专注于应用程序的开发,而不必过多关注依赖项管理的细节。

自定义starter

在日常开发工作中,可能会需要开发一个通用模块,以供其它工程复用。SpringBoot提供这样的功能机制:把通用模块封装成一个个 starter,这样其它工程复用的时候只需要在pom.xml 中引用依赖即可,由SpringBoot 自动装配。
咱们可以模仿mybatis的自动配置类和属性类写一个启动器(因为我们也属于非官方的第三方启动器)
image.png
这就是mybatis的自动配置类以及关联的properties文件。
image.png

下面假设为用户注册功能创建一个自动配置类。

自定义用户注册启动器

1.创建autoconfigure模块

 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.0</version>
  </parent>
 <dependencies>
 <!--导入自动配置的相关依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>

    <!--方便idea能检测到该依赖中用到的配置属性,能够自动补全,其实就是在编译的时候在
    META-INF下生成一个spring-configuration-metadata.json文件-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
  </dependencies>
@Component
@Data
@ConfigurationProperties(prefix = "jf3q.register")
public class RegisterProperties {
    private String username="admin";
    private String password="123456";

}
@Data
@AllArgsConstructor
public class RegService {

    private String username;
    private String password;

    public void login(){
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
    }
}
@Configuration
@EnableConfigurationProperties({RegisterProperties.class})
public class RegisterAutoConfiguration {

    @Autowired
    RegisterProperties registerProperties;
    @Bean
    @ConditionalOnMissingBean(RegService.class)//不存在这个bean的时候创建
    RegService regService(){
        return new RegService(registerProperties.getUsername(),registerProperties.getPassword());
    }
}

2.创建stater模块

把上面的模块打包安装到本地仓库,然后在此模块引入进来就行了

	  <dependencies>
    <dependency>
      <groupId>com.jf3q</groupId>
      <artifactId>RegisterAutoConfiguration</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>

3.测试模块

jf3q:
  register:
    password: 5678
    username: xiaojie
@SpringBootTest
public class StarterTest {
    @Autowired
    RegService regService;
    @Test
    void testUser(){
        regService.login();
    }
}

自定义redis启动器

1.创建autoconfigure模块

  • 修改pom文件
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
    </parent>

<dependencies>
        <!--导入自动配置的相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <!--方便idea能检测到该依赖中用到的配置属性,能够自动补全,其实就是在编译的时候在
        META-INF下生成一个spring-configuration-metadata.json文件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--jedis依赖-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>建一个属性类
  • 新建一个属性类
@Data
@ConfigurationProperties(prefix = "jfit.redis")
public class MyRedisProperties {
    private int database = 0;
    private String host="localhost";
    private int timeout=2000;//单位是秒,默认值为0,表示无限制
    private String password;
    private int port=6379;
}

  • 新建一个自动配置类
@Configuration
@EnableConfigurationProperties(MyRedisProperties.class)
@ConditionalOnClass(JedisPool.class)
public class MyRedisAutoConfiguration {
    @Autowired
    MyRedisProperties redisProperties;

    @Bean
    public JedisPoolConfig setPoolConfig(){
        return new JedisPoolConfig();
    }

    @Bean
    public JedisPool setPool(){
        JedisPool jedisPool = new JedisPool(setPoolConfig(),
                redisProperties.getHost(),
                redisProperties.getPort(),
                redisProperties.getTimeout(),
                redisProperties.getPassword(),
                redisProperties.getDatabase());

        return  jedisPool;

    }

    //RedisAPI这是我专门定义的一个工具类
    @Bean
    public RedisAPI setProperties(){

        RedisAPI redisAPI = new RedisAPI();
        redisAPI.setJedisPool(setPool());

        return redisAPI;

    }
}
  • 工具类
public class RedisAPI {

    JedisPool jedisPool;
    Jedis jedis;

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public  void set(String key, String value, Integer expireTime){
        try {
            jedis = jedisPool.getResource();
            jedis.setex(key,expireTime,value);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedisPool.close();
        }
    }

    public  void set(String key, String value){
        try {
            jedis = jedisPool.getResource();
            jedis.set(key,value);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
             jedisPool.close();
        }
    }




    public String get(String tokenString) {
        try {
            jedis = jedisPool.getResource();
            return jedis.get(tokenString);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedisPool.close();
        }


        return null;
    }

}
  • resources下META-INF下新建一个spring.factories(新版本不是这个文件,切记,看上面写的)
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.redis.spring.boot.redisspringbootautoconfigure.MyRedisAutoConfiguration

2.创建starter模块

在pom文件中引用一下上面步骤安装好的依赖即可

 <dependencies>
        <dependency>
            <groupId>com.jf3q</groupId>
            <artifactId>redis-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>


3.测试模块

在pom文件中把第二步安装好的依赖导入就可以使用了
image.png
注意:在创建自动配置模块的时候,一直无法生成那个json文件。导致application.properties里不能自动提示
解决方案:我建自动配置模块的时候把springboot-test引入了导致无法生成json文件

作业

自定义一个启动器,模仿老师的写法,试着写一个redis的启动器(用jedis做增删查)

好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695