还在curd吗?封装属于自己的SpringStarter

什么是Starter

Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件)进行自动配置。

使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,Spring Boot就能自动扫描并加载相应的模块并设置默认值,做到开箱即用

为什么使用Starter

在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。

如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,并在starter中设置好默认值,复用的时候只需要将其在pom中引用依赖即可,Spring Boot为我们完成自动装配,做到开箱即用。

Springboot自动配置

SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,Spring Boot就能自动扫描各个jar包下classpath路径的spring.factories文件,加载自动配置类信息,加载相应的bean信息并启动相应的默认配置。

Spring Boot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

大家可以看看我之前写的一篇文章,详细介绍了springboot自动配置的流程:一文搞懂SpringBoot自动配置原理 - 掘金 (juejin.cn)

spring.factories

Spring Boot会默认扫描跟启动类平级的包,如果我们的Starter跟启动类不在同一个主包下,需要通过配置spring.factories文件来配置生效,SpringBoot默认加载各个jar包下classpath路径的spring.factories文件,配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration

Starter开发常用注解

注解使用已经大大方便我们开发,再也不需要写xml配置文件了,SpringBoot经过查找spring.factories文件,加载自动配置类,而自动配置类中定义了各种运行时判断条件,如@ConditionalOnMissingBean(A.class)等,只要ioc容器中没有指定的A类型的bean信息,该配置文件才会生效。

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

Full全模式和Lite轻量级模式

@Configuration(proxyBeanMethods = false)
public class AppConfig {
    
    //放一份myBean到ioc容器
    @Bean
    public Mybean myBean() {
        return new Mybean();
    }

    //放一份yourBean到ioc容器
    @Bean
    public YourBean yourBean() {
        System.out.println("==========");
        //注意:@Configuration(proxyBeanMethods = false):myBean()方法不代理,直接调用
        //注意:@Configuration(proxyBeanMethods = true):myBean()方法代理,从ioc容器拿
        return new YourBean(myBean());
    }
}

什么时候用Full全模式,什么时候用Lite轻量级模式?

Starter命名规范

开发Starter

1. 创建Starter项目

还在curd吗?封装属于自己的Spring-Boot-Starter

2. 添加依赖

<?xml version="1.0" encoding="UTF-8"?>

    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.1
         
    

    4.0.0
    com.ljw
    ljw-spring-boot-starter
    1.0
    
   
        1.8
        8
        8
    


    

        
            org.springframework.boot
            spring-boot-starter
        

        
        
            org.springframework.boot
            spring-boot-autoconfigure
        

        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

    

3. 编写属性类

@ConfigurationProperties可以定义一个配置信息类,和配置文件进行映射

@ConfigurationProperties(prefix = "ljw.config")
public class HelloProperties {

    private String name = "hello 默认值!";

    private int age = 8;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4. 自定义业务类

这里可以模拟一些获取了配置文件信息的进行业务操作的业务类

public class HelloService {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String hello() {
        return "HelloService{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

5. 编写自动配置类

命名规范:XxxAutoConfiguration

@Configuration(proxyBeanMethods = false)
// 当存在某个类时,此自动配置类才会生效
@ConditionalOnClass(value = {HelloService.class})
// 导入我们自定义的配置类,供当前类使用
@EnableConfigurationProperties(value = HelloProperties.class)
// 只有非web应用程序时此自动配置类才会生效
@ConditionalOnWebApplication
//判断ljw.config.flag的值是否为“true”, matchIfMissing = true:没有该配置属性时也会正常加载
@ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
public class HelloAutoConfiguration {

    /**
     * @param helloProperties 直接方法签名入参注入HelloProperties,也可以使用属性注入
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(HelloService.class)
    //@ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
    public HelloService helloService(HelloProperties helloProperties) {
        HelloService helloService = new HelloService();
        //把获取的信息注入
        helloService.setName(helloProperties.getName());
        helloService.setAge(helloProperties.getAge());
        return helloService;
    }

}

注:这里配置一个web应用才能注入,并且ljw.config.flag的值是否为“true”或者不配置该key才能注入HelloService服务

6. 编写spring.factories

把自动配置类HelloAutoConfiguration配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration的key下,springboot会自动加载该文件并根据条件装配

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.ljw.starter.config.HelloAutoConfiguration

7. 编写配置提示文件(非必须)

additional-spring-configuration-metadata.json

配置additional-spring-configuration-metadata.json文件后,在开发人员的IDE工具使用个人编写的配置读取很有效的在application.properties或application.yml文件下完成提示。

配置详细格式参数可查看文档

我的配置:

{"properties": [
    {
      "name": "ljw.config.name",
      "type": "java.lang.String",
      "defaultValue": "hello 默认值!这里配置的是提示,真正默认值在Properties里面",
      "description": "这是字符串名称啊."
    },
    {
      "name": "ljw.config.age",
      "defaultValue": 8,
      "description": "这是int类型的年龄啊.",
      "deprecation": {
              "reason": "过时原因.",
              "replacement": "替代key是:ljw.config.age22",
              "level": "warning"
            }
    }
]}

大家参考下面properties表格进行配置上的理解。

名称

类型

目的

name

String

属性的全名。名称采用小写的周期分隔形式(例如server.address)。此属性是强制性的。

type

String

属性的数据类型的完整签名(例如java.lang.String),但也是完整的泛型类型(例如java.util.Map)。您可以使用此属性来指导用户可以输入的值的类型。为了保持一致性,通过使用其包装对应项(例如,boolean变为java.lang.Boolean)来指定基元的类型。请注意,此类可能是一个复杂类型,它从Stringas绑定的值转换而来。如果类型未知或基本类型,则可以省略。

description

String

可以向用户显示的组的简短描述。如果没有可用的描述,则可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾。

sourceType

String

贡献此属性的源的类名称。例如,如果属性来自带注释的类@ConfigurationProperties,则此属性将包含该类的完全限定名称。如果源类型未知,则可以省略。

defaultValue

Object

默认值,如果未指定属性,则使用该值。如果属性的类型是数组,则它可以是值数组。如果默认值未知,则可以省略。

deprecation

数组

过时的描述。

deprecation每个properties元素的属性中包含的JSON对象可以包含以下属性:

名称

类型

目的

level

String

弃用级别,可以是warning(默认)或error。当属性具有warning弃用级别时,它仍应绑定在环境中。但是,当它具有error弃用级别时,该属性不再受管理且不受约束。

reason

String

该属性被弃用的原因的简短描述。如果没有可用的原因,可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾。

replacement

String

替换此不推荐使用的属性的属性的全名。如果此属性没有替换,则可以省略。

spring-configuration-metadata.json

spring-configuration-metadata.json代码量挺大的,为了方便我们可以通过IDE来生成,这里使用的是idea。

在idea设置中搜索Annotation Processors,接下来勾住Enable annonation processing就完成了。 在编译打包后的文件中看到自动生成的spring-configuration-metadata.json。这个文件不用我们编写

还在curd吗?封装属于自己的Spring-Boot-Starter

下面是自动生成的:

{
  "groups": [
    {
      "name": "ljw.config",
      "type": "com.ljw.starter.properties.HelloProperties",
      "sourceType": "com.ljw.starter.properties.HelloProperties"
    }
  ],
  "properties": [
    {
      "name": "ljw.config.name",
      "type": "java.lang.String",
      "description": "这是字符串名称啊.",
      "sourceType": "com.ljw.starter.properties.HelloProperties",
      "defaultValue": "hello 默认值!这里配置的是提示,真正默认值在Properties里面"
    },
    {
      "name": "ljw.config.age",
      "type": "java.lang.Integer",
      "description": "这是int类型的年龄啊.",
      "sourceType": "com.ljw.starter.properties.HelloProperties",
      "defaultValue": 8,
      "deprecated": true,
      "deprecation": {
        "level": "warning",
        "reason": "过时原因.",
        "replacement": "替代key是:ljw.config.age22"
      }
    }
  ],
  "hints": []
}

测试Starter

1. 前置环境

install打包自定义starter项目:ljw-spring-boot-starter

新建项目:ljw-test-spring-boot-starter

2. 添加依赖

引入打好包的自定义starter


        
    
        org.springframework.boot
        spring-boot-starter
    
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
    
        com.ljw
        ljw-spring-boot-starter
        1.0
    

3. 测试类

@Service
public class TestController implements CommandLineRunner {

    /**
     * 注入自定义starter服务
     */
    @Resource
    private HelloService helloService;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(helloService.hello());
    }
}

4. 修改配置文件

输入前缀可以看出已经有提示了

还在curd吗?封装属于自己的Spring-Boot-Starter

ljw.config.name=ljw hello!
ljw.config.age=99
ljw.config.flag=true
#不会注入
#ljw.config.flag=true1
# 可以看到哪些自动配置了
debug=true

5. 运行程序打印

HelloService{name='ljw hello!', age=99}

6. 查看自动配置类生效的方法

通过启用 debug=true 属性,让控制台打印自动配置报告,这样就可以很方便地知道哪些自动配置类生效。

   HelloAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.ljw.starter.service.HelloService' (OnClassCondition)
      - @ConditionalOnWebApplication (required) found 'session' scope (OnWebApplicationCondition)
      - @ConditionalOnProperty (ljw.config.flag=true) matched (OnPropertyCondition)

   HelloAutoConfiguration#helloService matched:
      - @ConditionalOnMissingBean (types: com.ljw.starter.service.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)

作者:小伙子vae
链接:https://juejin.cn/post/7047674475331977224
来源:稀土掘金

展开阅读全文

页面更新:2024-03-06

标签:注解   容器   实例   属性   加载   条件   名称   类型   文件   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top