自定义一个starter一般需要经过以下几个过程

步骤

完整项目会以免积分附件形式发布,有兴趣的可以down下来跟着试一下
或使用码云地址:https://gitee.com/master336/demo-spring-boot-starter

前提

SpringBoot项目开启自动配置 @EnableAutoConfiguration(默认开启了,参见@SpringBootApplication)

1. 创建项目

这一步就一个关注点,项目名建议**-spring-boot-starter,据说是springboot官方建议的命名方式,有只要此建议地址的朋友可以留言以方便补充。这里给两个常用starter命名示例

springboot常用组件_System


springboot常用组件_自定义_02

2. maven依赖倒入

首先再次提一下命名问题,再看一眼上一步。
引入依赖,核心依赖就一个,目的是为了使用其注解

<dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-autoconfigure</artifactId>
         <version>2.2.5.RELEASE</version>
     </dependency>
 </dependencies>

3. 定义自动装配属性对应的配置类(非必须)

如果有允许用户自定义配置的地方,可以借助Configurationproperties注解轻松将配置转换为Java对象,方便注入使用。

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
    private String name = "default name";
    private Integer age = 18;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

简单说一下ConfigurationProperties这个注解,这个用于声明该类为配置类,prefix声明配置项的开头,如上代码中,配置装载的是以demo开头的配置,yml(或peopreties)文件配置如下:

demo:
  name: 张三
  age: 22

注意: Springboot本着"约定大于配置"原则,请合理设置默认值。

4. 定义需要交由Spring管理的Bean类及其具体实现方法

通过定义已上一步配置类为入参的构造方法获取配置类信息,用以获取可由用户自定义的一些属性值。

import cn.com.demo.autoconfigure.properties.DemoProperties;
public class DemoService {
    private DemoProperties demoProperties;

    public DemoService(DemoProperties demoProperties) {
        this.demoProperties = demoProperties;
    }
    public void printProperties(){
        if ( demoProperties == null){
            System.out.println("null propertoes");
        }else {
            System.out.println("name: "+demoProperties.getName() +"\t age:"+demoProperties.getAge());
        }
    }
}

看到这个类可能存在疑问:

  1. 这个类不是要注册成Bean交由Spring管理吗?为什么没有定义声明@Component@Bean等之类的标签标注一下?
    其实这个问题是这样的,Springboot中声明Bean除了要使用Spring中符合要求的注解外,还需要定义@Configuration(不然会因为没在扫描路径(ComponentScan)下导致无法被Spring管理,为什么不会一会儿再第6步里说明),用于告诉SpringBoot你这是个Bean配置管理类,这样一来就成了下一步要干的事了,这里把Bean的声明和实现分开,更清晰的管理自己的Bean。

另外:值得注意的是@Configuration 标注的也是一个Bean~,只是有特殊含义,其在某种情况下也是可以被其他方案代替的(参见@Configuration)。

5. 定义Configuration类完成Bean声明

起始就是完成Bean的new过程,即告诉Spring这个Bean是怎么来的。
import cn.com.demo.autoconfigure.properties.DemoProperties;
import cn.com.demo.autoconfigure.service.DemoService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfigure {
    /**属性自动从Springboot配置文件装配,DemoProperties里声明前缀*/
    private DemoProperties demoProperties;
    /* 如果无需(或不允许)用户修改配置,这里可以使用默认构造(简单来说就是别写任何构造方法了)
    public DemoAutoConfigure(){
        System.out.println("init no patam ");
    }*/
    public DemoAutoConfigure(DemoProperties demoProperties) {
        this.demoProperties = demoProperties;
    }

    /**
     * 自定义Bean以供外部使用
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DemoService demoService(){
        return new DemoService(demoProperties);
    }
}

@Bean用户告诉容器这是一个Bean,方法名为BeanName(可以通过name指定)
@ConditionalOnMissingBean 如果容器中没有这个Bean,会通过此定义创建改Bean,即如果不需要再次封装,可以使用这个Bean进行操作(相当于默认处理Bean,类似druid里定义datasource这个Bean一样,可参见:com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure)

6. 编写META-INF/spring.factories文件定义自动配置类

即告诉Spring自动装配的类路径,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.com.demo.autoconfigure.DemoAutoConfigure

这里有个疑问:为什么已经配置了@Configuration还要整个spring.factories文件呢?
这个问题在于@Configuration会不会生效的问题,如果@Configuration所在类在你的项目扫描路径下,就不需要配置,如果不在,那就需要配置(@Configuration)。如果你作为一个组件发布,你无法保证ComponentScan能扫描到你的组件,所以就有了配置这个的方法。

spring.factories文件的加载在:
new SpringApplication()
–> setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
–>private Collection getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)
–>Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
–>private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader)
–>private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader)
spring.factories中配置生效(Bean被创建)是在:
SpringApplication.refreshContext(context),过程比较复杂,感兴趣可以深入跟踪一下。

以上为执行过程,对照代码可能更容易懂~

7. 打包发布

如果本地测试的话:

mvn clean compile install

即可被本地其他项目以来,如果需要发布仓库被别人共用的话请使用deploy

8. 需要的项目上引入依赖即使用

<dependency>
   <groupId>cn.com.demo</groupId>
    <artifactId>demo-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

作为一个Bean正常注入即可使用

// 需要用哪个注入那个即可
    @Resource
    DemoService demoService;
    // 一般咱们不用这个
    @Resource
    DemoAutoConfigure demoAutoConfigure;