我们都知道,Spring Boot最大的特点就是自动装配,简化依赖,可以让我们快速的搭建项目。使用Spring Boot之后,我们要想在项目中使用一些其他框架,只需要引入对应的Starter依赖就可以了。
那么你在实际开发中是否也开发过一些基础功能,这些功能需要在你们的Spring Boot项目中使用呢?
而这些功能可能在其他项目中可能也需要用到,如果我们把相同的功能在另一个项目中重新再写一遍的话肯定不是一个好方法。
我们可以将我们的功能做成一个对应的Starter模块,在项目中要使用时直接作为Maven依赖添加进去就OK了,这样一来,不仅不用重复开发,并且在功能做升级时,也能有更好的版本管理。
接下来我们来构建一个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
类,用于从消息中心订阅特定的事件;因为我们的Starter要在多个微服务应用中使用,所以我们的Starter要能够使用Maven构建,方便其他应用引入。
首先我们来创建一个maven项目spring-boot-starter-eventmessage
。
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功能提供一个入口,我们需要提供一个@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
文件。
在该文件中配置我们的Configuration
类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.heiz123.event.EventAutoConfiguration
复制代码
Spring Boot会搜索classpath下的所有spring.Factories
文件,并加载其中声明的配置。
有了EventAutoConfiguration
类之后,我们现在就有了一个可以自动装配spring-boot-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
类时才激活自动配置。
在我们的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-eventmessage
。
我们可以将某些特性功能包装到Starter程序中,方便在任何其他Spring Boot应用中很方便快捷地使用。要构建一个Starter只需要简单的几个步骤:
提供自动配置类Configuration,让它可以被禁用和启用,对于一些可变的信息提供可配置的参数。