国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳談spring boot中幾種常見的依賴注入問題

詳談spring boot中幾種常見的依賴注入問題

2022-01-21 01:00一擼向北 Java教程

這篇文章主要介紹了spring boot中幾種常見的依賴注入問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

最近有空總結一下之前在使用spring boot時遇到過的幾種依賴注入時的坑,如果不了解spring內部的處理過程,使用起來總是感覺有種迷糊。

在分析場景前,需要大概了解一下spring對于bean的實例化過程是需要先注冊BeanDefinition信息然后才進行實例化,在org.springframework.context.support.AbstractApplicationContext#refresh中定義的基本的流程。部分代碼

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			// 1. 包含了BeanDefinition注冊過程
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			// Initialize message source for this context.
			initMessageSource();
			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			// Initialize other special beans in specific context subclasses.
			onRefresh();
			// Check for listener beans and register them.
			registerListeners();
			// 2. 根據BeanDefinition處理Bean實例化過程
			finishBeanFactoryInitialization(beanFactory);
			// Last step: publish corresponding event.
			finishRefresh();
		}

 

@Autowired依賴注入問題C邏輯使用先于@Autowired注解處理

之前不熟悉spring bean的實例化過程可能會遇到的坑就是使用@Autowired依賴注入的對象是null沒有注入到相應的對象里面,或者準確的來說是在我程序的某一塊邏輯代碼執行時使用到@Autowired依賴的bean,但是bean確實null。

這種場景一般就是在當時,@Autowired注解并沒有被處理,所以依賴的bean為null。

如果了解spring boot自動化裝配可以直達我們通過實現ImportBeanDefinitionRegistrar接口自定義注冊BeanDefinition信息,實現邏輯是ImportBeanDefinitionRegistrar#registerBeanDefinitions的具體實現中,這個方法的執行過程屬于

?// 1. 包含了BeanDefinition注冊過程
invokeBeanFactoryPostProcessors(beanFactory);

之前曾經遇到過在自定義的ImportBeanDefinitionRegistrar實現類中,使用@Autowired依賴某個bean,但是在使用時無法得到具體的實現對象,因為@Autowired注解的處理過程是在

?//2. 根據BeanDefinition處理Bean實例化過程
?finihBeanFactoryInitialization(beanFactory);

當程序執行ImportBeanDefinitionRegistrar#registerBeanDefinitions時,依賴的bean為null,報空指針。

測試用例

引導程序代碼

@SpringBootApplication(scanBasePackages = "garine.learn.test.auwired")
@Import(TestAutowiredRegistar.class)
public class BootstrapTestApplication3 {
  public static void main(String[] args) {
      ApplicationContext applicationContext = SpringApplication.run(BootstrapTestApplication3.class, args);
  }
}
@Component
public class TestAutowiredRegistar implements ImportBeanDefinitionRegistrar,BeanFactoryAware,InitializingBean{
  private BeanFactory beanFactory;
  @Autowired
  TestRegistarDependOn testRegistarDependOn;
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      System.out.println(TestAutowiredRegistar.class.getSimpleName() + "-----" +testRegistarDependOn);
  }
  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.beanFactory = beanFactory;
  }
  @Override
  public void afterPropertiesSet() throws Exception {
      System.out.println(TestAutowiredRegistar.class.getSimpleName() + "-----" + testRegistarDependOn);
  }
}
@Component
public class TestRegistarDependOn {
}

執行輸出:TestAutowiredRegistar-----null

**這種場景一般就是在當時,@Autowired注解并沒有被處理,所以依賴的bean為null。**如果遇到依賴注入為空時,如果確定已經定義了對應的bean,那么不妨看看代碼使用依賴bean時,到底@Autowired注解有沒有被處理。

這種場景的解決辦法就是使用BeanFactory來獲取bean,修改代碼如下。

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  System.out.println(TestAutowiredRegistar.class.getSimpleName() + "-----" +testRegistarDependOn);
  System.out.println(TestAutowiredRegistar.class.getSimpleName() + "-----" +beanFactory.getBean(TestRegistarDependOn.class));
}

再次執行輸出:

TestAutowiredRegistar-----null

TestAutowiredRegistar-----garine.learn.test.auwired.TestRegistarDependOn@7808fb9

 

BeanFactory.getBean問題CgetBean調用先于BeanDefinition信息注冊

這里就是beanFactory.getBean方法如果獲取不到bean就會調用bean的初始化過程。但是需要注意bean對應的BeanDefinition信息必須已經注冊完成。所以這種getBean的方式不是絕對安全。

一般而言ConfigurationClassParser#processConfigurationClass為入口,可以看到整個對Configclass的處理過程。對于@Configuration標注的類都是有排序的,排序在前的先進行處理。

那么會不會出現在ImportBeanDefinitionRegistrar#registerBeanDefinitions中使用beanFactory.getBean方法獲取bean而報錯的場景呢?答案是會,假如定義兩個@Configuration標注的類,a和b,a先于b處理,a通過@Import導入TestAutowiredRegistar,b中定義TestRegistarDependOn的bean實例化方法,代碼如下。

配置類:

@Configuration
@Order(1)
@Import(TestAutowiredRegistar.class)
public class FirstConfig {
  //1.先處理TestAutowiredRegistar的ImportBeanDefinitionRegistrar#registerBeanDefinitions
@Configuration
@Order(2)
public class SecondConfig {
  @Bean
  public TestRegistarDependOn testRegistarDependOn(){
      return new TestRegistarDependOn();
  }
}

TestRegistarDependOn去掉@Component注解,避免被掃描到提前注冊BeanDefinition

引導程序,去掉提前Import TestAutowiredRegistar.class

@SpringBootApplication(scanBasePackages = "garine.learn.test.auwired")
public class BootstrapTestApplication3 {
  public static void main(String[] args) {
      ApplicationContext applicationContext = SpringApplication.run(BootstrapTestApplication3.class, args);
  }
}

執行啟動程序,輸出報錯信息

A component required a bean of type ‘garine.learn.test.auwired.TestRegistarDependOn' that could not be found.

所以實際上在getBean時,如果bean的BeanDefinition并沒有注冊到Beanfactory,那么久會報出上述錯誤。

把兩個配置類的@Order順序換一下,就能處理成功,執行輸出。--------------------------然并卵。。。一樣報錯,

what?源碼里面明明有排序的,在

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

對所有的配置類都有

// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
 return Integer.compare(i1, i2);
});

經過調試發現,應該是在spring.factories中定義的Configuration類才會在這里做處理,可以稱之為最高優先級配置,對于這些配置@Order才會起作用。

那么我們自定義的@Configuration標注的類在哪里處理?經過調試,定位在@ComponentScan注解處理處,有如下代碼。

// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
 for (AnnotationAttributes componentScan : componentScans) {
    // The config class is annotated with @ComponentScan -> perform the scan immediately
    Set<BeanDefinitionHolder> scannedBeanDefinitions =
          this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    // Check the set of scanned definitions for any further config classes and parse recursively if needed
    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
       BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
       if (bdCand == null) {
          bdCand = holder.getBeanDefinition();
       }
       //判斷掃描到的是不是配置類,是的話就進行配置處理
       if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
          parse(bdCand.getBeanClassName(), holder.getBeanName());
       }
    }
 }
}

也就是說,我們一般自定義的配置順序@Order是不起作用的,全靠掃描文件得到的先后順序,所以,文件名稱是關鍵。。

這里把FirstConfig改成TFirstConfig試試,輸出

TestAutowiredRegistar-----null

TestAutowiredRegistar-----garine.learn.test.auwired.TestRegistarDependOn@189b5fb1

所以猜想通過??偨Y就是@Order對spring.factories中定義的配置類起作用,我們自定義的配置類處理順序需要文件名稱來控制。

 

在Configuration中使用@Autowired注解

通過調試都可以知道@Bean注冊實例的方式,實際代理調用@Bean修飾的方法是在

// 2. 根據BeanDefinition處理Bean實例化過程
finishBeanFactoryInitialization(beanFactory);

的過程中的,所以假如在@Bean修飾的方法中使用到@Autowired注解依賴的bean是怎么樣的場景?

spring 實例化Bean過程

先了解一下實例化bean的過程是怎么樣的。在finishBeanFactoryInitialization中,遍歷所有的BeanDefinition信息來實例化bean。遍歷的順序調試幾次后發現是按照注冊BeanDefinition信息的先后順序。所以可以有幾個簡單規則可以判斷哪個bean會先實例化。

  • 同是@ComponentScan掃描注冊的bean,按照class文件名順序,排在前面的先注冊,所以先實例化,例如 ATest類實例化先于BTest類.
  • @Conguration 配置類實例化先于其內部定義的@Bean方法執行實例化,例如Config類實例化先于其內部任意@Bean 方法實例化bean。

那么考慮,假如在@Conguration 修飾的類的@Bean方法里面使用@Autowired引入依賴,而這個依賴實例化順序要比@Conguration 修飾的類要遲,會怎么樣?

定義下面三個類,在同一個包里面順序也是如下所示:

  • ConfigInitBean
  • TestDependOnConfig
  • ZTestConfigurationDependOn

各自代碼如下:

public class ConfigInitBean {
}
@Configuration
public class TestDependOnConfig {
  @Autowired
  ZTestConfigurationDependOn zTestConfigurationDependOn;//觀察這個依賴什么時候進行初始化,斷點getBean調試
  @Bean
  public ConfigInitBean configInitBean(){
      zTestConfigurationDependOn.saySome();
      return new ConfigInitBean();
  }
}
@Component
public class ZTestConfigurationDependOn {
  public void saySome(){
      System.out.println("say some");
  }
}

在DefaultListableBeanFactory#preInstantiateSingletons方法中斷點查看beanNames的順序

詳談spring boot中幾種常見的依賴注入問題

根據spring 對@Configuration標注的類的處理過程,能夠對應的上,先掃描到TestDependOnConfig所以先注冊,ZTestConfigurationDependOn后掃描所以比TestDependOnConfig實例化要晚。ConfigInitBean是由@Bean定義的,在對配置類的處理中,都是先處理完@ComponentScan的BeanDefinition注冊,再處理@Bean、@Import導入的配置、@ImportResource導入的xml等等BeanDefinition注冊。

具體可以看有關自動裝配的文章

總結來說就是bean實例化的順序符合猜想,實際上還有一點就是每個bean實例化時,都會對其@Autowired注解的依賴進行注入,如果當時依賴沒有實例化,就根據依賴的BeanDefinition進行getBean過程所以一般情況下,我們平常使用業務代碼模型都不會出現注入為null問題。

當然,如果依賴的Beandefinition不存在,那么就會報錯:

Consider defining a bean of type ‘XXXx' in your configuration.

在這里例子中,TestDependOnConfig依賴ZTestConfigurationDependOn,但是比ZTestConfigurationDependOn實例化要早,所以會調用getBean.ZTestConfigurationDependOn,提前實例化ZTestConfigurationDependOn來注入依賴。

具體源碼在AbstractAutowireCapableBeanFactory#populateBean方法中,填充bean。

for (BeanPostProcessor bp : getBeanPostProcessors()) {
 if (bp instanceof InstantiationAwareBeanPostProcessor) {
    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    //填充屬性
    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvs == null) {
       return;
    }
 }
}

填充具體使用的實現方法是AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues來進行填充屬性,最終會調用依賴bean的getBean,從而實例化依賴的bean或者直接獲取依賴bean。

所以就算是配置類也好,普通的組件也好,都會在實例化時注入@Autowired依賴的屬性實例,如果是該實例沒有定義BeanDefinition,那么就會無法注入。

@Bean內部使用配置類@Autowired注解引入依賴

了解完上面的過程,可以知道,@Bean方法是在finishBeanFactoryInitialization過程實例化對應的bean時才會被代理調用,并且順序比對應配置類要后,這時對應配置類早已經實例化完畢,依賴屬性也已經注入,可以放心在@Bean方法內部使用。

還有一種情況是,假如@Bean方法被提前調用,例如@Bean的實例被另一個比@Bean所在配置類還要早實例化的組件中引入,那么此時@Bean所在配置類還沒實例化,這樣調用會出錯嗎?答案是不會,因為歸根到底,@Bean方法的調用都是代理方式,程序還是需要先實例化一個@Bean所在配置類的實例,才能進行@Bean方法的調用,從而實例化一個@Bean方法的bean。

 

InitializingBean#afterPropertiesSet內部使用依賴

了解到上面的知識,推測一下,在InitializingBean#afterPropertiesSet里面使用@Autowired依賴進行邏輯處理是否可以?看如下InitializingBean#afterPropertiesSet的調用時機。

	try {
	//填充依賴屬性
		populateBean(beanName, mbd, instanceWrapper);
		//最終調用到InitializingBean#afterPropertiesSet方法
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}

所以,InitializingBean#afterPropertiesSet是在填充玩依賴屬性之后調用的,因此可以使用依賴的bean進行一些邏輯操作的。

 

總結

1、所以總結來說就是,我們的代碼邏輯在

// 根據BeanDefinition處理Bean實例化過程
finishBeanFactoryInitialization(beanFactory);

過程之前都沒有使用的@Autowired依賴bean的話,那是沒問題的,因為@Autowired注解處理都是在finishBeanFactoryInitialization()也就是bean實例化時才會進行處理。如果使用了,那就是空指針;

2、在@Bean方法內部使用@Autowired注解的依賴,只要設計好程序也是可以的的,只要依賴的BeanDefinition已經注冊過,配置類實例化時就能主動發起依賴的實例化過程,然后注入依賴,不會出現空指針。

而@Bean方法是在finishBeanFactoryInitialization過程實例化對應的bean時才會被代理調用,并且順序比對應配置類要后,這時對應配置類早已經實例化完畢,依賴屬性也已經注入,可以放心在@Bean方法內部使用。

所以這里@Bean方法實例化bean時如果使用到@Autowired依賴的bean時,就對配置類的實例有很強的依賴性,這種依賴順序spring都幫我們保證先實例化配置類,再調用@Bean方法。

3、InitializingBean#afterPropertiesSet內部使用也是沒問題,原理如上。所以只要理解在使用到@Autowired的依賴時,到底在哪個時機,就能分析清楚是不是適合使用。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/qq_20597727/article/details/82833843

延伸 · 閱讀

精彩推薦
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 一本久久综合亚洲鲁鲁五月天 | 伊人二区| 午夜精品久久久久久久白皮肤 | 精品视频在线播放 | 欧美中文字幕一区二区三区亚洲 | 欧美视频三区 | 中文字幕亚洲欧美 | 欧美日本在线 | 国产精品视频免费观看 | 久久久久久网站 | 91精品久久久久久久久 | 中文字幕一区二区三区四区五区 | 欧美日韩久久精品 | 国产噜噜噜噜噜久久久久久久久 | 午夜成年人 | 超碰c| 综合久久综合久久 | 一级片导航 | 免费成人高清在线视频 | 色悠悠视频 | 亚洲精品在线观看av | 99视频精品在线 | 福利视频在线 | 超碰官网 | 亚洲精品视 | 91精品国产综合久久香蕉922 | 精品国产乱码久久久久久丨区2区 | 日韩精品免费在线视频 | 亚洲夜幕久久日韩精品一区 | 91在线影视 | 国产精品久久久精品 | 日韩一区二区在线播放 | 国产精品毛片在线 | 国产偷亚洲偷欧美偷精品 | 亚洲在线精品视频 | 激情毛片 | 亚洲人人 | 中文字幕av一区二区三区免费看 | 国产99久久精品 | 91夜色 | 午夜爱|