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

Spring?IOC中的組件掃描

 更新時(shí)間:2024年04月30日 09:55:41   作者:紙杯沒(méi)茶  
通過(guò)自動(dòng)掃描,Spring?會(huì)自動(dòng)從掃描指定的包及其子包下的所有類(lèi),并根據(jù)類(lèi)上的特定注解將該類(lèi)裝配到容器中,而無(wú)需在?XML?配置文件或?Java?配置類(lèi)中逐一聲明每一個(gè)?Bean,這篇文章主要介紹了Spring?IOC中的組件掃描,需要的朋友可以參考下

版本 Spring Framework 6.0.9?

1. 前言

通過(guò)自動(dòng)掃描,Spring 會(huì)自動(dòng)從掃描指定的包及其子包下的所有類(lèi),并根據(jù)類(lèi)上的特定注解將該類(lèi)裝配到容器中,而無(wú)需在 XML 配置文件或 Java 配置類(lèi)中逐一聲明每一個(gè) Bean。

支持的注解
Spring 支持一系列注解,用于標(biāo)記哪些類(lèi)應(yīng)被自動(dòng)掃描并作為 Bean 管理。這些注解通常包含:

  • @Component:基礎(chǔ)注解,標(biāo)記一個(gè)類(lèi)作為 Spring 組件。所有其他特殊用途的組件注解都繼承自此注解。
  • @Repository:用于標(biāo)注 DAO(Data Access Object)類(lèi),除了具備 @Component 的功能外,還為數(shù)據(jù)訪問(wèn)異常提供了特殊的翻譯機(jī)制。
  • @Service:用于標(biāo)注業(yè)務(wù)層(Service)類(lèi),強(qiáng)調(diào)這是一個(gè)業(yè)務(wù)相關(guān)的組件。
  • @Controller:用于標(biāo)注 MVC 架構(gòu)中的控制器類(lèi),通常在 Spring MVC 中使用,與 Spring Web 相關(guān)的請(qǐng)求處理邏輯相關(guān)聯(lián)。
  • @Configuration:用于標(biāo)注配置類(lèi),這類(lèi)類(lèi)通常包含 @Bean 方法,用于定義其他 Bean。
  • @RestController:結(jié)合了 @Controller 和 @ResponseBody,適用于構(gòu)建 RESTful Web 服務(wù)的控制器。

例子相關(guān)實(shí)體類(lèi)

控制層(例子中沒(méi)引入spring-web包,注解先使用@Component替代)

服務(wù)層

數(shù)據(jù)訪問(wèn)層

2. 基于xml

2.1 使用

在 Spring 的XML配置文件中,通過(guò) <context:component-scan> 標(biāo)簽開(kāi)啟自動(dòng)掃描功能。我們可以配置 base-package 指定掃描路徑。

輸出信息說(shuō)明每個(gè)實(shí)體類(lèi)都已加載到容器。

<context:component-scan> 其他配置元素:

  • resource-pattern:可選屬性,用于指定在掃描包路徑下應(yīng)匹配的資源模式。默認(rèn)值為 “**/*.class”,即掃描所有 .class 文件??梢愿鶕?jù)需要修改此模式,例如,僅掃描特定目錄下的類(lèi)。
  • use-default-filters:布爾屬性,默認(rèn)值為 true。決定是否啟用默認(rèn)的過(guò)濾規(guī)則,即查找?guī)в?@Component、@Repository、@Service、@Controller、@Configuration 等注解的類(lèi)。如果設(shè)置為 false,則需要手動(dòng)配置 和 來(lái)指定掃描規(guī)則。
  • scope-resolver:指定一個(gè)自定義的 ScopeMetadataResolver 實(shí)現(xiàn)類(lèi),用于確定掃描到的 Bean 的作用域。默認(rèn)情況下,Spring 使用 AnnotationScopeMetadataResolver,根據(jù)類(lèi)上的 @Scope 注解來(lái)確定作用域。
  • scoped-proxy:指定是否為掃描到的帶有作用域注解的 Bean 創(chuàng)建代理??赡艿闹蛋ǎ?ul>
  • no(默認(rèn)):不創(chuàng)建代理。
  • interfaces:為實(shí)現(xiàn)了接口的 Bean 創(chuàng)建 JDK 動(dòng)態(tài)代理。
  • targetClass:為未實(shí)現(xiàn)接口或需要保留原始類(lèi)類(lèi)型的 Bean 創(chuàng)建 CGLIB 代理。
  • name-generator:指定一個(gè)自定義的 BeanNameGenerator 實(shí)現(xiàn)類(lèi),用于生成掃描到的 Bean 的名稱(chēng)。默認(rèn)使用 AnnotationBeanNameGenerator,根據(jù)類(lèi)上的 @Component 等注解的 value 屬性或類(lèi)名生成 Bean 名稱(chēng)。
  • include-filter 和 exclude-filter:這兩個(gè)元素屬于子標(biāo)簽,用于指定更細(xì)粒度的掃描規(guī)則。可以按注解類(lèi)型、注解屬性、全類(lèi)名模式等進(jìn)行包含或排除。
  • 2.2 掃描原理

    • 當(dāng)我們使用ClassPathXmlApplicationContext作為IOC容器,在refresh方法的obtainFreshBeanFactory階段會(huì)創(chuàng)建一個(gè)bean工廠DefaultListableBeanFactory。
    • 當(dāng)前上下文調(diào)用loadBeanDefinitions方法,根據(jù)容器的配置文件加載bean定義。
    • 配置文件中通過(guò) <context:component-scan> 標(biāo)簽開(kāi)啟自動(dòng)掃描功能,屬于其他命名空間,托管給 ComponentScanBeanDefinitionParser處理。
    • ComponentScanBeanDefinitionParser#parse方法中創(chuàng)建一個(gè)ClassPathBeanDefinitionScanner對(duì)象,調(diào)用其scan方法掃描組件并加載到bean工廠中。

    其他命名空間

    Spring框架除了核心的XML命名空間外,還提供了多個(gè)擴(kuò)展命名空間(除beans以外的命名空間),以支持特定的功能模塊和簡(jiǎn)化配置。

    • http://www.springframework.org/schema/beans:核心命名空間,這是最基礎(chǔ)且最常用的命名空間,用于定義bean、設(shè)置屬性、注入依賴(lài)、配置構(gòu)造函數(shù)參數(shù)、初始化方法、銷(xiāo)毀方法、自動(dòng)裝配等基本IoC容器功能。http://www.springframework.org/schema/context:
    • context命名空間,提供了對(duì)Java配置類(lèi)、自動(dòng)掃描、注解驅(qū)動(dòng)的bean定義、屬性占位符替換、資源加載等上下文相關(guān)的高級(jí)功能的支持。通過(guò)此命名空間,可以簡(jiǎn)化基于注解的配置,如@Component、@Autowired等。

    在實(shí)例化XmlReaderContext時(shí),DefaultBeanDefinitionDocumentReader會(huì)創(chuàng)建一個(gè)命名空間解析器DefaultNamespaceHandlerResolver,緩存到XmlReaderContext,當(dāng)Reader解析配置文件時(shí)發(fā)現(xiàn)存在其他命名空間時(shí),通過(guò)DefaultNamespaceHandlerResolver加載 META-INF/spring.handlers 路徑下的其他命名空間處理器,獲取對(duì)應(yīng)的處理器處理。

    XmlReaderContext 是Spring框架中用于處理XML配置文件解析過(guò)程中的上下文對(duì)象,為DefaultBeanDefinitionDocumentReader在解析和處理XML配置文件時(shí)提供了必要的環(huán)境支持。

    context命名空間處理器(ComponentScanBeanDefinitionParser)

    • 獲取基礎(chǔ)包名basePackages。
    • 創(chuàng)建類(lèi)路徑 Bean 定義掃描程序ClassPathBeanDefinitionScanner。
    • 遍歷掃描包名,查找被@Component及其派生注解修飾的類(lèi)。將它們封裝為BeanDefinitionHolder對(duì)象。
    • 往bean工廠加載特定注解后置處理器的bean定義(internalConfigurationAnnotationProcessor、internalAutowiredAnnotationProcess、internalCommonAnnotationProcessor、internalEventListenerProcessor、internalEventListenerFactory),觸發(fā)組件注冊(cè)事件.

    獲取基礎(chǔ)包名basePackages

    • element.getAttribute(BASE_PACKAGE_ATTRIBUTE)方法從對(duì)象中獲取base-package的屬性值。
    • parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage)方法解析占位符,獲取解析值。
    • StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)方法將基礎(chǔ)包名按逗號(hào)、分號(hào)或空格等常見(jiàn)分隔符拆分為一個(gè)字符串?dāng)?shù)組basePackages。

    創(chuàng)建組件掃描器

    ComponentScanBeanDefinitionParser#configureScanner方法通過(guò)對(duì)XML元素element的各項(xiàng)屬性(use-default-filters、resource-pattern、name-generator、scope-resolver、scoped-proxy)和子元素(include-filter、exclude-filter)進(jìn)行解析,創(chuàng)建一個(gè)定制化的掃描器ClassPathBeanDefinitionScanner。

    另外通過(guò)ClassPathBeanDefinitionScanner構(gòu)造函數(shù)實(shí)例化時(shí),會(huì)創(chuàng)建一個(gè)Component類(lèi)型的注解類(lèi)型過(guò)濾器AnnotationTypeFilter添加到includeFilters屬性中,用于將類(lèi)與@Component進(jìn)行匹配。

    掃描組件

    ClassPathBeanDefinitionScanner#doScan掃描指定的basePackages包及其子包,查找并處理符合要求的bean定義,最終將它們封裝為BeanDefinitionHolder對(duì)象并返回,bean定義類(lèi)型是ScannedGenericBeanDefinition。

    掃描核心方法ClassPathScanningCandidateComponentProvider#findCandidateComponents用于在指定的basePackage包及其子包下查找符合要求的候選bean組件(BeanDefinition),支持索引查找(效率更高)或按傳統(tǒng)方式查找(獲取指定包下所有class對(duì)象篩選)。

    我們只看傳統(tǒng)方式查找方式,ClassPathScanningCandidateComponentProvider#scanCandidateComponents方法用于在給定的basePackage及其子包下通過(guò)類(lèi)路徑掃描機(jī)制查找候選bean組件(BeanDefinition).

    ClassPathScanningCandidateComponentProvider#isCandidateComponent方法用于判斷給定的MetadataReader所代表的類(lèi)是否滿(mǎn)足候選bean組件的條件,在不添加其他排除過(guò)濾器、包含過(guò)濾器或配置,上面創(chuàng)建組件掃描器過(guò)程中,已知添加了一個(gè)AnnotationTypeFilter包含過(guò)濾器,在此處使用,匹配被@Component注解的類(lèi)。

    觸發(fā)組件注冊(cè)事件

    ComponentScanBeanDefinitionParser#registerComponents負(fù)責(zé)將一組BeanDefinition注冊(cè)到Spring IoC容器中,并處理與之相關(guān)的XML元素及注解配置處理器。實(shí)際上做了兩件事

    往bean工廠加載特定注解后置處理器的bean定義觸發(fā)組件注冊(cè)事件,一般情況下是EmptyReaderEventListener,空方法。

    3. 全注解開(kāi)發(fā)

    AnnotationConfigApplicationContext 是 Spring Framework 中的一個(gè)核心類(lèi),用于創(chuàng)建和管理基于 Java 注解的 Spring 應(yīng)用程序上下文。AnnotationConfigApplicationContext 有兩種開(kāi)啟組件掃描的方式

    • 直接指定包路徑
    • 通過(guò)@ComponentScan注解

    3.1 通過(guò)指定包路徑開(kāi)啟掃描

    使用

    掃描原理

    接受一個(gè)字符串?dāng)?shù)組參數(shù) basePackages的有參構(gòu)造邏輯分成三部分:

    • this(); 調(diào)用無(wú)參構(gòu)造函數(shù)
    • scan(basePackages); 執(zhí)行類(lèi)路徑掃描
    • refresh(); 啟動(dòng)應(yīng)用上下文

    無(wú)參構(gòu)造函數(shù)實(shí)例化組件掃描器 ClassPathBeanDefinitionScanner,用于在類(lèi)路徑中掃描并注冊(cè)基于注解的 Bean 定義(如 @Component、@Service、@Repository、@Controller 等)。

    調(diào)用ClassPathBeanDefinitionScanner#scan方法掃描組件,與基于xml掃描邏輯一樣,調(diào)用的是ClassPathBeanDefinitionScanner#scan方法,所以bean定義創(chuàng)建的類(lèi)型也是ScannedGenericBeanDefinition類(lèi)。

    3.2 通過(guò)Java配置類(lèi)開(kāi)啟掃描

    使用

    c

    掃描原理

    接受一個(gè)類(lèi)型為 Class<?> 的可變參數(shù)數(shù)組 componentClasses的有參構(gòu)造邏輯分成三部分:

    • this(); 調(diào)用無(wú)參構(gòu)造函數(shù)
    • register(componentClasses); 注冊(cè)指定的組件
    • refresh(); 啟動(dòng)應(yīng)用上下文

    第一部分雖然與接受字符串的有參構(gòu)造(指定掃描路徑)一樣調(diào)用了無(wú)參構(gòu)造,但我們這里只關(guān)注 帶注釋的 Bean 定義讀取器AnnotatedBeanDefinitionReader,而不是類(lèi)路徑 Bean 定義掃描程器ClassPathBeanDefinitionScanner。

    實(shí)例化AnnotatedBeanDefinitionReader過(guò)程中,會(huì)調(diào)用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)方法,往bean工廠注冊(cè)一個(gè)bean定義后置處理器ConfigurationClassPostProcessor。

    第二部分注冊(cè)Java配置類(lèi)ScanConfig,實(shí)例化并初始化bean定義類(lèi)AnnotatedGenericBeanDefinition,并加載到bean工廠中。

    第三部分在refresh方法invokeBeanFactoryPostProcessors階段,會(huì)調(diào)用在第一部分注冊(cè)的ConfigurationClassPostProcessor(在當(dāng)前階段會(huì)調(diào)用getBean方法實(shí)例化)后置處理。

    ConfigurationClassPostProcessor 是 Spring 框架中一個(gè)重要的 BeanDefinitionRegistryPostProcessor 實(shí)現(xiàn),主要負(fù)責(zé)處理帶有 @Configuration 注解的類(lèi)以及它們內(nèi)部定義的 @Bean 方法和其他相關(guān)注解。postProcessBeanDefinitionRegistry方法會(huì)篩選出有效的@configuration類(lèi),調(diào)用配置類(lèi)解析器ConfigurationClassParser的parse方法解析。

    從第二部分知道,ScanConfig生成的bean定義類(lèi)是AnnotatedGenericBeanDefinition,是AnnotatedBeanDefinition的子類(lèi)。debug源碼到處理@ComponentScan注解(this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());),說(shuō)明ConfigurationClassParser處理邏輯委托給componentScanParser,調(diào)用其parse方法處理。

    這里的this.componentScanParser是ComponentScanAnnotationParser類(lèi),在調(diào)用ConfigurationClassParser構(gòu)造函數(shù)實(shí)例化時(shí)創(chuàng)建。

    ComponentScanAnnotationParser#parse方法中的邏輯就很熟悉了,可以對(duì)標(biāo)context命名空間處理各個(gè)屬性。在方法中是新建了一個(gè)ClassPathBeanDefinitionScanner類(lèi),而不是使用AnnotationConfigApplicationContext無(wú)參構(gòu)造中實(shí)例化的ClassPathBeanDefinitionScanner,最后執(zhí)行doscan方法掃描組件。

    總結(jié)

    總結(jié)一下通過(guò)@ComponentScan開(kāi)啟掃描流程.

    • this();
      • 在實(shí)例化AnnotatedBeanDefinitionReader過(guò)程中,往bean工廠加載了ConfigurationClassPostProcessor的bean定義
    • register(componentClasses);
      • 往bean工廠加載了包含@Configuration、@ComponentScan注解的ScanConfig配置類(lèi) 的bean定義。
    • refresh();
      • 在invokeBeanFactoryPostProcessors階段實(shí)例化了ConfigurationClassPostProcessor,并調(diào)用其postProcessBeanDefinitionRegistry方法處理@Configuration配置類(lèi)。
      • ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法中,獲取并篩選有效的 @Configuration類(lèi),實(shí)例化了配置類(lèi)解析器ConfigurationClassParser,調(diào)用其parse方法,解析配置類(lèi)(ScanConfig.class)。
      • ConfigurationClassParser#parse包含處理配置類(lèi)中各種注解,其中就包含@ComponentScan。但解析@ComponentScan,委托給了ComponentScanAnnotationParser類(lèi),調(diào)用其parse方法.
      • ComponentScanAnnotationParser#parse實(shí)例化了類(lèi)路徑 Bean 定義掃描器ClassPathBeanDefinitionScanner,ClassPathBeanDefinitionScanner加載掃描配置后調(diào)用doScan掃描組件。

    相關(guān)文章

    最新評(píng)論