<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          還在curd嗎?封裝屬于自己的Spring-Boot-Starter

          共 11171字,需瀏覽 23分鐘

           ·

          2022-01-08 20:48

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

          編輯:業(yè)余草

          juejin.cn/post/7047674475331977224

          推薦:https://www.xttblog.com/?p=5304

          本文閱讀后,你將收獲以下知識:

          • ????學(xué)會自定義Spring-Boot-Starter
          • ????理解SpringBoot自動配置原理

          什么是Starter

          Starter是Spring Boot中的一個非常重要的概念,Starter相當(dāng)于模塊,它能將模塊所需的依賴整合起來并對模塊內(nèi)的Bean根據(jù)環(huán)境( 條件)進(jìn)行自動配置。

          「使用者只需要依賴相應(yīng)功能的Starter,無需做過多的配置和依賴,Spring Boot就能自動掃描并加載相應(yīng)的模塊并設(shè)置默認(rèn)值,做到開箱即用」

          為什么使用Starter

          在我們的日常開發(fā)工作中,經(jīng)常會有一些獨立于業(yè)務(wù)之外的配置模塊,我們經(jīng)常將其放到一個特定的包下,然后如果另一個工程需要復(fù)用這塊功能的時候,需要將代碼硬拷貝到另一個工程,重新集成一遍,麻煩至極。

          如果我們將這些可獨立于業(yè)務(wù)代碼之外的功配置模塊封裝成一個個starter,并在starter中設(shè)置好默認(rèn)值,復(fù)用的時候只需要將其在pom中引用依賴即可,Spring Boot為我們完成自動裝配,做到開箱即用。

          Springboot自動配置

          SpringBoot中的starter是一種非常重要的機制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要在maven中引入starter依賴,Spring Boot就能自動掃描各個jar包下classpath路徑的spring.factories文件,加載自動配置類信息,加載相應(yīng)的bean信息并啟動相應(yīng)的默認(rèn)配置。

          Spring Boot提供了針對日常企業(yè)應(yīng)用研發(fā)各種場景的spring-boot-starter依賴模塊。所有這些依賴模塊都遵循著約定成俗的默認(rèn)配置,并允許我們調(diào)整這些配置,即遵循“約定大于配置”的理念。

          大家可以看看我之前寫的一篇文章,詳細(xì)介紹了springboot自動配置的流程:一文搞懂??SpringBoot自動配置原理

          spring.factories

          Spring Boot會默認(rèn)掃描跟啟動類平級的包,如果我們的Starter跟啟動類不在同一個主包下,需要通過配置spring.factories文件來配置生效,SpringBoot默認(rèn)加載各個jar包下classpath路徑的spring.factories文件,配置的key為org.springframework.boot.autoconfigure.EnableAutoConfiguration

          Starter開發(fā)常用注解

          注解使用已經(jīng)大大方便我們開發(fā),再也不需要寫xml配置文件了,SpringBoot經(jīng)過查找spring.factories文件,加載自動配置類,而自動配置類中定義了各種運行時判斷條件,如@ConditionalOnMissingBean(A.class)等,只要ioc容器中沒有指定的A類型的bean信息,該配置文件才會生效。

          @Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,滿足條件給容器注冊bean。

          • 屬性映射注解
            • @ConfigurationProperties :配置文件屬性值和實體類的映射
            • @EnableConfigurationProperties:和@ConfigurationProperties配合使用,把@ConfigurationProperties修飾的類加入ioc容器。
          • 配置bean注解
            • @Configuration :標(biāo)識該類為配置類,并把該類注入ioc容器
            • @Bean :一般在方法上使用,聲明一個bean,bean名稱默認(rèn)是方法名稱,類型為返回值。
          • 條件注解
            • prefix :配置屬性名稱的前綴
            • value :數(shù)組,獲取對應(yīng)property名稱的值,與name不可同時使用
            • name :數(shù)組,可與prefix組合使用,組成完整的配置屬性名稱,與value不可同時使用
            • havingValue :比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置
            • matchIfMissing :缺少該配置屬性時是否可以加載。如果為true,沒有該配置屬性時也會正常加載;反之則不會生效
            • @Conditional:是根據(jù)條件類創(chuàng)建特定的Bean,條件類需要實現(xiàn)Condition接口,并重寫matches接口來構(gòu)造判斷條件。
            • @ConditionalOnBean :容器中存在指定bean,才會實例化一個Bean
            • @ConditionalOnMissingBean:容器中不存在指定bean,才會實例化一個Bean
            • @ConditionalOnClass:系統(tǒng)中有指定類,才會實例化一個Bean
            • @ConditionalOnMissingClass:系統(tǒng)中沒有指定類,才會實例化一個Bean
            • @ConditionalOnExpression:當(dāng)SpEl表達(dá)式為true的時候,才會實例化一個Bean
            • @AutoConfigureAfter :在某個bean完成自動配置后實例化這個bean
            • @AutoConfigureBefore :在某個bean完成自動配置前實例化這個bean
            • @ConditionalOnJava :系統(tǒng)中版本是否符合要求
            • @ConditionalOnSingleCandidate:當(dāng)指定的Bean在容器中只有一個,或者有多個但是指定了首選的Bean時觸發(fā)實例化
            • @ConditionalOnResource:類路徑下是否存在指定資源文件
            • @ConditionalOnWebApplication:是web應(yīng)用
            • @ConditionalOnNotWebApplication:不是web應(yīng)用
            • @ConditionalOnJndi:JNDI指定存在項
            • @ConditionalOnProperty:配置Configuration的加載規(guī)則

          Full全模式和Lite輕量級模式

          • @Configuration參數(shù)proxyBeanMethods:
            • 同一配置類下,當(dāng)直接調(diào)用@Bean修飾的方法注入的對象,則調(diào)用該方法不會被代理,相當(dāng)于直接調(diào)用一個普通方法,會有構(gòu)造方法,但是沒有bean的生命周期,返回的是不同的實例。
            • 同一配置類下,當(dāng)直接調(diào)用@Bean修飾的方法注入的對象,則調(diào)用該方法會被代理,從ioc容器中取bean實列,所以實列是一樣的。即單實例對象,在該模式下SpringBoot每次啟動都會判斷檢查容器中是否存在該組件
            • Full 全模式(默認(rèn)):@Configuration(proxyBeanMethods = true)
            • Lite 輕量級模式:@Configuration(proxyBeanMethods = false)
          • 注:proxyBeanMethods 是為了讓使用@Bean注解的方法被代理。而不是@Bean的單例多例的設(shè)置參數(shù)。
          • 測試?yán)舆@里不展示,可以下載我的代碼查看
          @Configuration(proxyBeanMethods?=?false)
          public?class?AppConfig?{
          ????
          ????//放一份myBean到ioc容器
          ????@Bean
          ????public?Mybean?myBean()?{
          ????????return?new?Mybean();
          ????}

          ????//放一份yourBean到ioc容器
          ????@Bean
          ????public?YourBean?yourBean()?{
          ????????System.out.println("==========");
          ????????//注意:@Configuration(proxyBeanMethods = false):myBean()方法不代理,直接調(diào)用
          ????????//注意:@Configuration(proxyBeanMethods = true):myBean()方法代理,從ioc容器拿
          ????????return?new?YourBean(myBean());
          ????}
          }

          什么時候用Full全模式,什么時候用Lite輕量級模式?

          • 當(dāng)在你的同一個Configuration配置類中,注入到容器中的bean實例之間有依賴關(guān)系時,建議使用Full全模式
          • 當(dāng)在你的同一個Configuration配置類中,注入到容器中的bean實例之間沒有依賴關(guān)系時,建議使用Lite輕量級模式,以提高springboot的啟動速度和性能

          Starter命名規(guī)范

          • Spring官方Starter通常命名為spring-boot-starter-{name}如:spring-boot-starter-web
          • Spring官方建議非官方Starter命名應(yīng)遵循{name}-spring-boot-starter的格式:如mybatis-spring-boot-starter。

          開發(fā)Starter

          1. 創(chuàng)建Starter項目

          創(chuàng)建Starter項目
          • 新建項目后,要刪除main啟動類

          2. 添加依賴


          <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">

          ????<parent>
          ????????<groupId>org.springframework.bootgroupId>
          ????????<artifactId>spring-boot-starter-parentartifactId>
          ????????<version>2.6.1version>
          ????????<relativePath/>?
          ????parent>

          ????<modelVersion>4.0.0modelVersion>
          ????<groupId>com.ljwgroupId>
          ????<artifactId>ljw-spring-boot-starterartifactId>
          ????<version>1.0version>
          ????
          ???<properties>
          ????????<java.version>1.8java.version>
          ????????<maven.compiler.source>8maven.compiler.source>
          ????????<maven.compiler.target>8maven.compiler.target>
          ????properties>


          ????<dependencies>

          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starterartifactId>
          ????????dependency>

          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-autoconfigureartifactId>
          ????????dependency>

          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-configuration-processorartifactId>
          ????????????<optional>trueoptional>
          ????????dependency>

          ????dependencies>

          project>
          • 我們沒有main入口,需要去除pom文件中maven打包插件spring-boot-maven-plugin
          • spring-boot-configuration-processor作用:
            • spring-boot-configuration-processor其實是一個注解處理器,在編譯階段干活的,一般在maven的聲明都是optional 為true
            • 你在idea里面可以點擊port,進(jìn)到這個字段里面,還可以看到配置的提示信息
            • 這是因為在你的資源文件里面有一個spring-configuration-metadata.json文件,這是spring配置的元數(shù)據(jù),是json形式

          3. 編寫屬性類

          @ConfigurationProperties可以定義一個配置信息類,和配置文件進(jìn)行映射

          @ConfigurationProperties(prefix?=?"ljw.config")
          public?class?HelloProperties?{

          ????private?String?name?=?"hello 默認(rèn)值!";

          ????private?int?age?=?8;

          ????public?int?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(int?age)?{
          ????????this.age?=?age;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?void?setName(String?name)?{
          ????????this.name?=?name;
          ????}
          }

          4. 自定義業(yè)務(wù)類

          這里可以模擬一些獲取了配置文件信息的進(jìn)行業(yè)務(wù)操作的業(yè)務(wù)類

          public?class?HelloService?{

          ????private?String?name;

          ????private?int?age;

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?void?setName(String?name)?{
          ????????this.name?=?name;
          ????}

          ????public?int?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(int?age)?{
          ????????this.age?=?age;
          ????}

          ????public?String?hello()?{
          ????????return?"HelloService{"?+
          ????????????????"name='"?+?name?+?'''?+
          ????????????????",?age="?+?age?+
          ????????????????'
          }';
          ????}
          }

          5. 編寫自動配置類

          命名規(guī)范:XxxAutoConfiguration

          @Configuration(proxyBeanMethods?=?false)
          //?當(dāng)存在某個類時,此自動配置類才會生效
          @ConditionalOnClass(value?=?{HelloService.class})
          //?導(dǎo)入我們自定義的配置類,供當(dāng)前類使用
          @EnableConfigurationProperties(value?
          =?HelloProperties.class)
          //?只有非web應(yīng)用程序時此自動配置類才會生效
          @ConditionalOnWebApplication
          //判斷ljw.config.flag的值是否為“true”,?matchIfMissing?
          =?true:沒有該配置屬性時也會正常加載
          @ConditionalOnProperty(prefix?=?"ljw.config",?name?=?"flag",?havingValue?=?"true",?matchIfMissing?=?true)
          public?class?HelloAutoConfiguration?{

          ????/**
          ?????*?@param?helloProperties?直接方法簽名入?yún)⒆⑷際elloProperties,也可以使用屬性注入
          ?????*?@return
          ?????*/

          ????@Bean
          ????@ConditionalOnMissingBean(HelloService.class)
          ????//@ConditionalOnProperty(prefix?
          =?"ljw.config",?name?=?"flag",?havingValue?=?"true",?matchIfMissing?=?true)
          ????public?HelloService?helloService(HelloProperties?helloProperties)?{
          ????????HelloService?helloService?=?new?HelloService();
          ????????//把獲取的信息注入
          ????????helloService.setName(helloProperties.getName());
          ????????helloService.setAge(helloProperties.getAge());
          ????????return?helloService;
          ????}

          }

          注:這里配置一個web應(yīng)用才能注入,并且ljw.config.flag的值是否為“true”或者不配置該key才能注入HelloService服務(wù)

          6. 編寫spring.factories

          把自動配置類HelloAutoConfiguration配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration的key下,springboot會自動加載該文件并根據(jù)條件裝配

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

          7. 編寫配置提示文件(非必須)

          additional-spring-configuration-metadata.json

          配置additional-spring-configuration-metadata.json文件后,在開發(fā)人員的IDE工具使用個人編寫的配置讀取很有效的在application.propertiesapplication.yml文件下完成提示。

          配置詳細(xì)格式參數(shù)可查看文檔:https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-format。

          我的配置:

          {"properties":?[
          ????{
          ??????"name":?"ljw.config.name",
          ??????"type":?"java.lang.String",
          ??????"defaultValue":?"hello 默認(rèn)值!這里配置的是提示,真正默認(rèn)值在Properties里面",
          ??????"description":?"這是字符串名稱啊."
          ????},
          ????{
          ??????"name":?"ljw.config.age",
          ??????"defaultValue":?8,
          ??????"description":?"這是int類型的年齡啊.",
          ??????"deprecation":?{
          ??????????????"reason":?"過時原因.",
          ??????????????"replacement":?"替代key是:ljw.config.age22",
          ??????????????"level":?"warning"
          ????????????}
          ????}
          ]}

          大家參考下面properties表格進(jìn)行配置上的理解。

          名稱類型目的
          nameString屬性的全名。名稱采用小寫的周期分隔形式(例如server.address)。此屬性是強制性的。
          typeString屬性的數(shù)據(jù)類型的完整簽名(例如java.lang.String),但也是完整的泛型類型(例如java.util.Map)。您可以使用此屬性來指導(dǎo)用戶可以輸入的值的類型。為了保持一致性,通過使用其包裝對應(yīng)項(例如,boolean變?yōu)閖ava.lang.Boolean)來指定基元的類型。請注意,此類可能是一個復(fù)雜類型,它從Stringas綁定的值轉(zhuǎn)換而來。如果類型未知或基本類型,則可以省略。
          descriptionString可以向用戶顯示的組的簡短描述。如果沒有可用的描述,則可以省略。建議描述為簡短段落,第一行提供簡明摘要。描述中的最后一行應(yīng)以句點(.)結(jié)尾。
          sourceTypeString貢獻(xiàn)此屬性的源的類名稱。例如,如果屬性來自帶注釋的類@ConfigurationProperties,則此屬性將包含該類的完全限定名稱。如果源類型未知,則可以省略。
          defaultValueObject默認(rèn)值,如果未指定屬性,則使用該值。如果屬性的類型是數(shù)組,則它可以是值數(shù)組。如果默認(rèn)值未知,則可以省略。
          deprecation數(shù)組過時的描述。

          deprecation每個properties元素的屬性中包含的JSON對象可以包含以下屬性:

          名稱類型目的
          levelString棄用級別,可以是warning(默認(rèn))或error。當(dāng)屬性具有warning棄用級別時,它仍應(yīng)綁定在環(huán)境中。但是,當(dāng)它具有error棄用級別時,該屬性不再受管理且不受約束。
          reasonString該屬性被棄用的原因的簡短描述。如果沒有可用的原因,可以省略。建議描述為簡短段落,第一行提供簡明摘要。描述中的最后一行應(yīng)以句點(.)結(jié)尾。
          replacementString替換此不推薦使用的屬性的屬性的全名。如果此屬性沒有替換,則可以省略。

          SpringBoot配置文件提示

          spring-configuration-metadata.json代碼量挺大的,為了方便我們可以通過IDE來生成,這里使用的是idea。

          「在idea設(shè)置中搜索Annotation Processors,接下來勾住Enable annonation processing就完成了。在編譯打包后的文件中看到自動生成的spring-configuration-metadata.json。這個文件不用我們編寫」

          spring-configuration-metadata.json

          下面是自動生成的:

          {
          ??"groups":?[
          ????{
          ??????"name":?"ljw.config",
          ??????"type":?"com.ljw.starter.properties.HelloProperties",
          ??????"sourceType":?"com.ljw.starter.properties.HelloProperties"
          ????}
          ??],
          ??"properties":?[
          ????{
          ??????"name":?"ljw.config.name",
          ??????"type":?"java.lang.String",
          ??????"description":?"這是字符串名稱啊.",
          ??????"sourceType":?"com.ljw.starter.properties.HelloProperties",
          ??????"defaultValue":?"hello 默認(rèn)值!這里配置的是提示,真正默認(rèn)值在Properties里面"
          ????},
          ????{
          ??????"name":?"ljw.config.age",
          ??????"type":?"java.lang.Integer",
          ??????"description":?"這是int類型的年齡啊.",
          ??????"sourceType":?"com.ljw.starter.properties.HelloProperties",
          ??????"defaultValue":?8,
          ??????"deprecated":?true,
          ??????"deprecation":?{
          ????????"level":?"warning",
          ????????"reason":?"過時原因.",
          ????????"replacement":?"替代key是:ljw.config.age22"
          ??????}
          ????}
          ??],
          ??"hints":?[]
          }

          測試Starter

          1. 前置環(huán)境

          install打包自定義starter項目:ljw-spring-boot-starter

          新建項目:ljw-test-spring-boot-starter。

          2. 添加依賴

          引入打好包的自定義starter

          <dependencies>
          ????????
          ????<dependency>
          ????????<groupId>org.springframework.bootgroupId>
          ????????<artifactId>spring-boot-starterartifactId>
          ????dependency>
          ????
          ????<dependency>
          ????????<groupId>org.springframework.bootgroupId>
          ????????<artifactId>spring-boot-starter-webartifactId>
          ????dependency>
          ????
          ????<dependency>
          ????????<groupId>com.ljwgroupId>
          ????????<artifactId>ljw-spring-boot-starterartifactId>
          ????????<version>1.0version>
          ????dependency>
          dependencies>

          3. 測試類

          @Service
          public?class?TestController?implements?CommandLineRunner?{

          ????/**
          ?????*?注入自定義starter服務(wù)
          ?????*/

          ????@Resource
          ????private?HelloService?helloService;

          ????@Override
          ????public?void?run(String...?args)?throws?Exception?{
          ????????System.out.println(helloService.hello());
          ????}
          }

          4. 修改配置文件

          輸入前綴可以看出已經(jīng)有提示了

          SpringBoot配置文件前綴提示
          ljw.config.name=ljw hello!
          ljw.config.age=99
          ljw.config.flag=true
          #不會注入
          #ljw.config.flag=true1
          # 可以看到哪些自動配置了
          debug=true

          5. 運行程序打印

          HelloService{name='ljw?hello!',?age=99}
          • 條件注入
            • 如果沒有spring-boot-starter-web依賴,不能注入服務(wù)HelloService
            • 如果配置了ljw.config.flag,值不是true,不能注入服務(wù)HelloService;如果不配置ljw.config.flag,可以注入

          6. 查看自動配置類生效的方法

          通過啟用 debug=true 屬性,讓控制臺打印自動配置報告,這樣就可以很方便地知道哪些自動配置類生效。

          HelloAutoConfiguration?matched:
          ??-?@ConditionalOnClass?found?required?class?'com.ljw.starter.service.HelloService'?(OnClassCondition)
          ??-?@ConditionalOnWebApplication?(required)?found?'session'?scope?(OnWebApplicationCondition)
          ??-?@ConditionalOnProperty?(ljw.config.flag
          =true)?matched?(OnPropertyCondition)

          HelloAutoConfiguration#helloService?matched:
          ??-?@ConditionalOnMissingBean?(types:?com.ljw.starter.service.HelloService;?SearchStrategy:?all)?did?not?find?any?beans?(OnBeanCondition)
          • ????:有收獲的,點贊鼓勵!
          • ??:收藏文章,方便回看!
          • ??:評論交流,互相進(jìn)步!

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99国产婷婷踪合在线免费视频 | 天天干,天天日,天天插,人人人透 午夜久久精品嫖妓av一区二区三区 | 91探花秘 在线播放偷拍 | 成人免费黄片 | 国产在线无码视频56CC中文字幕 |