来~ 手写一个spring-boot-starter

来~ 手写一个spring-boot-starter_第1张图片

前言

我们都知道,Spring Boot最大的特点就是自动装配,简化依赖,可以让我们快速的搭建项目。使用Spring Boot之后,我们要想在项目中使用一些其他框架,只需要引入对应的Starter依赖就可以了。

来~ 手写一个spring-boot-starter_第2张图片

那么你在实际开发中是否也开发过一些基础功能,这些功能需要在你们的Spring Boot项目中使用呢?

而这些功能可能在其他项目中可能也需要用到,如果我们把相同的功能在另一个项目中重新再写一遍的话肯定不是一个好方法。

我们可以将我们的功能做成一个对应的Starter模块,在项目中要使用时直接作为Maven依赖添加进去就OK了,这样一来,不仅不用重复开发,并且在功能做升级时,也能有更好的版本管理。

构建一个spring-boot-starter

接下来我们来构建一个spring-boot-starter,为了方便描述,假设我们的这个Starter的主要功能是用来方便的发送和接收异步事件。注意我们本文的目的主要是讲如何构建Starter,所以对于发送接收异步事件的实现细节不会展开描述。

基础概念

在开始构建之前,我们需要先同步几个基本的概念。

Application Context

只要你对Spring有所了解,那么一定知道Application Context,这是一个用于管理Spring bean的容器,它包含我们项目中所有的Controller、Service、Repository、Component等所有的对象。

@Configuration

@Configuration是Spring中的一个注解,使用该注解标注的类相当于是Spring的xml配置文件,其主要目的是为Application Context提供一些Bean对象。你可以理解为@Configuration标注的类就是一个Bean对象的工厂。

Auto-Configuration

在Spring Boot中有一个@EnableAutoConfiguration注解,它通过读取spring.factories文件里的EnableAutoConfiguration下面指定的类,来初始化指定类下面的所有加了@Bean的方法,并初始化这Bean。并且可以指定条件,按照具体的条件决定是否要自动装配Bean对象。


创建工程

接下来我们开始我们Starter的构建,我们给它一个名字叫 spring-boot-starter-eventmessage

假设我们需要在一个微服务环境中,要实现一个允许各个服务之间进行异步通信。我们的spring-boot-starter-eventmessage主要提供以下功能:

  • 首先,我们需要有一个EventPublisher对象,这个对象用于将事件发送到消息中心;
  • 然后需要有一个EventListener类,用于从消息中心订阅特定的事件;

来~ 手写一个spring-boot-starter_第3张图片

因为我们的Starter要在多个微服务应用中使用,所以我们的Starter要能够使用Maven构建,方便其他应用引入。

首先我们来创建一个maven项目spring-boot-starter-eventmessage

来~ 手写一个spring-boot-starter_第4张图片

pom.xml文件如下:



    4.0.0

    com.heiz123
    spring-boot-starter-eventmessage
    1.0.0

    
        8
        8
    

    
        
            org.springframework.boot
            spring-boot-starter
            2.4.2
        
    


复制代码

让starter自动装配

接下来我们需要对我们的Starter功能提供一个入口,我们需要提供一个@Configuration类:

@Configuration
public class EventAutoConfiguration {

    @Bean
    public EventPublisher eventPublisher(List listeners) {
        return new EventPublisher(listeners);
    }

复制代码

EventAutoConfiguration包括我们提供Starter功能所需的所有@Bean定义。因为我们的Starter只需要提供一个事件发布的功能,所以这里主需要往ApplicationContext中添加一个EventPublisher对象。

我们的EventPublisher需要知道所有的EventListener,这样它才能将事件传递给它们,所以我们让Spring注入ApplicationContext中所有可用的EventListener的列表。

接下来到了重要环节,如何让我们的Starter能够自动配置呢,需要在META-INF/目录中创建一个spring.factories文件。

来~ 手写一个spring-boot-starter_第5张图片

在该文件中配置我们的Configuration类:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.heiz123.event.EventAutoConfiguration
复制代码

Spring Boot会搜索classpath下的所有spring.Factories文件,并加载其中声明的配置。

有了EventAutoConfiguration类之后,我们现在就有了一个可以自动装配spring-boot-starter的入口点。

让Starter支持可选

在实际开发场景中,可能我们对某一功能需要关闭,等到测试环境和线上环境时才会打开,那么我们的starter应该支持功能的关闭或者禁用。

我们可以使用Spring Boot的Conditional注解使我们的Configuration成为可选的:

@Configuration
@ConditionalOnProperty(value = "eventstarter.enabled", havingValue = "true")
@ConditionalOnClass(name = "com.heiz123.RabbitConnector")
@Slf4j
public class EventAutoConfiguration {

    @Bean
    public EventPublisher eventPublisher(List listeners) {
        log.info("event-message 自动装配中...");
        return new EventPublisher(listeners);
    }
}
复制代码

@ConditionalOnProperty注解的作用是只有eventstarter.enabled属性设置为true,才会将EventAutoConfiguration添加到ApplicationContext中。这样在使用了我们starter的应用中就可以通过eventstarter.enabled属性来动态控制我们的starter是否启用。

@ConditionalOnClass注解的作用是仅在classpath上有com.heiz123.RabbitConnector类时才激活自动配置。

让Starter可配置

在我们的EventAutoConfiguration中,需要注入应用中所有的EventListener到我们的EventPublisher中,但是有可能使用我们Starter的应用并不想让所有的EventListener都处理,可能只对一部分Event关注,那么我们就应该支持使用方能够便捷地进行配置。

比如在application.properties文件或者application.yml文件中可以进行如下配置:

eventstarter.listener.enabled-events:foo,bar
复制代码

yml文件:

eventstarter:
  listener:
    enabled-events:
      - foo
      - bar
复制代码

为了在我们的Starter的代码中能轻松访问这些属性,我们可以提供一个@ConfigurationProperties类:

@ConfigurationProperties(prefix = "eventstarter.listener")
class EventListenerProperties {

    /**
     * 会被传递给EventListener中的所有Event的类型。
     * 其他Event将会被忽略
     */
    private List enabledEvents = Collections.emptyList();

    public List getEnabledEvents() {
        return enabledEvents;
    }

    public void setEnabledEvents(List enabledEvents) {
        this.enabledEvents = enabledEvents;
    }
}
复制代码

然后我们通过使用@EnableConfigurationProperties注解我们的入口点配置来启用EventListenerProperties类:

@Configuration
@EnableConfigurationProperties(EventListenerProperties.class)
@Slf4j
public class EventAutoConfiguration {

    @Bean
    public EventPublisher eventPublisher(List listeners) {
        log.info("event-message 自动装配中...");
        return new EventPublisher(listeners);
    }
}
复制代码

最后,我们可以在需要的地方注入EventListenerProperties 对象,例如在抽象的EventListener类中过滤掉不关注的事件:

public abstract class EventListener {

    private final EventListenerProperties properties;

    public EventListener(EventListenerProperties properties) {
        this.properties = properties;
    }

    public void receive(Event event) {
        // 如果事件是Eanbled并且订阅了。
        if (isEnabled(event) && isSubscribed(event)) {
            onEvent(event);
        }
    }

    private boolean isSubscribed(Event event) {
        return event.getType().equals(getSubscribedEventType());
    }

    private boolean isEnabled(Event event) {
        return properties.getEnabledEvents().contains(event.getType());
    }

    protected abstract String getSubscribedEventType();

    protected abstract void onEvent(Event event);
}
复制代码

到这里我们的Starter基本就可以提供给别人使用了。

开始使用

实践是检验真理的唯一标准。我们来在一个新项目中引入一下我们的spring-boot-starter-eventmessage

首先创建一个spring-boot项目,我们就叫test-starter

然后我们只需要在Maven中添加Starter的依赖坐标信息。

        
            com.heiz123
            spring-boot-starter-eventmessage
            1.0.0
        
复制代码

接下来启动我们的SpringBoot项目。启动类如下:

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try {
            SpringApplication.run(Main.class, args);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}
复制代码

运行结果:

来~ 手写一个spring-boot-starter_第6张图片

说明我们已成功集成spring-boot-starter-eventmessage

总结

我们可以将某些特性功能包装到Starter程序中,方便在任何其他Spring Boot应用中很方便快捷地使用。要构建一个Starter只需要简单的几个步骤:

提供自动配置类Configuration,让它可以被禁用和启用,对于一些可变的信息提供可配置的参数。

你可能感兴趣的:(程序员,Java,Spring,spring-boot,spring,spring,boot,java,数据结构)