欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

static關(guān)鍵字有何魔法?竟讓Spring Boot搞出那么多靜態(tài)內(nèi)部類(推薦)

 更新時間:2020年07月20日 09:21:02   作者:A哥(YourBatman)  
這篇文章主要介紹了static關(guān)鍵字有何魔法?竟讓Spring Boot搞出那么多靜態(tài)內(nèi)部類,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

生命太短暫,不要去做一些根本沒有人想要的東西。本文已被 https://www.yourbatman.cn 收錄,里面一并有Spring技術(shù)棧、MyBatis、JVM、中間件等小而美的專欄供以免費學(xué)習(xí)。

前言

各位小伙伴大家好,我是A哥。上篇文章了解了static關(guān)鍵字 + @Bean方法的使用,知曉了它能夠提升Bean的優(yōu)先級,在@Bean方法前標(biāo)注static關(guān)鍵字,特定情況下可以避免一些煩人的“警告”日志的輸出,排除隱患讓工程變得更加安全。我們知道static關(guān)鍵字它不僅可使用在方法上,那么本文將繼續(xù)挖掘static在Spring環(huán)境下的用處。

根據(jù)所學(xué)的JavaSE基礎(chǔ),static關(guān)鍵字除了能夠修飾方法外,還能使用在這兩個地方:

修飾類。確切的說,應(yīng)該叫修飾內(nèi)部類,所以它叫靜態(tài)內(nèi)部類修飾成員變量

其實static還可以修飾代碼塊、static靜態(tài)導(dǎo)包等,但很明顯,這些與本文無關(guān)

接下來就以這為兩條主線,分別研究static在對應(yīng)場景下的作用,本文將聚焦在靜態(tài)內(nèi)部類上。

版本約定

本文內(nèi)容若沒做特殊說明,均基于以下版本:

  • JDK:1.8
  • Spring Framework:5.2.2.RELEASE

正文

說到Java里的static關(guān)鍵字,這當(dāng)屬最基礎(chǔ)的入門知識,是Java中常用的關(guān)鍵字之一。你平時用它來修飾變量和方法了,但是對它的了解,即使放在JavaSE情景下知道這些還是不夠的,問題雖小但這往往反映了你對Java基礎(chǔ)的了解程度。

當(dāng)然嘍,本文并不討論它在JavaSE下使用,畢竟咱們還是有一定逼格的專欄,需要進(jìn)階一把,玩玩它在Spring環(huán)境下到底能夠迸出怎么樣的火花呢?比如靜態(tài)內(nèi)部類~

Spring下的靜態(tài)內(nèi)部類

static修飾類只有一種情況:那就是這個類屬于內(nèi)部類,這就是我們津津樂道的靜態(tài)內(nèi)部類,形如這樣:

public class Outer {

 private String name;
 private static Integer age;

 // 靜態(tài)內(nèi)部類
 private static class Inner {

 private String innerName;
 private static Integer innerAge;

 public void fun1() {
 // 無法訪問外部類的成員變量
 //System.out.println(name);
 System.out.println(age);

 System.out.println(innerName);
 System.out.println(innerAge);
 }

 }

 public static void main(String[] args) {
 // 靜態(tài)內(nèi)部類的實例化并不需要依賴于外部類的實例
 Inner inner = new Inner();
 }
}

在實際開發(fā)中,靜態(tài)內(nèi)部類的使用場景是非常之多的。

認(rèn)識靜態(tài)/普通內(nèi)部類

由于一些小伙伴對普通內(nèi)部類 vs 靜態(tài)內(nèi)部類傻傻分不清,為了方便后續(xù)講解,本處把關(guān)鍵要素做簡要對比說明:

  • 靜態(tài)內(nèi)部類可以聲明靜態(tài)or實例成員(屬性和方法);而普通內(nèi)部類則不可以聲明靜態(tài)成員(屬性和方法)
  • 靜態(tài)內(nèi)部類實例的創(chuàng)建不依賴于外部類;而普通外部類實例創(chuàng)建必須先有外部類實例才行(綁定關(guān)系拿捏得死死的,不信你問鄭凱)
  • 靜態(tài)內(nèi)部類不能訪問外部類的實例成員;而普通內(nèi)部類可以隨意訪問(不管靜態(tài)or非靜態(tài)) --> 我理解這是普通內(nèi)部類能 “存活” 下來的最大理由了吧😄

總之,普通內(nèi)部類和外部類的關(guān)系屬于強綁定,而靜態(tài)內(nèi)部類幾乎不會受到外部類的限制,可以游離單獨使用。既然如此,那為何還需要static靜態(tài)內(nèi)部類呢,直接單獨寫個Class類豈不就好了嗎?存在即合理,這么使用的原因我個人覺得有如下兩方面思考,供以你參考:

  • 靜態(tài)內(nèi)部類是弱關(guān)系并不是沒關(guān)系,比如它還是可以訪問外部類的static的變量的不是(即便它是private的)
  • 高內(nèi)聚的體現(xiàn)

在傳統(tǒng)Spirng Framework的配置類場景下,你可能鮮有接觸到static關(guān)鍵字使用在類上的場景,但這在Spring Boot下使用非常頻繁,比如屬性配置類的典型應(yīng)用:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
	
	// server.port = xxx 
	// server.address = xxx
	private Integer port;
	private InetAddress address;
	...
	
	// tomcat配置
	public static class Tomcat {
		
		// server.tomcat.protocol-header = xxx
		private String protocolHeader;
		...
		
		// tomcat內(nèi)的log配置
		public static class Accesslog {
			
			// server.tomcat.accesslog.enabled = xxx
			private boolean enabled = false;
			...
		}
	}	
}

這種嵌套case使得代碼(配置)的key 內(nèi)聚性非常強,使用起來更加方便。試想一下,如果你不使用靜態(tài)內(nèi)部類去集中管理這些配置,每個配置都單獨書寫的話,像這樣:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
}

@ConfigurationProperties(prefix = "server.tomcat", ignoreUnknownFields = true)
public class TomcatProperties {
}

@ConfigurationProperties(prefix = "server.tomcat.accesslog", ignoreUnknownFields = true)
public class AccesslogProperties {
}

這代碼,就問你,如果是你同事寫的,你罵不罵吧!用臃腫來形容還是個中意詞,層次結(jié)構(gòu)體現(xiàn)得也非常的不直觀嘛。因此,對于這種屬性類里使用靜態(tài)內(nèi)部類是非常適合,內(nèi)聚性一下子高很多~

除了在內(nèi)聚性上的作用,在Spring Boot中的@Configuration配置類下(特別常見于自動配置類)也能經(jīng)??吹剿纳碛埃?/p>

@Configuration(proxyBeanMethods = false)
public class WebMvcAutoConfiguration {

	// web MVC個性化定制配置
	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
		...
	}

	@Configuration(proxyBeanMethods = false)
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
		...
	}

}

利用靜態(tài)內(nèi)部類把相似配置類歸并在一個 .java文件 內(nèi),這樣多個static類還可公用外部類的屬性、方法,也是一種高內(nèi)聚的體現(xiàn)。同時static關(guān)鍵字提升了初始化的優(yōu)先級,比如本例中的EnableWebMvcConfiguration它會優(yōu)先于外部類加載~

關(guān)于static靜態(tài)內(nèi)部類優(yōu)先級相關(guān)是重點,靜態(tài)內(nèi)部類的優(yōu)先級會更高嗎?使用普通內(nèi)部能達(dá)到同樣效果嗎?拍腦袋直接回答是沒用的,帶著這兩個問題,接下來A哥舉例領(lǐng)你一探究竟...

static靜態(tài)配置類提升配置優(yōu)先級

自己先構(gòu)造一個Demo,場景如下:

@Configuration
class OuterConfig {

 OuterConfig() {
 System.out.println("OuterConfig init...");
 }
 @Bean
 static Parent parent() {
 return new Parent();
 }

 @Configuration
 private static class InnerConfig {
 InnerConfig() {
 System.out.println("InnerConfig init...");
 }
 @Bean
 Daughter daughter() {
 return new Daughter();
 }
 }
}

測試程序:

@ComponentScan
public class TestSpring {

 public static void main(String[] args) {
 new AnnotationConfigApplicationContext(TestSpring.class);
 }
}

啟動程序,結(jié)果輸出:

InnerConfig init...
OuterConfig init...
Daughter init...
Parent init...

結(jié)果細(xì)節(jié):似乎都是按照字母表的順序來執(zhí)行的。I在前O在后;D在前P在后;

看到這個結(jié)果,如果你就過早的得出結(jié)論:靜態(tài)內(nèi)部類優(yōu)先級高于外部類,那么就太隨意了,圖樣圖森破啊。大膽猜想,小心求證 應(yīng)該是程序員應(yīng)有的態(tài)度,那么繼續(xù)往下看,在此基礎(chǔ)上我新增加一個靜態(tài)內(nèi)部類:

@Configuration
class OuterConfig {

 OuterConfig() {
 System.out.println("OuterConfig init...");
 }
 @Bean
 static Parent parent() {
 return new Parent();
 }


 @Configuration
 private static class PInnerConfig {
 PInnerConfig() {
 System.out.println("PInnerConfig init...");
 }
 @Bean
 Son son() {
 return new Son();
 }
 }

 @Configuration
 private static class InnerConfig {
 InnerConfig() {
 System.out.println("InnerConfig init...");
 }
 @Bean
 Daughter daughter() {
 return new Daughter();
 }
 }
}

我先解釋下我這么做的意圖:

  1. 增加一個字母P開頭的內(nèi)部類,自然順序P在O(外部類)后面,消除影響
  2. P開頭的內(nèi)部類在源碼擺放順序上故意放在了I開頭的內(nèi)部類的上面,同樣為了消除字母表順序帶來的影響
  3. 目的:看看是按照字節(jié)碼順序,還是字母表順序呢?
  4. PInnerConfig里面的@Bean實例為Son,字母表順序是三者中最為靠后的,但字節(jié)碼卻在中間,這樣也能夠消除影響

運行程序,結(jié)果輸出:

InnerConfig init...
PInnerConfig init...
OuterConfig init...
Daughter init...
son init...
Parent init...

結(jié)果細(xì)節(jié):外部類貌似總是滯后于內(nèi)部類初始化;同一類的多個內(nèi)部類之間順序是按照字母表順序(自然排序)初始化而非字節(jié)碼順序;@Bean方法的順序依照了類的順序

請留意本結(jié)果和上面結(jié)果是否有區(qū)別,你應(yīng)該若有所思。

這是單.java文件的case(所有static類都在同一個.java文件內(nèi)),接下來我在同目錄下增加 2個.java文件(請自行留意類名第一個字母,我將不再贅述我的設(shè)計意圖):

// 文件一:
@Configuration
class A_OuterConfig {

 A_OuterConfig() {
 System.out.println("A_OuterConfig init...");
 }
 @Bean
 String a_o_bean(){
 System.out.println("A_OuterConfig a_o_bean init...");
 return new String();
 }


 @Configuration
 private static class PInnerConfig {
 PInnerConfig() {
 System.out.println("A_OuterConfig PInnerConfig init...");
 }
 @Bean
 String a_p_bean(){
 System.out.println("A_OuterConfig a_p_bean init...");
 return new String();
 }
 }

 @Configuration
 private static class InnerConfig {
 InnerConfig() {
 System.out.println("A_OuterConfig InnerConfig init...");
 }
 @Bean
 String a_i_bean(){
 System.out.println("A_OuterConfig a_i_bean init...");
 return new String();
 }
 }
}

// 文件二:
@Configuration
class Z_OuterConfig {

 Z_OuterConfig() {
 System.out.println("Z_OuterConfig init...");
 }
 @Bean
 String z_o_bean(){
 System.out.println("Z_OuterConfig z_o_bean init...");
 return new String();
 }


 @Configuration
 private static class PInnerConfig {
 PInnerConfig() {
 System.out.println("Z_OuterConfig PInnerConfig init...");
 }
 @Bean
 String z_p_bean(){
 System.out.println("Z_OuterConfig z_p_bean init...");
 return new String();
 }
 }

 @Configuration
 private static class InnerConfig {
 InnerConfig() {
 System.out.println("Z_OuterConfig InnerConfig init...");
 }
 @Bean
 String z_i_bean(){
 System.out.println("Z_OuterConfig z_i_bean init...");
 return new String();
 }
 }
}

運行程序,結(jié)果輸出:

A_OuterConfig InnerConfig init...
A_OuterConfig PInnerConfig init...
A_OuterConfig init...
InnerConfig init...
PInnerConfig init...
OuterConfig init...
Z_OuterConfig InnerConfig init...
Z_OuterConfig PInnerConfig init...
Z_OuterConfig init...


A_OuterConfig a_i_bean init...
A_OuterConfig a_p_bean init...
A_OuterConfig a_o_bean init...
Daughter init...
son init...
Parent init...
Z_OuterConfig z_i_bean init...
Z_OuterConfig z_p_bean init...
Z_OuterConfig z_o_bean init...

這個結(jié)果大而全,是有說服力的,通過這幾個示例可以總結(jié)出如下結(jié)論:

垮.java文件 (垮配置類)之間的順序,是由自然順序來保證的(字母表順序)如上:下加載A打頭的配置類(含靜態(tài)內(nèi)部類),再是O打頭的,再是Z打頭的

同一.java文件內(nèi)部,static靜態(tài)內(nèi)部類優(yōu)先于外部類初始化。若有多個靜態(tài)內(nèi)部類,那么按照類名自然排序初始化(并非按照定義順序哦,請務(wù)必注意)說明:一般內(nèi)部類只可能與外部類“發(fā)生關(guān)系”,與兄弟之間不建議有任何聯(lián)系,否則順序控制上你就得當(dāng)心了。畢竟靠自然順序去保證是一種弱保證,容錯性太低

同一.java文件內(nèi),不同類內(nèi)的@Bean方法之間的執(zhí)行順序,保持同2一致(也就說你的@Bean所在的@Configuration配置類先加載,那你就優(yōu)先被初始化嘍)同一Class內(nèi)多個@Bean方法的執(zhí)行順序,上篇文章static關(guān)鍵字真能提高Bean的優(yōu)先級嗎?答:真能 就已經(jīng)說過了哈,請移步參見

總的來說,當(dāng)static標(biāo)注在class類上時,在同.java文件內(nèi)它是能夠提升優(yōu)先級的,這對于Spring Boot的自動配置非常有意義,主要體現(xiàn)在如下兩個方法:

  • static靜態(tài)內(nèi)部類配置優(yōu)先于外部類加載,從而靜態(tài)內(nèi)部類里面的@Bean也優(yōu)先于外部類的@Bean先加載
  • 既然這樣,那么Spring Boot自動配置就可以結(jié)合此特性,就可以進(jìn)行具有優(yōu)先級的@Conditional條件判斷了。

這里我舉個官方的例子,你便能感受到它的魅力所在:

@Configuration
public class FeignClientsConfiguration {
	...
	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}

	@Configuration
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled")
		public Feign.Builder feignHystrixBuilder() {
			return HystrixFeign.builder();
		}
	}
}

因為HystrixFeign.builder()它屬于靜態(tài)內(nèi)部類,所以這個@Bean肯定是優(yōu)先于外部的Feign.builder()先加載的。所以這段邏輯可解釋為:優(yōu)先使用HystrixFeign.builder()(若條件滿足),否則使用Feign.builder().retryer(retryer)作為兜底。通過此例你應(yīng)該再一次感受到Bean的加載順序之于Spring應(yīng)用的重要性,特別在Spring Boot/Cloud下此特性尤為凸顯。

你以為記住這幾個結(jié)論就完事了?不,這明顯不符合A哥的逼格嘛,下面我們就來繼續(xù)挖一挖吧。

源碼分析

關(guān)于@Configuration配置類的順序問題,事前需強調(diào)兩點:

  • 不同 .java文件 之間的加載順序是不重要的,Spring官方也強烈建議使用者不要去依賴這種順序因為無狀態(tài)性,因此你在使用過程中可以認(rèn)為垮@Configuration文件之前的初始化順序是不確定的
  • 同一.javaw文件內(nèi)也可能存在多個@Configuration配置類(比如靜態(tài)內(nèi)部類、普通內(nèi)部類等),它們之間的順序是我們需要關(guān)心的,并且需要強依賴于這個順序編程(比如Spring Boot)

@Configuration配置類只有是被@ComponentScan掃描進(jìn)來(或者被Spring Boot自動配置加載進(jìn)來)才需要討論順序(倘若是構(gòu)建上下文時自己手動指好的,那順序就已經(jīng)定死了嘛),實際開發(fā)中的配置類也確實是醬紫的,一般都是通過掃描被加載。接下來我們看看@ComponentScan是如何掃描的,把此注解的解析步驟(偽代碼)展示如下:

說明:本文并不會著重分析@ComponentScan它的解析原理,只關(guān)注本文“感興趣”部分

1、解析配置類上的@ComponentScan注解(們):本例中TestSpring作為掃描入口,會掃描到A_OuterConfig/OuterConfig等配置類們

ConfigurationClassParser#doProcessConfigurationClass:

	// **最先判斷** 該配置類是否有成員類(普通內(nèi)部類)
	// 若存在普通內(nèi)部類,最先把普通內(nèi)部類給解析嘍(注意,不是靜態(tài)內(nèi)部類)
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		processMemberClasses(configClass, sourceClass);
	}
	
	...

	// 遍歷該配置類上所有的@ComponentScan注解
	// 使用ComponentScanAnnotationParser一個個解析
	for (AnnotationAttributes componentScan : componentScans) {
		Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan,...);
		
		// 繼續(xù)判斷掃描到的bd是否是配置類,遞歸調(diào)用
		... 
	}

細(xì)節(jié)說明:關(guān)于最先解析內(nèi)部類時需要特別注意,Spring通過sourceClass.getMemberClasses()來獲取內(nèi)部類們:只有普通內(nèi)部類屬于這個,static靜態(tài)內(nèi)部類并不屬于它,這點很重要哦

2、解析該注解上的basePackages/basePackageClasses等屬性值得到一些掃描的基包,委托給ClassPathBeanDefinitionScanner去完成掃描

ComponentScanAnnotationParser#parse

	// 使用ClassPathBeanDefinitionScanner掃描,基于類路徑哦
	scanner.doScan(StringUtils.toStringArray(basePackages));

3、遍歷每個基包,從文件系統(tǒng)中定位到資源,把符合條件的Spring組件(強調(diào):這里只指外部@Configuration配置類,還沒涉及到里面的@Bean這些)注冊到BeanDefinitionRegistry注冊中心

ComponentScanAnnotationParser#doScan

	for (String basePackage : basePackages) {
		// 這個方法是本文最需要關(guān)注的方法
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			...
			// 把該配置**類**(并非@Bean方法)注冊到注冊中心
			registerBeanDefinition(definitionHolder, this.registry);
		}
	}

到這一步就完成了Bean定義的注冊,此處可以驗證一個結(jié)論:多個配置類之間,誰先被掃描到,就先注冊誰,對應(yīng)的就是誰最先被初始化。那么這個順序到底是咋樣界定的呢?那么就要來到這中間最為重要(本文最關(guān)心)的一步嘍:findCandidateComponents(basePackage)。

說明:Spring 5.0開始增加了@Indexed注解為云原生做了準(zhǔn)備,可以讓scan掃描動作在編譯期就完成,但這項技術(shù)還不成熟,暫時幾乎無人使用,因此本文仍舊只關(guān)注經(jīng)典模式的實現(xiàn)

ClassPathScanningCandidateComponentProvider#scanCandidateComponents

	// 最終返回的候選組件們
	Set<BeanDefinition> candidates = new LinkedHashSet<>();


	// 得到文件系統(tǒng)的路徑,比如本例為classpath*:com/yourbatman/**/*.class
	String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
	// 從文件系統(tǒng)去加載Resource資源文件進(jìn)來
	// 這里Resource代表的是一個本地資源:存在你硬盤上的.class文件
	Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
	for (Resource resource : resources) {
		if (isCandidateComponent(metadataReader)) {
			if (isCandidateComponent(sbd)) {
				candidates.add(sbd);
			}
		}
	}

這段代碼的信息量是很大的,分解為如下兩大步:

1.通過ResourcePatternResolver從磁盤里加載到所有的 .class資源Resource[]。這里面順序信息就出現(xiàn)了,加載磁盤Resource資源的過程很復(fù)雜,總而言之它依賴于你os文件系統(tǒng)。所以關(guān)于資源的順序可簡單理解為:你磁盤文件里是啥順序它就按啥順序加載進(jìn)來

注意:不是看.java源代碼順序,也不是看你target目錄下的文件順序(該目錄是經(jīng)過了IDEA反編譯的結(jié)果,無法反應(yīng)真實順序),而是編譯后看你的磁盤上的.class文件的文件順序

2.遍歷每一個Resource資源,并不是每個資源都會成為candidates候選,它有個雙重過濾(對應(yīng)兩個isCandidateComponent()方法):

過濾一:使用TypeFilter執(zhí)行過濾,看看是否被排除;再看看是否滿足@Conditional條件

過濾二:它有兩種case能滿足條件(任意滿足一個case即可)

  • isIndependent()是獨立類(top-level類 or 靜態(tài)內(nèi)部類屬于獨立類) 并且 isConcrete()是具體的(非接口非抽象類)
  • isAbstract()是抽象類 并且 類內(nèi)存在標(biāo)注有@Lookup注解的方法

基于以上例子,磁盤中的.class文件情況如下:

看著這個順序,再結(jié)合上面的打印結(jié)果,是不是感覺得到了解釋呢?既然@Configuration類(外部類和內(nèi)部類)的順序確定了,那么@Bean就跟著定了嘍,因為畢竟配置類也得遍歷一個一個去執(zhí)行嘛(有依賴關(guān)系的case除外)。

特別說明:理論上不同的操作系統(tǒng)(如windows和Linux)它們的文件系統(tǒng)是有差異的,對文件存放的順序是可能不同的(比如$xxx內(nèi)部類可能放在后面),但現(xiàn)實狀況它們是一樣的,因此各位同學(xué)對此無需擔(dān)心跨平臺問題哈,這由JVM底層來給你保證。

什么,關(guān)于此解析步驟你想要張流程圖?好吧,你知道的,這個A哥會放到本專欄的總結(jié)篇里統(tǒng)一供以你白嫖,關(guān)注我公眾號吧~

靜態(tài)內(nèi)部類在容器內(nèi)的beanName是什么?

看到這個截圖你就懂了:在不同.java文件內(nèi),靜態(tài)內(nèi)部類是不用擔(dān)心重名問題的,這不也就是內(nèi)聚性的一種體現(xiàn)麼。

說明:beanName的生成其實和你注冊Bean的方式有關(guān),比如@Import、Scan方式是不一樣的,這里就不展開討論了,知道有這個差異就成。

進(jìn)階:Spring下普通內(nèi)部類表現(xiàn)如何?

我們知道,從內(nèi)聚性上來說,普通內(nèi)部類似乎也可以達(dá)到目的。但是相較于靜態(tài)內(nèi)部類在Spring容器內(nèi)對優(yōu)先級的問題,它的表現(xiàn)可就沒這么好嘍?;谝陨侠?,把所有的static關(guān)鍵字去掉,就是本處需要的case。

reRun測試程序,結(jié)果輸出:

A_OuterConfig init...
OuterConfig init...
Z_OuterConfig init...


A_OuterConfig InnerConfig init...
A_OuterConfig a_i_bean init...
A_OuterConfig PInnerConfig init...
A_OuterConfig a_p_bean init...
A_OuterConfig a_o_bean init...

InnerConfig init...
Daughter init...
PInnerConfig init...
son init...
Parent init...

Z_OuterConfig InnerConfig init...
Z_OuterConfig z_i_bean init...
Z_OuterConfig PInnerConfig init...
Z_OuterConfig z_p_bean init...
Z_OuterConfig z_o_bean init...

對于這個結(jié)果A哥不用再做詳盡分析了,看似比較復(fù)雜其實有了上面的分析還是比較容易理解的。主要有如下兩點需要注意:

普通內(nèi)部類它不是一個獨立的類(也就是說isIndependent() = false),所以它并不能像靜態(tài)內(nèi)部類那樣預(yù)先就被掃描進(jìn)去,如圖結(jié)果展示:

普通內(nèi)部類初始化之前,一定得先初始化外部類,所以類本身的優(yōu)先級是低于外部類的(不包含@Bean方法哦)普通內(nèi)部類屬于外部類的memberClasses,因此它會在解析當(dāng)前外部類的第一步processMemberClasses()時被解析普通內(nèi)部類的beanName和靜態(tài)內(nèi)部類是有差異的,如下截圖:

思考題:

請思考:為何使用普通內(nèi)部類得到的是這個結(jié)果呢?建議copy我的demo,自行走一遍流程,多動手總是好的

總結(jié)

本文一如既往的很干哈。寫本文的原動力是因為真的太多小伙伴在看Spring Boot自動配置類的時候,無法理解為毛它有些@Bean配置要單獨寫在一個static靜態(tài)類里面,感覺挺費事;方法前直接價格static不香嗎?通過這篇文章 + 上篇文章的解讀,相信A哥已經(jīng)給了你答案了。

到此這篇關(guān)于static關(guān)鍵字有何魔法?竟讓Spring Boot搞出那么多靜態(tài)內(nèi)部類(推薦)的文章就介紹到這了,更多相關(guān)Spring Boot靜態(tài)內(nèi)部類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于springboot集成swagger3時spring-plugin-core報錯的問題

    關(guān)于springboot集成swagger3時spring-plugin-core報錯的問題

    這篇文章主要介紹了關(guān)于springboot集成swagger3時spring-plugin-core報錯的問題,本文給大家分享解決方法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • JAVA如何把數(shù)據(jù)庫的數(shù)據(jù)處理成樹形結(jié)構(gòu)

    JAVA如何把數(shù)據(jù)庫的數(shù)據(jù)處理成樹形結(jié)構(gòu)

    本文介紹了JAVA如何把數(shù)據(jù)庫的數(shù)據(jù)處理成樹形結(jié)構(gòu),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • java實現(xiàn)微信小程序加密數(shù)據(jù)解密算法

    java實現(xiàn)微信小程序加密數(shù)據(jù)解密算法

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)微信小程序加密數(shù)據(jù)解密算法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • 使用Java操作TensorFlow的方法

    使用Java操作TensorFlow的方法

    TensorFlow是一個功能強大且廣泛使用的框架,它不斷得到改進(jìn),并最近被引入新語言包括Java和JavaScript,這篇文章主要介紹了如何使用Java操作TensorFlow,需要的朋友可以參考下
    2023-05-05
  • springcloud集成zookeeper的方法示例

    springcloud集成zookeeper的方法示例

    這篇文章主要介紹了springcloud集成zookeeper的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Java下變量大小寫駝峰、大小寫下劃線、大小寫連線轉(zhuǎn)換

    Java下變量大小寫駝峰、大小寫下劃線、大小寫連線轉(zhuǎn)換

    有時候需要處理對象屬性的getter、setter方法,或者將屬性與數(shù)據(jù)表字段進(jìn)行相互轉(zhuǎn)換,感興趣的可以了解一下
    2021-06-06
  • 深入了解JAVA數(shù)據(jù)類型與運算符

    深入了解JAVA數(shù)據(jù)類型與運算符

    這篇文章主要介紹了Java基本數(shù)據(jù)類型和運算符,結(jié)合實例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運算符、邏輯運算符等相關(guān)原理與操作技巧,需要的朋友可以參考下
    2021-07-07
  • Spring Cloud Ribbon負(fù)載均衡器處理方法

    Spring Cloud Ribbon負(fù)載均衡器處理方法

    這篇文章主要介紹了Spring Cloud Ribbon負(fù)載均衡器處理方法,看看是如何獲取服務(wù)實例,獲取以后做了哪些處理,處理后又是如何選取服務(wù)實例的,需要的朋友可以參考下
    2018-02-02
  • java實現(xiàn)五子棋大戰(zhàn)

    java實現(xiàn)五子棋大戰(zhàn)

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)五子棋大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java中BM(Boyer-Moore)算法的圖解與實現(xiàn)

    Java中BM(Boyer-Moore)算法的圖解與實現(xiàn)

    本文主要介紹了兩個大的部分,第一部分通過圖解的方式講解BM算法,第二部分則代碼實現(xiàn)一個簡易的BM算法,感興趣的小伙伴可以學(xué)習(xí)一下
    2022-05-05

最新評論