在我们的日常开发工作中,经常会有一些独立于业务之外的common模块,特别是现在微服务框架盛行的背景下,我们一个项目往往分成了多个单体项目,而这些单体项目中常会引用公司的一些公共组件,这个时候我们定义Starter,可以使这些单体项目快速搭起,我们只需要关注业务开发。复用的时候只需要将其在pom中引用依赖即可,SpringBoot会为我们完成自动装配,使用起来十分便捷。
命名规范:
- 官方命名空间
前缀:“spring-boot-starter-”
模式:spring-boot-starter-模块名
举例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc - 自定义命名空间
后缀:“-spring-boot-starter”
模式:模块-spring-boot-starter
举例:mybatis-spring-boot-starter
模块结构:
在springboot官方文档中,特别提到,我们需要创建两个module ,其中一个是autoconfigure module 一个是 starter module ,其中 starter module依赖 autoconfigure module。但是,这并不是强制规定的大多数开发情况下直接只用一个module写在一起也没有问题,这当然并没有错,这点官方也有说明:
You may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concerns
//如果不需要将自动配置代码和依赖项管理分离开来,则可以将它们组合到一个模块中。
实战:
下面实战案例使用一个模块进行创建
热插拔技术:
在项目中我们经常会在启动类Application上面加@EnableXXX注解,这个@Enablexxx注解就是一种热拔插技术,加了这个注解就可以启动对应的starter,当不需要对应的starter的时候只需要把这个注解注释掉就行,是不是很优雅呢?那么这是如何实现的呢?下面我们拿@EnableAsync注解来看一下其中原理:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class}) // 关键在@Import
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
在这里最关键的就是@Import({AsyncConfigurationSelector.class})这个点,此时需要将AsyncConfigurationSelector加入到IOC容器。但是如果在我们在关键的自动配置类上@ConditionalOnBean(AsyncConfigurationSelector.class)这个时候会出现什么情况?此时就会出现不加上@EnableAsync注解就无法对关键Bean进行注入,我们的功能就会无法正常开启。 当开启@EnableAsync注解时AsyncConfiguration Selector类就会注入到IOC容器,此时@ConditionalOnBean(AsyncConfigurationSelector.class)条件成立,相对应的自动配置Bean也能相继进行注入。这就是一个简单的实现原理,当然@EnableAsync的实现远不是这么简单,但是总的思想就是通过@Import来实现对bean的控制。在我们想实现类似功能时就可以考虑这种实现思路。
spring-boot-configuration-processor:
如果你集成了Spring Boot 校验库 ,就会对Properties进行校验。在配置application.yml就会有参数描述,就像java中的注释一样方便我们理解该配置的作用,其实这个就是java注释生成的。你需要依赖下面jar包为我们定义的ConfigurationProperties类生成元信息。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
然后就该依赖会对Properties 成员属性的注释进行提取生成一个spring-configuration-metadata.json文件,这就是配置描述的元数据文件。之后在我们在配置文件中进行输入的时候就会有对应提示,下面是一个简单演示不用在意细节。。。。都是我随便写的。
Spring Boot官方也对注释进行了一些规则约束:
不要以“The”或“A”开头描述。
对于boolean类型,请使用“Whether" 或“Enable”开始描述。
对于基于集合的类型,请使用“Comma-separated list”
如果默认时间单位不等同于毫秒,则使用java.time.Duration而不是long描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。
除非必须在运行时确定,否则不要在描述中提供默认值。
Demo:
下面是一个不健全的模块好多代码都没来得及写仅用于当前演示:
配置属性类:
@ConfigurationProperties(
prefix = "business-order"
)
public class BusinessOrderProperties {
/**
* hello,hello,hello
*/
private Boolean vipChannelEnabled = true;
/**
* hello,hello,hello
*/
private int sendMessageTimeout = 3000;
public Boolean getVipChannelEnabled() {
return vipChannelEnabled;
}
public void setVipChannelEnabled(Boolean vipChannelEnabled) {
this.vipChannelEnabled = vipChannelEnabled;
}
public int getSendMessageTimeout() {
return sendMessageTimeout;
}
public void setSendMessageTimeout(int sendMessageTimeout) {
this.sendMessageTimeout = sendMessageTimeout;
}
}
服务类OrderService ,此类为普通的service,提供给其他服务调用。
public interface OrderService {
String orderDemo();
}
public class OrderServiceImpl implements OrderService {
BusinessOrderProperties businessOrderProperties;
public String orderDemo(){
return "订单消息推送超时配置" + businessOrderProperties.getSendMessageTimeout();
}
public BusinessOrderProperties getBusinessOrderProperties() {
return businessOrderProperties;
}
public void setBusinessOrderProperties(BusinessOrderProperties businessOrderProperties) {
this.businessOrderProperties = businessOrderProperties;
}
}
自动配置类:
@Configuration
@EnableConfigurationProperties({BusinessOrderProperties.class})
@ConditionalOnWebApplication
public class BusinessOrderAutoConfiguration {
@Autowired
BusinessOrderProperties businessOrderProperties;
@Bean
public OrderService orderService(){
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.setBusinessOrderProperties(businessOrderProperties);
return orderService;
}
}
在resources文件夹下创建META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
vip.chihai.common.order.config.BusinessOrderAutoConfiguration
接下来就可以打包安装到本地仓库进行测试了:
下面随便找个项目引入下看看是否成功:
在配置文件中对配置进行修改,将默认3000修改为5000。因为我们之前引入了spring-boot-configuration-processor所以在写配置文件时会有相应提示
business-order:
send-message-timeout: 5000
控制台成功输出:
一些常用的条件注解:
@ConditionalOnClass:当类路径classpath下有指定的类的情况下进行自动配置
@ConditionalOnMissingBean:当容器(Spring Context)中没有指定Bean的情况下进行自动配置
@ConditionalOnProperty(prefix = “example.service”, value = “enabled”, matchIfMissing = true),当配置文件中example.service.enabled=true时进行自动配置,如果没有设置此值就默认使用matchIfMissing对应的值
@ConditionalOnMissingBean,当Spring Context中不存在该Bean时。
@ConditionalOnBean:当容器(Spring Context)中有指定的Bean的条件下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnWebApplication:当前项目是Web项目的条件下
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean