上期讲到,关于SpringBoot自动装配原理,相信小伙伴们已经看明白啦,今天,我们就来聊一聊如何根据自动装配原理,自定义一个starter吧
什么是starter
我们玩了那么久的SpringBoot, 几乎项目依赖中基本上全是各种各样的starter, 那么到底什么是starter?
starter是一组方便的依赖描述符,当我们使用它时,可以获得所有需要的Spring和相关技术的一站式服务,典型的如spring-boot-starter-web
,引入之后,自动引入所有有关spring web项目相关的依赖。
说实话,用久了SpringBoot,阿鉴都已经忘记被每个项目都需要引入spring-core
,spring-context
,spring-web
等等所支配的恐惧了哈哈。
回顾SpringBoot自动装配的内容
在上一节,我们是从探究redis的自动装配过程开始的,那么小伙伴们还记得它的过程吗?
阿鉴带大家回顾一下:
项目启动时,Spring通过@Import注解导入了AutoConfigurationImportSelector
, 然后调用该类selectImports
时,从classpath下的META-INF/spring.factories
文件中读取key为EnableAutoConfiguration
的配置类,然后Spring便会将这些类加载到Spring的容器中,变成一个个的Bean。
动手实践
流程已经梳理完了,现在就开始实践吧,思路其实非常简单
- 写一个配置类
- 将该配置类放到资源文件夹中的
META-INF/spring.factories
中
先来创建一个项目
1. 创建spring-boot-starter-demo项目并编写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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.zijiancode</groupId>
<artifactId>spring-boot-starter-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<modules>
<!-- 自定义starter -->
<module>spring-boot-starter-demo-starter</module>
<!-- 用于测试自定义starter-->
<module>spring-boot-starter-demo-sample</module>
</modules>
</project>
2. 创建子模块spring-boot-starter-demo-starter
<?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>
<parent>
<groupId>cn.zijiancode</groupId>
<artifactId>spring-boot-starter-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-demo-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
3. 创建测试用模块spring-boot-starter-demo-sample
<?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>
<parent>
<groupId>cn.zijiancode</groupId>
<artifactId>spring-boot-starter-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-demo-sample</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 自定义starter-->
<dependency>
<groupId>cn.zijiancode</groupId>
<artifactId>spring-boot-starter-demo-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
项目结构如下:
spring-boot-starter-demo-starter: 我们本次的主角,自定义starter
spring-boot-starter-demo-sample: 测试模块,用于测试starter是否生效
4. 编写starter中的代码
编写一个handler,用于做注入测试
public class DemoHandler {
public DemoHandler(){
System.out.println("demo handler init!!");
}
}
不需要加任何注解,因为等下使用@Bean的方式注入
编写自动配置类
public class DemoAutoConfiguration {
@Bean
public DemoHandler demoHandler(){
return new DemoHandler();
}
}
自动配置类同样也可以不需要加任何注解,因为它本质上是使用@Import导入的
当然,如果不加@Configuration注解的话在特定的场景其实会引发一个小小的问题
这个小问题阿鉴决定卖个关子,放到下期和大家聊一聊,很快的,就这两天(其实是因为说起来还是有些内容的)
在resources
目录下新建META-INF/spring.factories
文件
编写配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.zijiancode.starter.config.DemoAutoConfiguration
5. 在测试模块中编写测试类
@SpringBootApplication
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args).close();
}
}
6. 启动项目查看结果
demo handler成功的被自动配置类注入了
也就是说,我们的starter生效啦~
我知道,到这个时候,肯定有小伙伴说:就这?
阿鉴:确实,就这,哈哈,一个普遍的使用方式到这就结束了~
利用starter做一些额外的操作
在上面的例子中,我们只是通过starter注入了bean,但是其实我们可以利用这样的机制做更多的事情,比如spring-boot-starter-data-redis
就在项目启动时与redis建立了连接,并初始化连接池。还有我们之前学的nacos,在项目启动时,将服务注册到nacos等等。
那么,这样的操作应该怎么完成呢?
其实在例子中,我们的DemoHandler
在构造方法里打印了一句demo hanlder init
,这就是个小小的思路,我们完全可以在初始化bean时做些别的事情,当然,这样的做法并不太好,因为spring中的bean是一个接一个初始化的,如果我们在UserService的构造方法里写调用RoleService的逻辑,很可能会因为RoleService还没初始化而报错。
在Spring中,还有一个东西叫做监听器,我们可以利用它在做一些事情,这也是阿鉴最喜欢的一种方式。
监听器肯定是对应着一系列的事件的,有个事件叫做ContextRefreshedEvent
, 表示Spring的上下文刷新完毕,所有的Bean都已经初始化完成,Spring的启动流程即将结束。
试试
1. 在DemoHandler加个方法
public class DemoHandler {
public DemoHandler(){
System.out.println("demo handler init!!");
}
public void hello(){
System.out.println("hello world for demo starter!");
}
}
2. 编辑监听器
public class DemoListener implements ApplicationListener<ContextRefreshedEvent> {
@Resource
private DemoHandler demoHandler;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
demoHandler.hello();
}
}
3. 将监听器注入到容器中
public class DemoAutoConfiguration {
@Bean
public DemoHandler demoHandler(){
return new DemoHandler();
}
@Bean
public DemoListener demoListener(){
return new DemoListener();
}
}
4. 使用测试模块测试
测试成功,listern已生效
小结
本文基于上一期的SpringBoot自动装配原理介绍了如何自定义starter,并和小伙伴们聊了下怎么利用这个机制做一些额外的事情。
这一期的内容还是比较简单的,希望大家有所收获。
我们下期…完了,上一期还说了要和大家聊一聊如何查看组件的源码并进行扩展,我有罪,请允许我放到下一期吧「磕头.png」,我们下期再见~