一、spring-boot-starter简介
使用spring-boot时,只要引入官方提供的starter,就可以获取到我们想要用的Bean,做到真正的开箱即用。当然,我们也可以自己写一个starter,比如封装一些第三方服务的连接代码,类似于jedis,或者封装一些其他的通用代码。下面我们写个自定义starter,感受一下。
二、自定义starter示例
1.新建springboot工程
①File -> New -> Project...
②点击next
③点击create,创建工程
④这样一个新的工程就创建好了,由于我们需要的是一个starter工程,因此去掉test目录和启动类
⑤去掉pom中无效的依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-spring-boot-starter</name>
<description>demo-spring-boot-starter</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 注释掉无效依赖 -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
</dependencies>
<!-- 这段注释掉,否则install的时候会报Unable to find main class -->
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- </build>-->
</project>
2.编写starter代码
①编写一个service,用connect()方法模拟连接服务器,不需要@Service注解
package com.example.demospringbootstarter.service;
public class DemoStarterService {
private String ip;
private String port;
public void connect() {
System.out.println(ip + ":" + port + "连接成功!!!");
}
public DemoStarterService(String ip, String port) {
this.ip = ip;
this.port = port;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}
②编写一个配置类,用来注册bean
package com.example.demospringbootstarter.config;
import com.example.demospringbootstarter.service.DemoStarterService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DemoConfiguration {
//绑定配置文件中demo-starter.ip属性,默认为127.0.0.1
@Value("${demo-starter.ip:127.0.0.1}")
private String ip;
//绑定配置文件中demo-starter.port属性,默认为8080
@Value("${demo-starter.port:8080}")
private String port;
@Bean
public DemoStarterService demoStarterService() {
return new DemoStarterService(ip, port);
}
}
③再编写一个runner类,尝试下runner能否自动注入(容器启动完成之后执行)
package com.example.demospringbootstarter.runner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
//不要添加@Component注解
public class DemoRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("DemoRunner执行了!!!");
}
}
④在resources目录下创建META-INF目录,在下面编写spring.factories文件,springboot会扫描包中的spring.factories文件,加载其中的bean,这是starter的关键
spring.factories文件如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demospringbootstarter.config.DemoConfiguration,com.example.demospringbootstarter.runner.DemoRunner
笔者这里将配置类和runner都添加了。
3.构建starter包
使用maven install或者用命令mvn clean install -Dmaven.test.skip=true构建starter包
4.引入starter,测试效果
①添加自定义starter依赖
<dependency>
<groupId>com.example</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
②调用service
在application.yml中设置连接信息
调用connect()方法
③运行项目,观察日志
发现stater中的定义的runnner已经被扫描成Bean并且执行了,再测试下service有没有被扫描到
④调用接口
观察日志
方法执行成功!这样一个简单的自定义starter就创建好了。
三、starter集成原理简单分析
1.查看@SpringBootApplication
2.进入@EnableAutoConfiguration
发现是通过@Import注解,导入AutoConfigurationImportSelector实现的
3.分析AutoConfigurationImportSelector源码
启动项目,在getAutoConfigurationEntry方法打断点,这个方法获取自动配置实体
进入getCandidateConfigurations方法(获取候选配置)
进入loadFactoryNames方法
进入loadSpringFactories方法
这段代码实现了读取META-INF/spring.factories中的配置,从而能够导入bean。