如何在SpringBoot自定义一个starter
Posted 诺浅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在SpringBoot自定义一个starter相关的知识,希望对你有一定的参考价值。
1、什么是SpringBoot
的Starter
还记得我们在SpringBoot
项目里面是如何使用Redis
的吗?我们需要在pom中引入以下内容
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
他的名称是spring-boot-starter-data-redis
,同理如果你要引入mq
,你需要在这里填的是spring-boot-starter-data-xxmq
他们的名称一般是以spring-boot-starter
开头,这其实就是一个starter
starter
就是一个外部的项目,我们需要使用它的时候就可以在当前springboot项目中引入它。
在我们的日常开发工作中,经常会有一些独立于业务之外的模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,这样做虽然可以解决问题,但始终不够优雅。
如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter
,复用的时候只需要将其在pom.xml
中引用依赖即可,SpringBoot
为我们完成自动装配,简直不要太爽。
2、如何自定义一个Starter
SpringBoot
提供的starter以spring-boot-starter-xxx
的方式命名的。官方建议自定义的starter使用xxx-spring-boot-starter
命名规则。以区分SpringBoot
生态提供的starter。
2.1、新建一个工程im
里面有一个类NettyServer
,这个类的功能是启动一个Netty
服务(做什么事本身并不重要,本文讲的是如何与springboot
集成)
public class NettyServer
public void run()
System.out.println("启动netty服务");
2.2、新建一个spingboot
工程service
并引入im
项目的依赖
我们想在这个service工程
中引用im工程
的NettyServer
,然后启动一个Netty服务。那么应该怎么做呢?
先在pom中引入im的工程
<dependency>
<groupId>com.bxoon</groupId>
<artifactId>im-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2.3、改造im工程
首先我们需要在resources下面新建META-INF文件夹,然后创建spring.factories文件
文件内容如下(NettyServer的全路径名)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.bxoon.service.NettyServer
上面这句话的意思就是SpringBoot
启动的时候会去加载我们的NettyServer
到IOC容器中。这其实是一种变形的SPI机制,什么是SPI呢?可以我的这篇文章
理论上这个时候我们启动SpringBoot
,Sping就会把NettyServer
类加载到Bean容器中。
2.4、如何使得Stater中的代码在项目初始化时被执行?
我们使得NettyServer
实现ServletContextInitializer
接口,然后实现这个接口的onStartup
方法中调用run
方法,最终NettyServer的代码如下所示
public class NettyServer implements ServletContextInitializer
public void run()
System.out.println("启动netty服务");
@Override
public void onStartup(ServletContext servletContext) throws ServletException
run();
如此我们就可以在项目启动的时候NettyServer
的run
方法被执行。
到此为止,启动service
工程,就会看到控制台输出下面这句话
启动netty服务
说明启动netty服务成功了。
注意:实现ServletContextInitializer
的方法只会在使用java -jar
这种方式启动时才会执行(idea里面也会执行),如果你的项目是打包成war包并放入tomcat等符合servlet3.1规范的容器中运行,则需要实现WebApplicationInitializer
方法。所以我觉得吧,两个都实现吧(不知道会不会有问题,比如会加载两次?讲道理应该是不会)
public class NettyServer implements ServletContextInitializer,WebApplicationInitializer
3、热插拔技术的实现
如果有一天我们不想要启动service工程的时候启动netty服务怎么办呢?有人说那简单啊,我们去pom中把im的依赖注释掉,的确,这是一种方案,但Spring提供了更好的解决方案。
还记得我们经常会在启动类Application上面加@EnableXXX
注解吗?
其实这个@Enablexxx
注解就是一种热拔插技术,加了这个注解就可以启动对应的starter
,当不需要对应的starter的时候只需要把这个注解注释掉就行,是不是很优雅呢?那么这是如何实现的呢?
3.1、改造im工程新增热插拔支持类
新增标记类ImConfigMarker
public class ImConfigMarker
public ImConfigMarker()
新增EnableImRegisterServer注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImConfigMarker.class)
public @interface EnableImRegisterServer
改造NettyServer
新增条件注解@ConditionalOnBean(ImConfigMarker.class)
,@ConditionalOnBean
这个是条件注解,前面的意思代表只有当上下文中含有ImConfigMarker
对象,被标注的类才会被实例化。
@ConditionalOnBean(ImConfigMarker.class)
public class NettyServer implements ServletContextInitializer
public void run()
System.out.println("启动netty服务");
@Override
public void onStartup(ServletContext servletContext) throws ServletException
run();
3.2、改造service工程
在启动类上新增@EnableImRegisterServer注解
到此热插拔就实现好了
当加了@EnableImRegisterServer
注解的时候,由于这个注解使用了@Import(ImConfigMarker.class)
,所以会导致Spring去加载ImConfigMarker
到上下文中,而又因为条件注解@ConditionalOnBean(ImConfigMarker.class)
的存在,所以NettyServer
类就会被实例化。
而当没有@EnableImRegisterServer
注解的时候,就不会加载ImConfigMarker
到上下文中,就不满足条件注解@ConditionalOnBean(ImConfigMarker.class)
,进而导致NettyServer
不会被加载。
4、关于Spring中的条件注解的讲解
前面我们在热插拔小结中说的@ConditionalOnBean
其实是Spring中的一个条件注解,除此之外,Spring还提供了很多条件注解。
@ConditionalOnBean
:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。@ConditionalOnClass
:某个class位于类路径上,才会实例化一个Bean。@ConditionalOnExpression
:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式的条件判断。@ConditionalOnMissingBean
:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。@ConditionalOnMissingClass
:某个class类路径上不存在的时候,才会实例化一个Bean。@ConditionalOnNotWebApplication
:不是web应用,才会实例化一个Bean。@ConditionalOnWebApplication
:当项目是一个Web项目时进行实例化。@ConditionalOnNotWebApplication
:当项目不是一个Web项目时进行实例化。@ConditionalOnProperty
:当指定的属性有指定的值时进行实例化。@ConditionalOnJava
:当JVM版本为指定的版本范围时触发实例化。@ConditionalOnResource
:当类路径下有指定的资源时触发实例化。@ConditionalOnJndi
:在JNDI存在的条件下触发实例化。@ConditionalOnSingleCandidate
:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。
以上是关于如何在SpringBoot自定义一个starter的主要内容,如果未能解决你的问题,请参考以下文章
自定义SpringBoot Starter 实现请求日志打印
自定义SpringBoot Starter 通过注解启动装配
springboot 源码解析## 如何自定义 starter 让 springboot 扫描到你的配置
如何禁用 TomcatServletWebServerFactory 的 SpringBoot 自动配置以便自定义 spring-starter 提供它?