SpringBoot自動(dòng)裝配原理及過(guò)程解讀
一、SPI源碼分析
為什么要講SPI呢?因?yàn)樵赟pringBoot的自動(dòng)裝配中其實(shí)有使用到SPI機(jī)制,所以掌握了這部分對(duì)于SpringBoot的學(xué)習(xí)還是很有幫助的。
SPI ,全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。它通過(guò)在ClassPath路徑下的 META-INF/services 文件夾查找文件,自動(dòng)加載文件里所定義的類。這一機(jī)制為很多框架擴(kuò)展提供了可能,比如在Dubbo、JDBC中都使用到了SPI機(jī)制。我們先通過(guò)一個(gè)很簡(jiǎn)單的例子來(lái)看下它是怎么用的。
案例介紹
先定義接口項(xiàng)目

然后創(chuàng)建一個(gè)擴(kuò)展的實(shí)現(xiàn),先導(dǎo)入上面接口項(xiàng)目的依賴
<dependencies>
<dependency>
<groupId>com.bobo</groupId>
<artifactId>JavaSPIBase</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>然后創(chuàng)建接口的實(shí)現(xiàn)
/**
* SPI:MySQL對(duì)于 baseURL 的一種實(shí)現(xiàn)
*/
public class MySQLData implements BaseData {
@Override
public void baseURL() {
System.out.println("mysql 的擴(kuò)展實(shí)現(xiàn)....");
}
}然后在resources目錄下創(chuàng)建 META-INF/services 目錄,然后在目錄中創(chuàng)建一個(gè)文件,名稱必須是定義的接口的全類路徑名稱。然后在文件中寫(xiě)上接口的實(shí)現(xiàn)類的全類路徑名稱。

同樣的再創(chuàng)建一個(gè)案例

然后在測(cè)試的項(xiàng)目中測(cè)試
public static void main(String[] args) {
ServiceLoader<BaseData> providers = ServiceLoader.load(BaseData.class);
Iterator<BaseData> iterator = providers.iterator();
while(iterator.hasNext()){
BaseData next = iterator.next();
next.baseURL();
}
}根據(jù)不同的導(dǎo)入,執(zhí)行的邏輯會(huì)有不同


源碼查看
- ServiceLoader
首先來(lái)看下ServiceLoader的類結(jié)構(gòu)
// 配置文件的路徑
private static final String PREFIX = "META-INF/services/";
// 加載的服務(wù) 類或者接口
private final Class<S> service;
// 類加載器
private final ClassLoader loader;
// 訪問(wèn)權(quán)限的上下文對(duì)象
private final AccessControlContext acc;
// 保存已經(jīng)加載的服務(wù)類
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 內(nèi)部類,真正加載服務(wù)類
private LazyIterator lookupIterator;- load
load方法創(chuàng)建了一些屬性,重要的是實(shí)例化了內(nèi)部類,LazyIterator。
public final class ServiceLoader<S> implements Iterable<S>
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//要加載的接口
service = Objects.requireNonNull(svc, "Service interface cannot be null");
//類加載器
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
//訪問(wèn)控制器
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
public void reload() {
//先清空
providers.clear();
//實(shí)例化內(nèi)部類
LazyIterator lookupIterator = new LazyIterator(service, loader);
}
}查找實(shí)現(xiàn)類和創(chuàng)建實(shí)現(xiàn)類的過(guò)程,都在LazyIterator完成。
當(dāng)我們調(diào)用iterator.hasNext和iterator.next方法的時(shí)候,實(shí)際上調(diào)用的都是LazyIterator的相應(yīng)方法。
private class LazyIterator implements Iterator<S>{
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private boolean hasNextService() {
//第二次調(diào)用的時(shí)候,已經(jīng)解析完成了,直接返回
if (nextName != null) {
return true;
}
if (configs == null) {
//META-INF/services/ 加上接口的全限定類名,就是文件服務(wù)類的文件
//META-INF/services/com.viewscenes.netsupervisor.spi.SPIService
String fullName = PREFIX + service.getName();
//將文件路徑轉(zhuǎn)成URL對(duì)象
configs = loader.getResources(fullName);
}
while ((pending == null) || !pending.hasNext()) {
//解析URL文件對(duì)象,讀取內(nèi)容,最后返回
pending = parse(service, configs.nextElement());
}
//拿到第一個(gè)實(shí)現(xiàn)類的類名
nextName = pending.next();
return true;
}
}創(chuàng)建實(shí)例對(duì)象,當(dāng)然,調(diào)用next方法的時(shí)候,實(shí)際調(diào)用到的是,lookupIterator.nextService。
它通過(guò)反射的方式,創(chuàng)建實(shí)現(xiàn)類的實(shí)例并返回。
private class LazyIterator implements Iterator<S>{
private S nextService() {
//全限定類名
String cn = nextName;
nextName = null;
//創(chuàng)建類的Class對(duì)象
Class<?> c = Class.forName(cn, false, loader);
//通過(guò)newInstance實(shí)例化
S p = service.cast(c.newInstance());
//放入集合,返回實(shí)例
providers.put(cn, p);
return p;
}
}看到這兒,我想已經(jīng)很清楚了。獲取到類的實(shí)例,我們自然就可以對(duì)它為所欲為了!
二、自動(dòng)裝配源碼分析
在前面的分析中,Spring Framework一直在致力于解決一個(gè)問(wèn)題,就是如何讓bean的管理變得更簡(jiǎn)單,如何讓開(kāi)發(fā)者盡可能的少關(guān)注一些基礎(chǔ)化的bean的配置,從而實(shí)現(xiàn)自動(dòng)裝配。所以,所謂的自動(dòng)裝配,實(shí)際上就是如何自動(dòng)將bean裝載到Ioc容器中來(lái)。
實(shí)際上在spring 3.x版本中,Enable模塊驅(qū)動(dòng)注解的出現(xiàn),已經(jīng)有了一定的自動(dòng)裝配的雛形,而真正能夠?qū)崿F(xiàn)這一機(jī)制,還是在spirng 4.x版本中,conditional條件注解的出現(xiàn)。ok,我們來(lái)看一下spring boot的自動(dòng)裝配是怎么一回事。
自動(dòng)裝配的演示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>spring:
redis:
host: 127.0.0.1
port: 6379@Autowired
private RedisTemplate<String,String>redisTemplate;按照下面的順序添加starter,然后添加配置,使用RedisTemplate就可以使用了? 那大家想沒(méi)想過(guò)一個(gè)問(wèn)題,為什么RedisTemplate可以被直接注入?它是什么時(shí)候加入到Ioc容器的呢? 這就是自動(dòng)裝配。自動(dòng)裝配可以使得classpath下依賴的包相關(guān)的bean,被自動(dòng)裝載到Spring Ioc容器中,怎么做到的呢?
深入分析EnableAutoConfiguration
EnableAutoConfiguration的主要作用其實(shí)就是幫助springboot應(yīng)用把所有符合條件的@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建并使用的IoC容器中。
再回到EnableAutoConfiguration這個(gè)注解中,我們發(fā)現(xiàn)它的import是這樣
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {但是從EnableAutoCOnfiguration上面的import注解來(lái)看,這里面并不是引入另外一個(gè)Configuration。而是一個(gè)ImportSelector。這個(gè)是什么東西呢?
AutoConfigurationImportSelector是什么?
Enable注解不僅僅可以像前面演示的案例一樣很簡(jiǎn)單的實(shí)現(xiàn)多個(gè)Configuration的整合,還可以實(shí)現(xiàn)一些復(fù)雜的場(chǎng)景,比如可以根據(jù)上下文來(lái)激活不同類型的bean,@Import注解可以配置三種不同的class
第一種就是前面演示過(guò)的,基于普通bean或者帶有@Configuration的bean進(jìn)行諸如
實(shí)現(xiàn)ImportSelector接口進(jìn)行動(dòng)態(tài)注入
實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口進(jìn)行動(dòng)態(tài)注入
- CacheService:
public class CacheService {}- LoggerService:
public class LoggerService {}- EnableDefineService:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited --允許被繼承
@Import({MyDefineImportSelector.class})
public @interface EnableDefineService {
String[] packages() default "";
}- MyDefineImportSelector:
public class MyDefineImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//獲得指定注解的詳細(xì)信息。我們可以根據(jù)注解中配置的屬性來(lái)返回不同的class,
//從而可以達(dá)到動(dòng)態(tài)開(kāi)啟不同功能的目的
annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true)
.forEach((k,v) -> {
log.info(annotationMetadata.getClassName());
log.info("k:{},v:{}",k,String.valueOf(v));
});
return new String[]{CacheService.class.getName()};
}
}- EnableDemoTest:
@SpringBootApplication
@EnableDefineService(name = "aizer",value = "aizer")
public class EnableDemoTest {
public static void main(String[] args) {
ConfigurableApplicationContext ca=SpringApplication.run(EnableDemoTest.class,args);
System.out.println(ca.getBean(CacheService.class));
System.out.println(ca.getBean(LoggerService.class));
}
}了解了selector的基本原理之后,后續(xù)再去分析AutoConfigurationImportSelector的原理就很簡(jiǎn)單了,它本質(zhì)上也是對(duì)于bean的動(dòng)態(tài)加載。
@EnableAutoConfiguration注解的實(shí)現(xiàn)原理
了解了ImportSelector和ImportBeanDefinitionRegistrar后,對(duì)于EnableAutoConfiguration的理解就容易一些了
它會(huì)通過(guò)import導(dǎo)入第三方提供的bean的配置類:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
從名字來(lái)看,可以猜到它是基于ImportSelector來(lái)實(shí)現(xiàn)基于動(dòng)態(tài)bean的加載功能。之前我們講過(guò)Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的數(shù)組(類的全類名)都會(huì)被納入到spring容器中。
那么可以猜想到這里的實(shí)現(xiàn)原理也一定是一樣的,定位到AutoConfigurationImportSelector這個(gè)類中的selectImports方法
- selectImports:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 從配置文件(spring-autoconfigure-metadata.properties)中加載 AutoConfigurationMetadata
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 獲取所有候選配置類EnableAutoConfiguration
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}- getAutoConfigurationEntry:
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//獲取元注解中的屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoriesLoader 加載classpath路徑下META-INF\spring.factories中,
//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration對(duì)應(yīng)的value
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//應(yīng)用exclusion屬性
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//過(guò)濾,檢查候選配置類上的注解@ConditionalOnClass,如果要求的類不存在,則這個(gè)候選類會(huì)被過(guò)濾不被加載
configurations = filter(configurations, autoConfigurationMetadata);
//廣播事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}本質(zhì)上來(lái)說(shuō),其實(shí)EnableAutoConfiguration會(huì)幫助springboot應(yīng)用把所有符合@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建的IoC容器,而這里面借助了Spring框架提供的一個(gè)工具類SpringFactoriesLoader的支持。以及用到了Spring提供的條件注解@Conditional,選擇性的針對(duì)需要加載的bean進(jìn)行條件過(guò)濾
SpringFactoriesLoader
為了給大家補(bǔ)一下基礎(chǔ),我在這里簡(jiǎn)單分析一下SpringFactoriesLoader這個(gè)工具類的使用。它其實(shí)和java中的SPI機(jī)制的原理是一樣的,不過(guò)它比SPI更好的點(diǎn)在于不會(huì)一次性加載所有的類,而是根據(jù)key進(jìn)行加載。
首先,SpringFactoriesLoader的作用是從classpath/META-INF/spring.factories文件中,根據(jù)key來(lái)加載對(duì)應(yīng)的類到spring IoC容器中。接下來(lái)帶大家實(shí)踐一下
- 創(chuàng)建外部項(xiàng)目jar:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.13.RELEASE</version> </dependency>
- 創(chuàng)建bean以及config:
public class aizerCore {
public String study(){
System.out.println("good good study, day day up");
return "aizerEdu.com";
}
}
@Configuration
public class aizerConfig {
@Bean
public aizerCore aizerCore(){
return new aizerCore();
}
}- 創(chuàng)建另外一個(gè)工程(spring-boot):
把前面的工程打包成jar,當(dāng)前項(xiàng)目依賴該jar包
<dependency>
<groupId>com.aizeredu.practice</groupId>
<artifactId>aizer-Core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>- 通過(guò)下面代碼獲取依賴包中的屬性:
運(yùn)行結(jié)果會(huì)報(bào)錯(cuò),原因是aizerCore并沒(méi)有被Spring的IoC容器所加載,也就是沒(méi)有被EnableAutoConfiguration導(dǎo)入
@SpringBootApplication
public class SpringBootStudyApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ac=SpringApplication.run(SpringBootStudyApplication.class, args);
aizerCore Myc=ac.getBean(aizerCore.class);
System.out.println(Myc.study());
}
}- 解決方案:
在aizer-Core項(xiàng)目resources下新建文件夾META-INF,在文件夾下面新建spring.factories文件,文件中配置,key為自定配置類EnableAutoConfiguration的全路徑,value是配置類的全路徑
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.aizeredu.practice.aizerConfig
重新打包,重新運(yùn)行SpringBootStudyApplication這個(gè)類。
可以發(fā)現(xiàn),我們編寫(xiě)的那個(gè)類,就被加載進(jìn)來(lái)了。
Spring Boot中的條件過(guò)濾
在分析AutoConfigurationImportSelector的源碼時(shí),會(huì)先掃描spring-autoconfiguration-metadata.properties文件,最后在掃描spring.factories對(duì)應(yīng)的類時(shí),會(huì)結(jié)合前面的元數(shù)據(jù)進(jìn)行過(guò)濾,為什么要過(guò)濾呢?
原因是很多的@Configuration其實(shí)是依托于其他的框架來(lái)加載的,如果當(dāng)前的classpath環(huán)境下沒(méi)有相關(guān)聯(lián)的依賴,則意味著這些類沒(méi)必要進(jìn)行加載,所以,通過(guò)這種條件過(guò)濾可以有效的減少@configuration類的數(shù)量從而降低SpringBoot的啟動(dòng)時(shí)間。
修改aizer-Core:
在META-INF/增加配置文件,spring-autoconfigure-metadata.properties。
com.aizeredu.practice.aizerConfig.ConditionalOnClass=com.aizeredu.TestClass
格式:自動(dòng)配置的類全名.條件=值
上面這段代碼的意思就是,如果當(dāng)前的classpath下存在TestClass,則會(huì)對(duì)aizerConfig這個(gè)Configuration進(jìn)行加載
演示過(guò)程(spring-boot):
沿用前面spring-boot工程的測(cè)試案例,直接運(yùn)行main方法,發(fā)現(xiàn)原本能夠被加載的aizerCore,發(fā)現(xiàn)在ioc容器中找不到了。
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ac=SpringApplication.run(SpringBootStudyApplication.class, args);
aizerCore Myc=ac.getBean(aizerCore.class);
System.out.println(Myc.study());
}在當(dāng)前工程中指定的包c(diǎn)om.aizeredu下創(chuàng)建一個(gè)TestClass以后,再運(yùn)行上面這段代碼,程序能夠正常執(zhí)行
三、手寫(xiě)Starter
我們通過(guò)手寫(xiě)Starter來(lái)加深對(duì)于自動(dòng)裝配的理解
1.創(chuàng)建一個(gè)Maven項(xiàng)目,quick-starter
定義相關(guān)的依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
<!-- 可選 -->
<optional>true</optional>
</dependency>2.定義Formate接口
定義的格式轉(zhuǎn)換的接口,并且定義兩個(gè)實(shí)現(xiàn)類
public interface FormatProcessor {
/**
* 定義一個(gè)格式化的方法
* @param obj
* @param <T>
* @return
*/
<T> String formate(T obj);
}public class JsonFormatProcessor implements FormatProcessor {
@Override
public <T> String formate(T obj) {
return "JsonFormatProcessor:" + JSON.toJSONString(obj);
}
}public class StringFormatProcessor implements FormatProcessor {
@Override
public <T> String formate(T obj) {
return "StringFormatProcessor:" + obj.toString();
}
}3.定義相關(guān)的配置類
首先定義格式化加載的Java配置類
@Configuration
public class FormatAutoConfiguration {
@ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
@Bean
@Primary // 優(yōu)先加載
public FormatProcessor stringFormatProcessor(){
return new StringFormatProcessor();
}
@ConditionalOnClass(name="com.alibaba.fastjson.JSON")
@Bean
public FormatProcessor jsonFormatProcessor(){
return new JsonFormatProcessor();
}
}定義一個(gè)模板工具類
public class HelloFormatTemplate {
private FormatProcessor formatProcessor;
public HelloFormatTemplate(FormatProcessor processor){
this.formatProcessor = processor;
}
public <T> String doFormat(T obj){
StringBuilder builder = new StringBuilder();
builder.append("Execute format : ").append("<br>");
builder.append("Object format result:" ).append(formatProcessor.formate(obj));
return builder.toString();
}
}再就是整合到SpringBoot中去的Java配置類
@Configuration
@Import(FormatAutoConfiguration.class)
public class HelloAutoConfiguration {
@Bean
public HelloFormatTemplate helloFormatTemplate(FormatProcessor formatProcessor){
return new HelloFormatTemplate(formatProcessor);
}
}4.創(chuàng)建spring.factories文件
在resources下創(chuàng)建META-INF目錄,再在其下創(chuàng)建spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.aizeredu.autoconfiguration.HelloAutoConfiguration
install 打包,然后就可以在SpringBoot項(xiàng)目中依賴改項(xiàng)目來(lái)操作了。
5.測(cè)試
在SpringBoot中引入依賴
<dependency>
<groupId>org.example</groupId>
<artifactId>format-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>在controller中使用
@RestController
public class UserController {
@Autowired
private HelloFormatTemplate helloFormatTemplate;
@GetMapping("/format")
public String format(){
User user = new User();
user.setName("BoBo");
user.setAge(18);
return helloFormatTemplate.doFormat(user);
}
}6.自定義Starter關(guān)聯(lián)配置信息
有些情況下我們可以需要用戶在使用的時(shí)候動(dòng)態(tài)的傳遞相關(guān)的配置信息,比如Redis的Ip,端口等等,這些信息顯然是不能直接寫(xiě)到代碼中的,這時(shí)我們就可以通過(guò)SpringBoot的配置類來(lái)實(shí)現(xiàn)。
首先引入依賴支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.6.RELEASE</version>
<optional>true</optional>
</dependency>然后創(chuàng)建對(duì)應(yīng)的屬性類
@ConfigurationProperties(prefix = HelloProperties.HELLO_FORMAT_PREFIX)
public class HelloProperties {
public static final String HELLO_FORMAT_PREFIX="aizer.hello.format";
private String name;
private Integer age;
private Map<String,Object> info;
public Map<String, Object> getInfo() {
return info;
}
public void setInfo(Map<String, Object> info) {
this.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}然后再Java配置類中關(guān)聯(lián)
@Configuration
@Import(FormatAutoConfiguration.class)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
@Bean
public HelloFormatTemplate helloFormatTemplate(HelloProperties helloProperties,FormatProcessor formatProcessor){
return new HelloFormatTemplate(helloProperties,formatProcessor);
}
}調(diào)整模板方法
public class HelloFormatTemplate {
private FormatProcessor formatProcessor;
private HelloProperties helloProperties;
public HelloFormatTemplate(HelloProperties helloProperties,FormatProcessor processor){
this.helloProperties = helloProperties;
this.formatProcessor = processor;
}
public <T> String doFormat(T obj){
StringBuilder builder = new StringBuilder();
builder.append("Execute format : ").append("<br>");
builder.append("HelloProperties:").append(formatProcessor.formate(helloProperties.getInfo())).append("<br>");
builder.append("Object format result:" ).append(formatProcessor.formate(obj));
return builder.toString();
}
}增加提示
在這個(gè)工程的META-INF/下創(chuàng)建一個(gè)additional-spring-configuration-metadata.json,這個(gè)是設(shè)置屬性的提示類型
{
"properties": [
{
"name": "aizer.hello.format.name",
"type": "java.lang.String",
"description": "賬號(hào)信息",
"defaultValue": "root"
},{
"name": "aizer.hello.format.age",
"type": "java.lang.Integer",
"description": "年齡",
"defaultValue": 18
}
]
}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 加載當(dāng)前系統(tǒng)下 META-INF/spring.factories 文件中聲明的配置類
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除掉重復(fù)的
configurations = removeDuplicates(configurations);
// 移除掉顯示排除的
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 過(guò)濾掉不需要載入的配置類
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot整合dataworks的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了SpringBoot整合dataworks的實(shí)現(xiàn)過(guò)程,實(shí)現(xiàn)主要是編寫(xiě)工具類,如果需要?jiǎng)t可以配置成SpringBean,注入容器即可使用,需要的朋友可以參考下2022-08-08
SpringBoot如何指定某些類優(yōu)先啟動(dòng)
這篇文章主要介紹了SpringBoot如何指定某些類優(yōu)先啟動(dòng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
基于SpringBoot bootstrap.yml配置未生效的解決
這篇文章主要介紹了基于SpringBoot bootstrap.yml配置未生效的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
Java 入門(mén)圖形用戶界面設(shè)計(jì)之列表框JList
圖形界面(簡(jiǎn)稱GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶來(lái)說(shuō)在視覺(jué)上更易于接受,本篇精講Java語(yǔ)言中關(guān)于圖形用戶界面的列表框JList2022-02-02
SpringBoot中靜態(tài)資源處理的7個(gè)技巧分享
在Web應(yīng)用開(kāi)發(fā)中,靜態(tài)資源(如CSS、JavaScript、圖片等)的處理是一個(gè)基礎(chǔ)但重要的環(huán)節(jié),本文將介紹SpringBoot中7種靜態(tài)資源處理技巧,希望對(duì)大家有所幫助2025-05-05
SpringBoot3和mybatis-plus整合出現(xiàn)的問(wèn)題解決辦法
SpringBoot和MybatisPlus的整合可以讓我們更加方便地進(jìn)行數(shù)據(jù)庫(kù)操作,這篇文章主要給大家介紹了關(guān)于SpringBoot3和mybatisplus整合出現(xiàn)的一些問(wèn)題的相關(guān)資料,需要的朋友可以參考下2024-01-01
查找native方法的本地實(shí)現(xiàn)函數(shù)native_function詳解
JDK開(kāi)放給用戶的源碼中隨處可見(jiàn)Native方法,被Native關(guān)鍵字聲明的方法說(shuō)明該方法不是以Java語(yǔ)言實(shí)現(xiàn)的,而是以本地語(yǔ)言實(shí)現(xiàn)的,Java可以直接拿來(lái)用。這里介紹下查找native方法的本地實(shí)現(xiàn)函數(shù)native_function,感興趣的朋友跟隨小編一起看看吧2021-12-12

