之前的帖子说过,使用依赖尽量使用starter相关的依赖。因为这些相关的依赖会根据配置文件配置相关的类。比如在Spring中整合使用redis。那么在快速开发中,推荐使用spring-boot-starter-data-redis依赖,它会根据配置文件默认配置好redisTemplate等相关类。
那么接下来介绍如何构建starter组件。
首先对于自定义的starter(第三方),官方推荐的命名是xx-spring-boot-starter,只有Spring官方的starter命名是spring-boot-starter-xxx。
首先来看依赖部分。pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.apex</groupId>
<artifactId>myservice-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- 自定义springboot的组件,必须引入此依赖作为入口 -->
<!--这里有我们需要的注解,Spring会根据EnableAutoConfigurationImportSelector注入相关的类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<!-- 读取配置文件时的自动提示功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.0.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
<!--非必须,由于我打包时idea建议我使用UTF-8编码-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
这边编写个YmlProperties类,用来读取yml配置文件。
package com.apex.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author ZJX
* @date 2022/2/20
* @description
*/
//获取配置文件(application.properties或者application.yml)中前缀为myservice的属性
@ConfigurationProperties(prefix = "myservice")
public class YmlProperties {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
接下来编写Service层。
package com.apex.service;
import com.apex.config.YmlProperties;
import org.springframework.stereotype.Service;
/**
* @author ZJX
* @date 2022/2/20
* @description
*/
public class YmlService {
private YmlProperties ymlProperties;
public YmlService(YmlProperties ymlProperties){
this.ymlProperties = ymlProperties;
}
public String getUrl(){
if(ymlProperties!=null){
return ymlProperties.getUrl();
}else{
return null;
}
}
public YmlProperties getYmlProperties() {
return ymlProperties;
}
public void setYmlProperties(YmlProperties ymlProperties) {
this.ymlProperties = ymlProperties;
}
}
接下来编写自动配置的配置类。
package com.apex;
import com.apex.config.YmlProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.apex.service.YmlService;
/**
* @author ZJX
* @date 2022/2/20
* @description
*/
//说明该类是个配置类
@Configuration
//说明自动读取配置文件的类
@EnableConfigurationProperties(YmlProperties.class)
//标注配置文件需要配置特定属性才生效,在此处是需要配置myservice.enabled=true才生效,matchIfMissing表明如果不配置,默认为true即不配置默认生效
@ConditionalOnProperty(prefix = "myservice",name = "enabled",matchIfMissing = true)
public class MyAutoConfiguration {
@Autowired
YmlProperties ymlProperties;
@Bean
public YmlService ymlService (){
return new YmlService(ymlProperties);
}
}
接下来是配置spring.factories文件(在resources下的META-INF下,没有改文件或者目录需要自己创建)。
#配置自动加载的类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.apex.MyAutoConfiguration
上述配置是因为@SpringBootApplication包含了@EnableAutoConfiguration。
而@EnableAutoConfiguration里通过@Import导入了AutoConfigurationImportSelector。
找到其中selectImports方法,可以看到其调用了getAutoConfigurationEntry方法。
而在getAutoConfigurationEntry方法中,是通过getCandidateConfigurations获取配置信息。
可以看到该方法继续调用了loadFactoryNames方法,后面的断言信息也说明了,实际是获取META-INF/factories文件。但是我们可以继续从方法中查找具体的文件。
在loadFactoryNames方法中,如果当前类加载对象存在,在当前类加载包下的指定路径下去加载,如果不存在就在当前系统类加载路径去找。而对于FACTORIES_RESOURCE_LOCATION来说,其指向META-INF/spring,factories文件(即会扫描各个jar包下的META-INF/spring.factories)。
最后在其他模块中引入该组件。
<dependencies>
<dependency>
<groupId>com.apex</groupId>
<artifactId>myservice-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
Controller层
@Controller
public class FirstController {
//自动注入
@Autowired
YmlService ymlService;
@GetMapping("/hello")
@ResponseBody
public String sayHello(){
return ymlService.getUrl();
}
}
该模块的application.yml文件。
myservice:
url: 123
测试
虽然项目成功运行了,但是有个问题,那就是idea会报找不到该bean。那是因为YmlService是配置在其他模块下的,idea能找到该类,但是不知道该类是否被Spring容器控制反转,但实际上是的。
为了消除该爆红,我们可以在自定义的starter模块下的spring.factories下添加该Service类。
这样子就能很明显地告诉“idea”,该YmlService类会被Spring自动扫描并注入到Spring容器中。然后爆红也消失了。
最后给大家看看我的项目结构。