经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
详解SpringBoot如何自定义Starter
来源:jb51  时间:2021/12/31 17:27:21  对本文有异议

阅读收获

学会自定义Spring-Boot-Starter

理解SpringBoot自动配置原理

本章源码下载

开发starter

测试starter

什么是Starter

Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件)进行自动配置。

使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,Spring Boot就能自动扫描并加载相应的模块并设置默认值,做到开箱即用

为什么使用Starter

在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。

如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,并在starter中设置好默认值,复用的时候只需要将其在pom中引用依赖即可,Spring Boot为我们完成自动装配,做到开箱即用。

Springboot自动配置

SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,Spring Boot就能自动扫描各个jar包下classpath路径的spring.factories文件,加载自动配置类信息,加载相应的bean信息并启动相应的默认配置。

Spring Boot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

大家可以看看我之前写的一篇文章,详细介绍了springboot自动配置的流程:一文搞懂SpringBoot自动配置原理

spring.factories

Spring Boot会默认扫描跟启动类平级的包,如果我们的Starter跟启动类不在同一个主包下,需要通过配置spring.factories文件来配置生效,SpringBoot默认加载各个jar包下classpath路径的spring.factories文件,配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration

Starter开发常用注解

注解使用已经大大方便我们开发,再也不需要写xml配置文件了,SpringBoot经过查找spring.factories文件,加载自动配置类,而自动配置类中定义了各种运行时判断条件,如@ConditionalOnMissingBean(A.class)等,只要ioc容器中没有指定的A类型的bean信息,该配置文件才会生效。

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

1.属性映射注解

  • @ConfigurationProperties :配置文件属性值和实体类的映射
  • @EnableConfigurationProperties:和@ConfigurationProperties配合使用,把@ConfigurationProperties修饰的类加入ioc容器。

2.配置bean注解

  • @Configuration :标识该类为配置类,并把该类注入ioc容器
  • @Bean :一般在方法上使用,声明一个bean,bean名称默认是方法名称,类型为返回值。

3.条件注解

@Conditional:是根据条件类创建特定的Bean,条件类需要实现Condition接口,并重写matches接口来构造判断条件。

@ConditionalOnBean :容器中存在指定bean,才会实例化一个Bean

@ConditionalOnMissingBean:容器中不存在指定bean,才会实例化一个Bean

@ConditionalOnClass:系统中有指定类,才会实例化一个Bean

@ConditionalOnMissingClass:系统中没有指定类,才会实例化一个Bean

@ConditionalOnExpression:当SpEl表达式为true的时候,才会实例化一个Bean

@AutoConfigureAfter :在某个bean完成自动配置后实例化这个bean

@AutoConfigureBefore :在某个bean完成自动配置前实例化这个bean

@ConditionalOnJava :系统中版本是否符合要求

@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化

@ConditionalOnResource:类路径下是否存在指定资源文件

@ConditionalOnWebApplication:是web应用

@ConditionalOnNotWebApplication:不是web应用

@ConditionalOnJndi:JNDI指定存在项

@ConditionalOnProperty: 配置Configuration的加载规则

  • prefix :配置属性名称的前缀
  • value :数组,获取对应property名称的值,与name不可同时使用
  • name :数组,可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
  • havingValue  :比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
  • matchIfMissing :缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效

Full全模式和Lite轻量级模式

@Configuration参数proxyBeanMethods:

Full 全模式(默认):@Configuration(proxyBeanMethods = true)

同一配置类下,当直接调用@Bean修饰的方法注入的对象,则调用该方法会被代理,从ioc容器中取bean实列,所以实列是一样的。即单实例对象,在该模式下SpringBoot每次启动都会判断检查容器中是否存在该组件

Lite 轻量级模式:@Configuration(proxyBeanMethods = false)

同一配置类下,当直接调用@Bean修饰的方法注入的对象,则调用该方法不会被代理,相当于直接调用一个普通方法,会有构造方法,但是没有bean的生命周期,返回的是不同的实例。

注:proxyBeanMethods 是为了让使用@Bean注解的方法被代理。而不是@Bean的单例多例的设置参数。

测试例子这里不展示,可以下载我的代码查看

  1. @Configuration(proxyBeanMethods = false)
  2. public class AppConfig {
  3. //放一份myBean到ioc容器
  4. @Bean
  5. public Mybean myBean() {
  6. return new Mybean();
  7. }
  8. //放一份yourBean到ioc容器
  9. @Bean
  10. public YourBean yourBean() {
  11. System.out.println("==========");
  12. //注意:@Configuration(proxyBeanMethods = false):myBean()方法不代理,直接调用
  13. //注意:@Configuration(proxyBeanMethods = true):myBean()方法代理,从ioc容器拿
  14. return new YourBean(myBean());
  15. }
  16. }

什么时候用Full全模式,什么时候用Lite轻量级模式?

  • 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间有依赖关系时,建议使用Full全模式
  • 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间没有依赖关系时,建议使用Lite轻量级模式,以提高springboot的启动速度和性能

Starter命名规范

Spring官方Starter通常命名为spring-boot-starter-{name}如:spring-boot-starter-web

Spring官方建议非官方Starter命名应遵循{name}-spring-boot-starter的格式:如mybatis-spring-boot-starter。

开发Starter

1. 创建Starter项目

新建项目后,要删除main启动类

2. 添加依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.6.1</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11.  
  12. <modelVersion>4.0.0</modelVersion>
  13. <groupId>com.ljw</groupId>
  14. <artifactId>ljw-spring-boot-starter</artifactId>
  15. <version>1.0</version>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. <maven.compiler.source>8</maven.compiler.source>
  19. <maven.compiler.target>8</maven.compiler.target>
  20. </properties>
  21.  
  22.  
  23. <dependencies>
  24.  
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter</artifactId>
  28. </dependency>
  29.  
  30. <!-- 包含自动配置的代码-->
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-autoconfigure</artifactId>
  34. </dependency>
  35.  
  36. <!-- 配置文件点击可以跳转实体-->
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-configuration-processor</artifactId>
  40. <optional>true</optional>
  41. </dependency>
  42.  
  43. </dependencies>
  44.  
  45. </project>

我们没有main入口,需要去除pom文件中maven打包插件spring-boot-maven-plugin

spring-boot-configuration-processor作用:

  • spring-boot-configuration-processor其实是一个注解处理器,在编译阶段干活的,一般在maven的声明都是optional 为true
  • 你在idea里面可以点击port,进到这个字段里面,还可以看到配置的提示信息
  • 这是因为在你的资源文件里面有一个spring-configuration-metadata.json文件,这是spring配置的元数据,是json形式

3. 编写属性类

@ConfigurationProperties可以定义一个配置信息类,和配置文件进行映射

  1. @ConfigurationProperties(prefix = "ljw.config")
  2. public class HelloProperties {
  3.  
  4. private String name = "hello 默认值!";
  5.  
  6. private int age = 8;
  7.  
  8. public int getAge() {
  9. return age;
  10. }
  11.  
  12. public void setAge(int age) {
  13. this.age = age;
  14. }
  15.  
  16. public String getName() {
  17. return name;
  18. }
  19.  
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. }

4. 自定义业务类

这里可以模拟一些获取了配置文件信息的进行业务操作的业务类

  1. public class HelloService {
  2.  
  3. private String name;
  4.  
  5. private int age;
  6.  
  7. public String getName() {
  8. return name;
  9. }
  10.  
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14.  
  15. public int getAge() {
  16. return age;
  17. }
  18.  
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22.  
  23. public String hello() {
  24. return "HelloService{" +
  25. "name='" + name + ''' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. }

5. 编写自动配置类

命名规范:XxxAutoConfiguration

  1. @Configuration(proxyBeanMethods = false)
  2. // 当存在某个类时,此自动配置类才会生效
  3. @ConditionalOnClass(value = {HelloService.class})
  4. // 导入我们自定义的配置类,供当前类使用
  5. @EnableConfigurationProperties(value = HelloProperties.class)
  6. // 只有非web应用程序时此自动配置类才会生效
  7. @ConditionalOnWebApplication
  8. //判断ljw.config.flag的值是否为“true”, matchIfMissing = true:没有该配置属性时也会正常加载
  9. @ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
  10. public class HelloAutoConfiguration {
  11.  
  12. /**
  13. * @param helloProperties 直接方法签名入参注入HelloProperties,也可以使用属性注入
  14. * @return
  15. */
  16. @Bean
  17. @ConditionalOnMissingBean(HelloService.class)
  18. //@ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
  19. public HelloService helloService(HelloProperties helloProperties) {
  20. HelloService helloService = new HelloService();
  21. //把获取的信息注入
  22. helloService.setName(helloProperties.getName());
  23. helloService.setAge(helloProperties.getAge());
  24. return helloService;
  25. }
  26.  
  27. }

注:这里配置一个web应用才能注入,并且ljw.config.flag的值是否为“true”或者不配置该key才能注入HelloService服务

6. 编写spring.factories

把自动配置类HelloAutoConfiguration配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration的key下,springboot会自动加载该文件并根据条件装配

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ljw.starter.config.HelloAutoConfiguration

7. 编写配置提示文件(非必须)

additional-spring-configuration-metadata.json

配置additional-spring-configuration-metadata.json文件后,在开发人员的IDE工具使用个人编写的配置读取很有效的在application.properties或application.yml文件下完成提示。

配置详细格式参数可查看文档

我的配置:

  1. {"properties": [
  2. {
  3. "name": "ljw.config.name",
  4. "type": "java.lang.String",
  5. "defaultValue": "hello 默认值!这里配置的是提示,真正默认值在Properties里面",
  6. "description": "这是字符串名称啊."
  7. },
  8. {
  9. "name": "ljw.config.age",
  10. "defaultValue": 8,
  11. "description": "这是int类型的年龄啊.",
  12. "deprecation": {
  13. "reason": "过时原因.",
  14. "replacement": "替代key是:ljw.config.age22",
  15. "level": "warning"
  16. }
  17. }
  18. ]}

大家参考下面properties表格进行配置上的理解。

deprecation每个properties元素的属性中包含的JSON对象可以包含以下属性:

spring-configuration-metadata.json

spring-configuration-metadata.json代码量挺大的,为了方便我们可以通过IDE来生成,这里使用的是idea。

在idea设置中搜索Annotation Processors,接下来勾住Enable annonation processing就完成了。在编译打包后的文件中看到自动生成的spring-configuration-metadata.json。这个文件不用我们编写

下面是自动生成的:

  1. {
  2. "groups": [
  3. {
  4. "name": "ljw.config",
  5. "type": "com.ljw.starter.properties.HelloProperties",
  6. "sourceType": "com.ljw.starter.properties.HelloProperties"
  7. }
  8. ],
  9. "properties": [
  10. {
  11. "name": "ljw.config.name",
  12. "type": "java.lang.String",
  13. "description": "这是字符串名称啊.",
  14. "sourceType": "com.ljw.starter.properties.HelloProperties",
  15. "defaultValue": "hello 默认值!这里配置的是提示,真正默认值在Properties里面"
  16. },
  17. {
  18. "name": "ljw.config.age",
  19. "type": "java.lang.Integer",
  20. "description": "这是int类型的年龄啊.",
  21. "sourceType": "com.ljw.starter.properties.HelloProperties",
  22. "defaultValue": 8,
  23. "deprecated": true,
  24. "deprecation": {
  25. "level": "warning",
  26. "reason": "过时原因.",
  27. "replacement": "替代key是:ljw.config.age22"
  28. }
  29. }
  30. ],
  31. "hints": []
  32. }

测试Starter

1. 前置环境

install打包自定义starter项目:ljw-spring-boot-starter

新建项目:ljw-test-spring-boot-starter

2. 添加依赖

引入打好包的自定义starter

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter</artifactId>
  5. </dependency>
  6. <!-- 测试web应用-->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-web</artifactId>
  10. </dependency>
  11. <!--自定义satrter-->
  12. <dependency>
  13. <groupId>com.ljw</groupId>
  14. <artifactId>ljw-spring-boot-starter</artifactId>
  15. <version>1.0</version>
  16. </dependency>
  17. </dependencies>

3. 测试类

  1. @Service
  2. public class TestController implements CommandLineRunner {
  3.  
  4. /**
  5. * 注入自定义starter服务
  6. */
  7. @Resource
  8. private HelloService helloService;
  9.  
  10. @Override
  11. public void run(String... args) throws Exception {
  12. System.out.println(helloService.hello());
  13. }
  14. }

4. 修改配置文件

输入前缀可以看出已经有提示了

  1. ljw.config.name=ljw hello!
  2. ljw.config.age=99
  3. ljw.config.flag=true
  4. #不会注入
  5. #ljw.config.flag=true1
  6. # 可以看到哪些自动配置了
  7. debug=true

5. 运行程序打印

  1. HelloService{name='ljw hello!', age=99}

条件注入

  • 如果没有spring-boot-starter-web依赖,不能注入服务HelloService
  • 如果配置了ljw.config.flag,值不是true,不能注入服务HelloService;如果不配置ljw.config.flag,可以注入

6. 查看自动配置类生效的方法

通过启用 debug=true 属性,让控制台打印自动配置报告,这样就可以很方便地知道哪些自动配置类生效。

  1. HelloAutoConfiguration matched:
  2. - @ConditionalOnClass found required class 'com.ljw.starter.service.HelloService' (OnClassCondition)
  3. - @ConditionalOnWebApplication (required) found 'session' scope (OnWebApplicationCondition)
  4. - @ConditionalOnProperty (ljw.config.flag=true) matched (OnPropertyCondition)
  5.  
  6. HelloAutoConfiguration#helloService matched:
  7. - @ConditionalOnMissingBean (types: com.ljw.starter.service.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)

以上就是详解SpringBoot如何自定义Starter的详细内容,更多关于SpringBoot自定义Starter的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号