热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【SpringBoot】自定义springbootstarter

1、SpringBootStarter简介(1).什么是SpringBootStarter(启动器)SpringBoot通过将我们常用的功能抽取出来,做成的一系列启动器,这些启动器




1、Spring Boot Starter简介


(1).什么是Spring Boot Starter(启动器)

Spring Boot 通过将我们常用的功能抽取出来,做成的一系列启动器,这些启动器帮我们导入了实现各个功能所需要依赖的全部组件,我们只需要在项目中引入这些 starters,需要的依赖就会全部被导入进来,并且我们通过【约定大于配置】的方式,抛弃繁杂的配置,仅需要通过配置文件来进行少量的配置就可以使用相应的功能。

starter 的实现:虽然不同的 starter 实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties 和 AutoConfiguration。因为 Spring Boot 坚信 “约定大于配置” 这一理念,所以我们使用 ConfigurationProperties 来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter 的 ConfigurationProperties 还使得所有的配置属性被聚集到一个文件中(一般在 resources 目录下的 application.properties),这样我们就告别了 Spring 项目中众多的 XML 配置。


(2).Starter 模块整体结构

starter的整体实现逻辑主要由两个基本部分组成:

xxxAutoConfiguration:自动配置类,对某个场景下需要使用到的一些组件进行自动注入,并利用xxxProperties类来进行组件相关配置

xxxProperties:某个场景下所有可配置属性的集成,在配置文件中配置可以进行属性值的覆盖,按照SpringBoot官方的定义,Starer的作用就是依赖聚合,因此直接在starter内部去进行代码实现是不符合规定的,starter应该只起到依赖导入的作用,而具体的代码实现应该交给其他模块来实现,然后在starter中去引用该模块即可,因此整体的starter的构成应该如下图所示:
请添加图片描述
可见starter模块依赖了两部分,一部分是一些常用依赖,另一部分就是对自动配置模块的依赖,而xxxAutoConfiguration与xxxProperties的具体实现,都封装在自动配置模块中,starter实际是通过该模块来对外提供相应的功能。


(3).Spring 官方提供的 Starter 和 Starter 命名规范


序号名称功能
1spring-boot-starter-web支持 Web 开发,包括 Tomcat 和 spring-webmvc
2spring-boot-starter-redis支持 Redis 键值存储数据库,包括 spring-redis
3spring-boot-starter-test支持常规的测试依赖,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模块
4spring-boot-starter-aop支持面向切面的编程即 AOP,包括 spring-aop 和 AspectJ
5spring-boot-starter-data-elasticsearch支持 ElasticSearch 搜索和分析引擎,包括 spring-data-elasticsearch
6spring-boot-starter-jdbc支持JDBC数据库
7spring-boot-starter-data-jpa支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate

可以看到这些 Starter 的名称都是以 spring-boot-starter 为开头,后面跟着具体的模块名,所有官方的 Starter 遵循相似的命名模式。

根据约定,Spring Boot官方的starter命名要定义为spring-boot-starter-*,自定义或者说第三方的要命名为thirdpartyproject-spring-boot-starter

官方命名空间:


  • 前缀:spring-boot-starter-
  • 模式:spring-boot-starter-模块名
  • 举例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc

自定义命名空间:


  • 后缀:-spring-boot-starter
  • 模式:模块-spring-boot-starter
  • 举例:mybatis-spring-boot-starter

2.自定义Spring Boot Starter

如果你想要自己创建一个 starter,那么基本上包含以下几步


(1).根据 starter 命名规范创建一个 spring boot 项目

创建一个名为config-spring-boot-starter的 spring boot 项目


(2).在 pom.xml 文件中引入依赖



org.springframework.boot
spring-boot-autoconfigure


org.springframework.boot
spring-boot-configuration-processor
true


org.projectlombok
lombok
true


(3).编写ConfigInfo.java、ConfigService.java、 ConfigServiceImpl.java

ConfigInfo 配置信息类,用于封装配置信息。

@Data
@Builder
public class ConfigInfo implements Serializable {
private static final long serialVersiOnUID= -2878523532668902073L;
private String id;
private String ip;
}

ConfigService 示例业务接口,这里我们定义了一个configInfo接口,用于获取配置信息。

public interface ConfigService {
ConfigInfo configInfo();
}

ConfigServiceImpl 业务逻辑实现类,用于实现功能。


public class ConfigServiceImpl implements ConfigService {
private String id;
private String ip;
public ConfigServiceImpl(String id, String ip) {
this.id = id;
this.ip = ip;
}
@Override
public ConfigInfo configInfo() {
return ConfigInfo.builder().id(this.id).ip(this.ip).build();
}
}

(4).编写ConfigProperties.java

创建一个配置文件读取类 ConfigurationProperties 用于保存配置信息(如果你的项目不使用配置信息则可以跳过这一步,不过这种情况非常少见)

@ConfigurationProperties注解使开发人员可以轻松地将整个文件.properties和yml文件映射到一个对象中。编写Properties,应使用唯一的名称空间。不要使用Spring Boot的名称空间(如server,management,spring等)。所以应在所有配置键前面加上自己的名称空间。如我们这里使用的是com.jourwon.config作为配置名称空间。

@Data
@ConfigurationProperties(value = "com.jourwon.config")
public class ConfigProperties {
private String id;
private String ip;
}

(5).编写ConfigAutoConfiguration.java

创建一个 AutoConfiguration,编写带有@Configuration的配置类,并添加@EnableConfigurationProperties注解,@EnableConfigurationProperties作用是为了使@ConfigurationProperties注解的类生效。

@Slf4j
@Configuration
@EnableConfigurationProperties(value = ConfigProperties.class)
public class ConfigAutoConfiguration {
@Resource
private ConfigProperties properties;
@Bean
@ConditionalOnMissingBean
public ConfigService configService() {
log.info("Config ConfigService Start...");
ConfigService service = new ConfigServiceImpl(properties.getId(), properties.getIp());
log.info("Config ConfigService End.");
return service;
}
}

(6).编写spring.factories

在resources/META-INF/下创建spring.factories文件,并且把上一步创建的AutoConfiguration类加入 spring.factories 配置文件中

org.springframework.boot.autoconfigure.EnableAutoCOnfiguration=\
com.jourwon.spring.boot.config.ConfigAutoConfiguration

(7).发布自定义 starter

在自定义 starter 项目根目录执行 mvn install 进行打包安装


(8).测试自定义 starter


1).添加config-spring-boot-starter依赖


1.0.0



com.jourwon.spring.boot
config-spring-boot-starter
${config-spring-boot-starter.version}



org.springframework.boot
spring-boot-starter-web



com.alibaba
fastjson



2).配置application.yml

在使用官方starter的时候,我们可以发现IDE可以进行提示
请添加图片描述
在application.yml配置文件添加如下配置

# 自定义starter相关配置
com:
jourwon:
config:
id: 23145fdb-7427-42d1-ae29-a67f5be30d02
ip: 127.0.0.1

3).编写 ConfigInfoController.java

@RestController
public class ConfigInfoController {
@Resource
private ConfigService configService;
@GetMapping("/configInfo")
public String configInfo() {
return JSON.toJSONString(configService.configInfo());
}
}

(4).启动测试

打开浏览器,输入 http://127.0.0.1:8080/configInfo ,你将会看到我们配置的内容。


3.Spring Boot Starter原理

在导入的starter之后,SpringBoot主要帮我们完成了两件事情,这两件事情统一称为SpringBoot的自动配置:


  • 相关组件的自动导入
  • 相关组件的自动配置

SpringBoot 是如何知道要实例化哪些类,并进行自动配置的呢


  • 首先,SpringBoot 在启动时会去依赖的starter包中寻找 resources/META-INF/spring.factories文件,然后根据文件中配置的Jar包去扫描项目所依赖的Jar包,这类似于 Java 的 SPI 机制。
  • 第二步,根据 spring.factories配置加载AutoConfigure类。
  • 最后,根据 @Conditional注解的条件,进行自动配置并将Bean注入Spring Context 上下文当中。

我们也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自动配置哪些类。


(1).自动配置原理


1).自动配置类的获取与注入

我们从主程序入口来探索一下整个过程的原理:

//标注这个类是一个springboot的应用
@SpringBootApplication
public class CommunityApplication {
public static void main(String[] args) {
//将springboot应用启动
SpringApplication.run(CommunityApplication.class, args);
}
}

@SpringBootApplication注解内部结构如下图所示:
请添加图片描述
AutoConfigurationImportSelector :重点看该类中重写的selectImports方法,看下它返回的字符串数组是如何得来的:
请添加图片描述
我们可以去到上边提到的spring.factories文件中去看一下,找到spring官方提供的spring-boot-autoconfigure包,在其下去找一下该文件:
请添加图片描述
可以看到这个就是SpringBoot官方为我们提供的所有自动配置类的候选列表。我们可以在其中找到一个我们比较熟悉的自动配置类去看一下它内部的实现:
请添加图片描述
可以看到这些一个个的都是JavaConfig配置类,而且都通过@Bean注解向容器中注入了一些Bean
请添加图片描述
结论:


  • SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的所有自动配置类的全限定类名
  • 将这些自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作;
  • 整个J2EE的整体解决方案和自动配置都在spring-boot-autoconfigure的jar包中;
  • 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件,并配置好这些组件 ;
  • 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;

2).自动配置的过程

自动配置类被注入到容器当中后,会帮我们进行组件的自动配置和自动注入的工作,我们以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释这个过程:

首先我们先看下SpringBoot中配置文件与POJO类之间映射的方法,这是进行自动配置的基础。

配置集中化管理:SpringBoot中所有可配置项都集中在一个文件中(application.yml),这个文件中的配置通过@ConfigurationProperties注解来与我们程序内部定义的POJO类来产生关联,这些POJO类统一命名为xxxProperties,并且这些xxxProperties类中各个属性字段都有自己的默认值,这也是SpringBoot约定大于配置理念的体现,尽可能减少用户做选择的次数,但同时又不失灵活性。只要我们想,配置文件中的配置随时可以覆盖默认值。

请添加图片描述
之后,通过配合@EnableConfigurationProperties注解,就可以自动将与配置文件绑定好的这个类注入到容器中供我们使用。

自动配置类的工作流程:


  • 根据限定的条件向容器中注入组件
  • 使用xxxProperties对注入的组件的相关属性进行配置

//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration
//将与配置文件绑定好的某个类注入到容器中,使其生效
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties(HttpProperties.class)
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断系统中有没有CharacterEncodingFilter这个类,如果有配置类才生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
//matchIfMissing = true表明即使我们配置文件中不配置spring.http.encoding.enabled=true,该配置类也是默认生效的;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//该类已经与配置文件绑定了
private final HttpProperties.Encoding properties;
//构建该自动配置类时将与配置文件绑定的配置类作为入参传递进去
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
//注入bean时使用配置类中属性的值进行初始化,相当于将配置文件中的值映射到了组件的某些属性上
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
//注入配置好的bean
return filter;
}
}

一句话总结下自动配置类的工作过程 :


  • 首先容器会根据当前不同的条件判断,决定这个配置类是否生效!
  • 一但这个配置类生效;这个配置类就会给容器中添加相应组件;
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着,配置文件可以配置什么内容,可以参照该前缀对应的属性类中的属性字段


//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
// .....
}

(2).Spring Boot自动配置使用总结

SpringBoot启动会加载大量的自动配置类

我们首先可以看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;

我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)

给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;

xxxxAutoConfigurartion:自动配置类;给容器中添加组件

xxxxProperties:封装配置文件中相关属性;

了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才生效;


序号@Conditional扩展注解作用(判断是否满足当前条件)
1@ConditionalOnWebApplication当前项目是Web项目的条件下
2@ConditionalOnNotWebApplication当前项目不是Web项目的条件下
3@ConditionalOnResource类路径下是否有指定的资源
4@ConditionalOnExpression基于SpEL表达式作为判断条件
5@ConditionalOnMissingBean当容器中没有指定Bean的情况下
6@ConditionalOnBean当容器中有指定的Bean的条件下
7@ConditionalOnSingleCandidate当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean
8@ConditionalOnJava基于JVM版本作为判断条件
9@ConditionalOnMissingClass当类路径下没有指定的类的条件下
10@ConditionalOnClass当类路径下有指定的类的条件下
11@ConditionalOnProperty指定的属性是否有指定的值

那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

我们怎么知道哪些自动配置类生效?
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#在配置文件中开启springboot的调试类
debug=true

Positive matches:(自动配置类启用的:正匹配)


Positive matches:
-----------------
AopAutoConfiguration matched:
- @ConditionalOnClass found required classes 'org.springframework.context.annotation.EnableAspectJAutoProxy', 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice', 'org.aspectj.weaver.AnnotatedElement' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.CglibAutoProxyConfiguration matched:
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
AuditAutoConfiguration#auditListener matched:
- @ConditionalOnMissingBean (types: org.springframework.boot.actuate.audit.listener.AbstractAuditListener; SearchStrategy: all) did not find any beans (OnBeanCondition)
AuditAutoConfiguration#authenticationAuditListener matched:
- @ConditionalOnClass found required class 'org.springframework.security.authentication.event.AbstractAuthenticationEvent' (OnClassCondition)
- @ConditionalOnMissingBean (types: org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener; SearchStrategy: all) did not find any beans (OnBeanCondition)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)


Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
AopAutoConfiguration.JdkDynamicAutoProxyConfiguration:
Did not match:
- @ConditionalOnProperty (spring.aop.proxy-target-class=false) did not find property 'proxy-target-class' (OnPropertyCondition)
AppOpticsMetricsExportAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'io.micrometer.appoptics.AppOpticsMeterRegistry' (OnClassCondition)
ArtemisAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)

Exclusions、Unconditional classes(排除的、没有限定条件的自动配置类):

Exclusions:
-----------
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Unconditional classes:
----------------------
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration


推荐阅读
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有