spring項目中starter包的原理,以及自定義starter包的使用
MAVEN項目中starter的原理
一.原始方式
我們最早配置spring應用的時候,必須要經歷的步驟:1.pom文件中引入相關的jar包,包括spring,redis,jdbc等等 2.通過properties或者xml配置相關的信息 3.不斷調試直到可以使用。
問題:時間長,複雜,同時在寫下一個項目的時候大概率要經過相同的模式配置才能達到可以使用的狀態。同時在眾多的jar中,我們需要相互配置依賴間的版本關係,十分的複雜
原始版本:
我們就想到能不能把這些jdbc整合起來,類似於深度學習中anaconda下載依賴一樣去管理,依賴間的關係不需要我們去負責,而是交給spring去管理。
starter版本:
我們可以將starter包看作是一個包裝箱,把複雜的事情都交給了spring負責,官方維護starter包會導入的東西。而我們只需要知道那個starter包是有什麼用處,例如:spring-boot-starter-web是負責spring web項目的依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
二.starter內部詳情
starter文件也只是一個pom文件,而不是jar,它的目的也是去自動的引入其他的jar文件,上圖展示的spring-boot-starter-web中的依賴就有spring-boot-starter。
starter只是一個pom文件
下面也就是starter的關鍵所在,請問我們為什麼引入了starter之後只需要配置一點點的個性化設置,例如創建application.properties僅僅配置端口等等就可以完成啟動應用?是誰幫助我們配置了其他複雜的信息?
引出自動配置
三.自動配置
1.自動配置類的梳理
自動配置主要通過xxxAutoConfiguration這些類來實現,我們查找一個這樣的類來進行示例演示
上圖是DataSourceAutoConfiguration這個自動配置類,我們可以看到類上的幾個註解。
- @Configuration 將該類標記為配置類,@Configuration註解的類可以看作是能生產讓Spring IoC容器管理的Bean實例的工廠
- @ConditionalOnClass表示某個類位於類路徑上時候,才會實例化這個bean
- @EnableConfigurationProperties註解的作用是使@ConfigurationProperties註解生效。如果只配置@ConfigurationProperties註解,在spring容器中是獲取不到yml或者properties配置文件轉化的bean的。
我們點擊@EnableConfiguration註解中的@DataSourceProperties進去查看
可以查看到這裡使用@configurationPropertes,@ConfigurationProperties註解的作用是把yml或者properties配置文件轉化為bean。
同時這裡也設置了prefix前綴,在我們項目的application.properties中配置的時候,提示的就是這些bean實例中的屬性。
所以是使用@ConfigurationProperties和@EnableConfigurationProperties這兩個註解來完成將一個包含眾多屬性的類來註冊成為可供springIoc容器管理的bean。
而這個bean的註冊過程是在各個XXXAutoConfiguration類中完成的。
2.如何發現依賴包中的bean
我們都知道springboot默認掃描啟動類下面的主類和子類的bean來完成註解,但是並沒有包括依賴包中的類,那麼依賴包中的bean是如何被發現和加載的?
關鍵在於@SpringBootApplication這個註解
註解層次:
-
@springbootApplication
-
@SpringBootConfiguration:和@Configuration相同的用處,並且將裏面通過@bean註解標註的方法的返回值作為bean對象註冊到ioc容器之中。
獲得bean的兩種方式,一種是在配置類中通過方法返回,一種是直接在類上註解@bean(或者相同的註解,類似@Mapper等)來註冊為bean實例
-
@EnableAutoConfiguration:藉助@Import的支持,收集和註冊依賴包中相關的bean定義。
-
@Import({AutoConfigurationImportSelector.class}):該註解掃描依賴包下需要註冊為bean的自動配置類。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; }
SpringFactoriesLoader.loadFactoryNames方法調用loadSpringFactories方法從所有的jar包中讀取META-INF/spring.factories文件信息。
而Spring.factories中key/value中就有一個key是:org.springframework.boot.autoconfigure.EnableAutoConfiguration,後面跟着的都是需要AutoConfigurationImportSelector來進行註冊的自動配置類
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
-
@AutoConfigurationPackage
-
@Import({Registrar.class}):Registrar就是掃描啟動類目錄下的所有bean並且註冊,具體實現通過下列代碼:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() { } public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()); } public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata)); } }
-
-
-
註解層次圖示:
3.如何加載發現的bean
如果要讓一個普通類交給Spring容器管理,通常有以下方法:
- 使用 @Configuration與@Bean 註解
- 使用@Controller @Service @Repository @Component 註解標註該類,然後啟用@ComponentScan自動掃描
- 使用@Import 方法
springboot中使用了@Import 方法
@EnableAutoConfiguration註解中使用了@Import({AutoConfigurationImportSelector.class})註解,AutoConfigurationImportSelector實現了DeferredImportSelector接口,
DeferredImportSelector接口繼承了ImportSelector接口,ImportSelector接口只有一個selectImports方法。
selectImports方法返回一組bean,@EnableAutoConfiguration註解藉助@Import註解將這組bean注入到spring容器中,springboot正式通過這種機制來完成bean的注入的。
關於@import註解的加載可以查看這個文章://zhuanlan.zhihu.com/p/147025312
ps:暈乎乎的,我只看懂了一部分。
加載redisAutoConfiguration的流程圖示
四.自定義starter
下面我們演示自定義starter的流程:
我們確定好自定義starter的GAV(groupId,ArtifactId,Version),這裡需要注意的是ArtifactId的命名,對於spring進行管理的starter包,命名規則是:spring-boot-starter-xxx,而為了區別spring進行管理的starter包,自定義的starter包一般命名規則是:xxx-spring-boot-starter,例如mybatis官方推出的starter包:mybatis-spring-boot-starter。
groupId使用自己域名的反寫即可。
項目結構如下:
1.pom文件導入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.7.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.7.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
依賴:spring-boot-configuration-processor 作用:導入之後會自動生成元數據(meta-data),在application.properties配置的時候會有提示。
例如:
由於是自動生成,在你配置了@ConfigurationProperties之後就會自動生成元數據,在你寫application.properties的時候就會進行自動提示。
當無法提示的時候多次clean,之後再compile然後在install發佈。
具體可以看這個博客 //blog.csdn.net/wangleleb/article/details/104904348
依賴: spring-boot-starter 作用:是為了使用前面提到的自動配置的註解。註解@ConfigurationProperties和@EnableConfigurationProperties兩個註解都在spring-boot-context包中
2.編寫代碼
編寫自動配置類代碼:
package properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "demo")
@Getter
@Setter
public class DemoProperties{
private String var1;
private String var2;
}
編寫service代碼:
package service;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class DemoService {
public String var1;
public String var2;
public String variable(){
return this.var1 + " " + this.var2;
}
}
編寫config類:
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
// 只有當name的值與havingValue的值相同的時候加載
@ConditionalOnProperty(
prefix = "demo",
name = "isopen",
havingValue = "true"
)
public class DemoConfig {
@Resource
private DemoProperties demoProperties;
@Bean
public DemoService demoService(){
return new DemoService(demoProperties.getVar1(),demoProperties.getVar2());
}
}
關於這裡@ConditionOnProperty這個註解,該註解的大概含義就是,對於前綴是demo的屬性,底下的值isopen為true時候,該自動配置類才會生效。
編寫spring.factories
將key:EnableAutoConfiguration–>DemoConfig這個類
3.打包
使用maven命令:mvn clean compile install 清理,編譯,發佈到本地倉庫中去
4.其他項目引入
<!--引入我寫的starter-->
<dependency>
<groupId>org.oldoldcoder</groupId>
<artifactId>oldoldcoder-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
配置資源文件
# 使用自己寫的starter
demo.isopen=true
demo.var1=var1
demo.var2==var2
隨便編寫一個類驗證
@Component
public class TestService {
@Resource
private DemoService demoService;
@PostConstruct
public void test(){
System.out.println("你好"+demoService.variable());
}
}
結果