本文主要内容:
- 如何封装一个
spring-boot-starter
在编码实现之前、首先考虑如下几个问题:
- 如何读取配置文件中的配置信息?
- 如何使用读取到的配置信息?
- 如何自动配置到应用中?
读取配置文件
springboot应用配置文件常用的有yml和properties文件格式、不管怎么是采用那种配置方式、我们都需要把这些信息封装到一个对象中、所以为了我们方便统一管理配置信息、需要一个封装此starter信息的类。除此之外还需要考虑一个事情、就是如何将配置信息注入到这个类上。
以自定义序列化json字符串为例(功能: 给序列化添加自定义前缀和后缀)。首先创建一个maven工程。命名格式如下:
- 如果是我们开发者自己开发的 starter 组件(即属于第三方组件),那么命名规范是 {name}-spring-boot-starter,
- 如果是 SpringBoot 官方自己开发的组件,则命名为 spring-boot-starter-{name} 。
我的取名为super-json-spring-boot-starter在pom.xml文件中引入如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
<!-- 编写配置文件时idea提示配置信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.8.RELEASE</version>
<optional>true</optional>
</dependency>
<!-- json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- 自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
</dependencies>
接着定义一个java类封装配置文件信息:
@ConfigurationProperties(prefix = "super.json")
public class SuperJsonProperties {
public static final String DEFAULT_PREFIX = "super";
public static final String DEFAULT_SUFFIX = "json";
private String prefix = DEFAULT_PREFIX;
private String suffix = DEFAULT_SUFFIX;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
代码说明:
@onfigurationProperties(prefix = "super.json"):是将配置文件中的前缀为super.json的这一模块的配置信息封装到这个类里面。
为什么不用@value?
虽然@Value注解也可以读取配置文件的信息、但是如果模块配置信息较多的情况下、使的代码冗余。主要原因有如下几点:
@ConfigurationProperties更适合于绑定一组相关的配置属性,尤其是当属性数量较多或需要复杂类型的绑定时。@ConfigurationProperties的配置类提供了集中管理和类型安全的优势,便于维护和调试。@Value更适合于注入少量的简单配置属性,或需要使用 SpEL 表达式进行复杂处理的场景。
@Value注解直接在代码中使用,如果属性较多,维护起来会比较麻烦。
使用配置信息
当读取到这些信息之后、如何使用呢?这就很简单咯、想怎么用就怎么用、代码如下:
public class JsonService {
private String prefixName;
private String suffixName;
public String objectToJson(Object object) {
return prefixName + JSON.toJSONString(object) + suffixName;
}
public String getPrefixName() {
return prefixName;
}
public void setPrefixName(String prefixName) {
this.prefixName = prefixName;
}
public String getSuffixName() {
return suffixName;
}
public void setSuffixName(String suffixName) {
this.suffixName = suffixName;
}
}
注入到Spring容器中
当目前为止、我们读取了数据信息、在自定义的service中实现了我们需要的业务逻辑。接下来就需要把这个service注入到 IOC 容器中、是需要使用到地方自动注入即可,因此我们需要一个配置类将其配置到IOC容器中。
@org.springframework.context.annotation.Configuration
@ConditionalOnClass(JsonService.class)
@EnableConfigurationProperties(SuperJsonProperties.class)
public class Configuration {
private SuperJsonProperties jsonProperties;
public Configuration(SuperJsonProperties jsonProperties) {
this.jsonProperties = jsonProperties;
}
@Bean
@ConditionalOnMissingBean(JsonService.class)
@ConditionalOnProperty(name = "super.json.enable",matchIfMissing = true)
public JsonService jsonService() {
JsonService jsonService = new JsonService();
// 使用从配置文件中的读取到的配置。
jsonService.setPrefixName(jsonProperties.getPrefix());
jsonService.setSuffixName(jsonProperties.getSuffix());
return jsonService;
}
}
代码说明:
@EnableConfigurationProperties:将@ConfigurationProperties注解的类注册为 Spring Bean,并使其能够从外部配置中读取属性值。@Configuration、@ConfigurationOnClass:前者表示这个配置类、需要在满足后一个注解的条件下才起作用、即需要满足在类路径下包含JsonService.class才装配当前类。@ConditionalOnMissingBean(JsonService.class)、@ConditionalOnProperty(name = "super.json.enable",matchIfMissing = true)和@ConditionalOnClass类似、只有满足了条件才会装配这个Bean
常用的条件化注解配置如下:
| 注解 | 说明 | 配置属性 |
|---|---|---|
@ConditionalOnProperty | 根据配置文件中的属性值来决定是否加载配置。 | name: 指定要检查的属性名称。 havingValue: 指定属性值,只有属性值匹配此值时,条件才会成立。 matchIfMissing: 指定如果属性缺失时是否匹配 |
@ConditionalOnClass | 当类路径中存在指定的类时,加载配置 | value: 指定类路径中的类或类的数组。 |
@ConditionalOnMissingBean | 当容器中没有指定类型的 Bean 时,加载配置 | value: 指定 Bean 的类型或类型的数组 |
@ConditionalOnBean | 容器中存在指定类型的 Bean 时,加载配置 | value: 指定 Bean 的类型或类型的数组。 |
@ConditionalOnMissingClass | 当类路径中不存在指定的类时,加载配置 | value: 指定类路径中的类或类的数组 |
到这里基本功能就已经完成了。
装配到Application中
在我们使用 spring boot 整合redis这些starter的过程中。引入相应的starter依赖就可以注入RedisTemplate<String, Object> redisTemplate;供我们使用。接下来我们就要完成这部分工作。
使用@Import方式
首先什么不再进行编码上的工作、通过 idea 的maven install 将其安装到本地的 maven 仓库。
接着在需要使用的服务中引入此模块的坐标:
<dependency>
<groupId>com.supersist</groupId>
<artifactId>super-json-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
此时我们还不能正常使用、无法注入 jsonService、使用使用@Import注解、使用方式如下:
@SpringBootApplication
@Import({com.supersist.config.Configuration.class})
public class SpringRedisApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringRedisApplication.class);
}
@Autowired
JsonService jsonService;
@Override
public void run(String... args) throws Exception {
System.out.println(jsonService.objectToJson(" hello world "));
}
@Data
static class Test{
private String name;
private Integer age;
}
}
使用 @Import 引入之后就可以自动注入JsonService,运行结果如下:
可以看到可以正常使用了,在 application.yml 添加相关配置
super:
json:
prefix: "@@json"
suffix: "@@"
运行结果如下:可以看到功能上使用没什么问题。
但是如果每一个stater都需要@Import这种方式引入的话、就不太好、因为我们本质上需要懒。
spring.fatories
为了使我们引入即可用、则需要在resource目录下创建META-INF/spring.factories文件、添加如下的配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.supersist.config.Configuration
此时重新打包安装之后、重新加载依赖、即可直接使用、注释掉 @Import 相关代码得到如下运行结果
可以看到运行效果前后一致、完美!
优化@Import使用方式
为了避免手动导入、可以自定义一个注解、通过注解来控制是否加载此json模块。自定义一个注解、代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(com.supersist.config.Configuration.class)
public @interface EnableSuperJson {
}
此时运行代码。也可以得到对应的运行结果。这里就不放图片了。最后向各位看官奉上思维导航图