面試官說:Spring這幾個問題你回答下,月薪3萬,下週來上班!


面試官說:Spring這幾個問題你回答下,月薪3萬,下週來上班!


靈魂拷問

  1. @ComponentScan註解是做什麼的?
  2. basePackages的方式和basePackageClasses的方式有什麼區別?你建議用哪個?為什麼?
  3. useDefaultFilters有什麼用?
  4. 常見的過濾器有哪些類型?說說你知道的幾個
  5. @ComponentScan是在哪個類中處理的?說一下大概的解析過程?

這些問題如果都ok,恭喜你,太優秀了,不知道沒關係,一起來看看。

背景介紹

到目前為止,介紹了2種註冊bean的方式:

  1. xml中bean元素的方式
  2. @Bean註解標註方法的方式

通常情況下,項目中大部分類都需要交給spring去管理,按照上面這2種方式,代碼量還是挺大的。

為了更方便bean的註冊,Spring提供了批量的方式註冊bean,方便大量bean批量註冊,spring中的@ComponentScan就是幹這個事情的。

@ComponentScan

@ComponentScan用於批量註冊bean。

這個註解會讓spring去掃描某些包及其子包中所有的類,然後將滿足一定條件的類作為bean註冊到spring容器容器中。

具體需要掃描哪些包?以及這些包中的類滿足什麼條件時被註冊到容器中,這些都可以通過這個註解中的參數動態配置。

先來看一下這個註解的定義:

<code>@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) //@1
public @interface ComponentScan {

    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class>[] basePackageClasses() default {};

    Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/*.class";

    boolean useDefaultFilters() default true;

    Filter[] includeFilters() default {};

    Filter[] excludeFilters() default {};

    boolean lazyInit() default false;
}/<code>

定義上可以看出此註解可以用在任何類型上面,不過我們通常將其用在類上面。

常用參數:

value:指定需要掃描的包,如:com.javacode2018

basePackages:作用同value;value和basePackages不能同時存在設置,可二選一

basePackageClasses:指定一些類,spring容器會掃描這些類所在的包及其子包中的類

nameGenerator:自定義bean名稱生成器

resourcePattern:需要掃描包中的那些資源,默認是:**/*.class,即會掃描指定包中所有的class文件

useDefaultFilters:對掃描的類是否啟用默認過濾器,默認為true

includeFilters:過濾器:用來配置被掃描出來的那些類會被作為組件註冊到容器中

excludeFilters:過濾器,和includeFilters作用剛好相反,用來對掃描的類進行排除的,被排除的類不會被註冊到容器中

lazyInit:是否延遲初始化被註冊的bean

@1:@Repeatable(ComponentScans.class),這個註解可以同時使用多個。

@ComponentScan工作的過程:

  1. Spring會掃描指定的包,且會遞歸下面子包,得到一批類的數組
  2. 然後這些類會經過上面的各種過濾器,最後剩下的類會被註冊到容器中

所以玩這個註解,主要關注2個問題:

第一個:需要掃描哪些包?通過value、backPackages、basePackageClasses這3個參數來控制

第二:過濾器有哪些?通過useDefaultFilters、includeFilters、excludeFilters這3個參數來控制過濾器

這兩個問題搞清楚了,就可以確定哪些類會被註冊到容器中。

默認情況下,任何參數都不設置的情況下,此時,會將@ComponentScan修飾的類所在的包作為掃描包;默認情況下useDefaultFilters為true,這個為true的時候,spring容器內部會使用默認過濾器,規則是:凡是類上有@Repository、@Service、@Controller、@Component這幾個註解中的任何一個的,那麼這個類就會被作為bean註冊到spring容器中,所以默認情況下,只需在類上加上這幾個註解中的任何一個,這些類就會自動交給spring容器來管理了。

@Component、@Repository、@Service、@Controller

這幾個註解都是spring提供的。

先說一下@Component這個註解,看一下其定義:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}/<code>

從定義中可以看出,這個註解可以用在任何類型上面。

通常情況下將這個註解用在類上面,標註這個類為一個組件,默認情況下,被掃描的時候會被作為bean註冊到容器中。

value參數:被註冊為bean的時候,用來指定bean的名稱,如果不指定,默認為類名首字母小寫。如:類UserService對應的beanname為userService

再來看看@Repository源碼如下:

<code>@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

    @AliasFor(annotation = Component.class)
    String value() default "";

}/<code>

Repository上面有@Component註解。

value參數上面有@AliasFor(annotation = Component.class),設置value參數的時候,也相當於給@Component註解中的value設置值。

其他兩個註解@Service、@Controller源碼和@Repository源碼類似。

這4個註解本質上是沒有任何差別,都可以用在類上面,表示這個類被spring容器掃描的時候,可以作為一個bean組件註冊到spring容器中。

spring容器中對這4個註解的解析並沒有進行區分,統一採用@Component註解的方式進行解析,所以這幾個註解之間可以相互替換。

spring提供這4個註解,是為了讓系統更清晰,通常情況下,系統是分層結構的,多數系統一般分為controller層、service層、dao層。

@controller通常用來標註controller層組件,@service註解標註service層的組件,@Repository標註dao層的組件,這樣可以讓整個系統的結構更清晰,當看到這些註解的時候,會和清晰的知道屬於哪個層,對於spring來說,將這3個註解替換成@Component註解,對系統沒有任何影響,產生的效果是一樣的。

下面通過案例來感受@ComponentScan各種用法。

案例1:任何參數未設置

UserController

<code>package com.javacode2018.lesson001.demo22.test1.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {
}/<code>

UserService

<code>package com.javacode2018.lesson001.demo22.test1.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
}/<code>

UserDao

<code>package com.javacode2018.lesson001.demo22.test1.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
}/<code>

UserModel

<code>package com.javacode2018.lesson001.demo22.test1;

import org.springframework.stereotype.Component;

@Component
public class UserModel {
}/<code>

上面幾個類中,分別使用了4種註解。

@CompontentScan修飾的類

<code>package com.javacode2018.lesson001.demo22.test1;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class ScanBean1 {
}/<code>

上面幾個類的結構圖

fsdfds

測試用例

<code>package com.javacode2018.lesson001.demo22;

import com.javacode2018.lesson001.demo22.test1.ScanBean1;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ComponentScanTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean1.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(beanName + "->" + context.getBean(beanName));
        }
    }
}/<code>

@1:使用AnnotationConfigApplicationContext作為ioc容器,將ScanBean作為參數傳入。

默認會掃描ScanBean類所在的包中的所有類,類上有@Component、@Repository、@Service、@Controller任何一個註解的都會被註冊到容器中

運行輸出

部分輸出如下:

<code>userModel->com.javacode2018.lesson001.demo22.test1.UserModel@595b007d
userController->com.javacode2018.lesson001.demo22.test1.controller.UserController@72d1ad2e
userDao->com.javacode2018.lesson001.demo22.test1.dao.UserDao@2d7275fc
userService->com.javacode2018.lesson001.demo22.test1.service.UserService@399f45b1/<code>

注意最後4行這幾個bean,都被註冊成功了。

案例2:指定需要掃描的包

指定需要掃毛哪些包,可以通過value或者basePackage來配置,二者選其一,都配置運行會報錯,下面我們通過value來配置。

ScanBean2

<code>package com.javacode2018.lesson001.demo22.test2;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan({
        "com.javacode2018.lesson001.demo22.test1.controller",
        "com.javacode2018.lesson001.demo22.test1.service"
})
public class ScanBean2 {
}/<code>

上面指定了2需要掃描的包,這兩個包中有2個類。

測試用例

ComponentScanTest中新增個方法

<code>@Test
public void test2() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean2.class);
    for (String beanName : context.getBeanDefinitionNames()) {
        System.out.println(beanName + "->" + context.getBean(beanName));
    }
}/<code>

運行輸出

截取了關鍵幾行如下:

<code>userController->com.javacode2018.lesson001.demo22.test1.controller.UserController@dd8ba08
userService->com.javacode2018.lesson001.demo22.test1.service.UserService@245b4bdc/<code>

可以看出只有controller包和service包中的2個類被註冊為bean了。

注意

指定包名的方式掃描存在的一個隱患,若包被重名了,會導致掃描會失效,一般情況下面我們使用basePackageClasses的方式來指定需要掃描的包,這個參數可以指定一些類型,默認會掃描這些類所在的包及其子包中所有的類,這種方式可以有效避免這種問題。

下面來看一下basePackageClasses的方式。

案例:basePackageClasses指定掃描範圍

我們可以在需要掃描的包中定義一個標記的接口或者類,他們的唯一的作用是作為basePackageClasses的值,其他沒有任何用途。

下面我們定義這樣一個接口

<code>package com.javacode2018.lesson001.demo22.test6.beans;

public interface ScanClass {
}/<code>

再來定義2個類,用@Component註解標記

<code>package com.javacode2018.lesson001.demo22.test6.beans;

import org.springframework.stereotype.Component;

@Component
public class Service1 {
}/<code>
<code>package com.javacode2018.lesson001.demo22.test6.beans;

import org.springframework.stereotype.Component;

@Component
public class Service2 {
}/<code>

來一個@CompontentScan標記的類

<code>package com.javacode2018.lesson001.demo22.test6;

import com.javacode2018.lesson001.demo22.test6.beans.ScanClass;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackageClasses = ScanClass.class)
public class ScanBean6 {
}/<code>

測試用例

ComponentScanTest中新增個方法

<code>@Test
public void test6() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean6.class);
    for (String beanName : context.getBeanDefinitionNames()) {
        System.out.println(beanName + "->" + context.getBean(beanName));
    }
}/<code>

運行輸出

<code>service1->com.javacode2018.lesson001.demo22.test6.beans.Service1@79924b
service2->com.javacode2018.lesson001.demo22.test6.beans.Service2@7b9a4292/<code>

includeFilters的使用

用法

再來看一下includeFilters這個參數的定義:

<code>Filter[] includeFilters() default {};/<code>

是一個Filter類型的數組,多個Filter之間為或者關係,即滿足任意一個就可以了,看一下Filter的代碼:

<code>@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {

    FilterType type() default FilterType.ANNOTATION;

    @AliasFor("classes")
    Class>[] value() default {};

    @AliasFor("value")
    Class>[] classes() default {};

    String[] pattern() default {};

}/<code>

可以看出Filter也是一個註解,參數:

type:過濾器的類型,是個枚舉類型,5種類型

ANNOTATION:通過註解的方式來篩選候選者,即判斷候選者是否有指定的註解

ASSIGNABLE_TYPE:通過指定的類型來篩選候選者,即判斷候選者是否是指定的類型

ASPECTJ:ASPECTJ表達式方式,即判斷候選者是否匹配ASPECTJ表達式

REGEX:正則表達式方式,即判斷候選者的完整名稱是否和正則表達式匹配

CUSTOM:用戶自定義過濾器來篩選候選者,對候選者的篩選交給用戶自己來判斷

value:和參數classes效果一樣,二選一

classes:3種情況如下

當type=FilterType.ANNOTATION時,通過classes參數可以指定一些註解,用來判斷被掃描的類上是否有classes參數指定的註解

當type=FilterType.ASSIGNABLE_TYPE時,通過classes參數可以指定一些類型,用來判斷被掃描的類是否是classes參數指定的類型

當type=FilterType.CUSTOM時,表示這個過濾器是用戶自定義的,classes參數就是用來指定用戶自定義的過濾器,自定義的過濾器需要實現org.springframework.core.type.filter.TypeFilter接口

pattern:2種情況如下

當type=FilterType.ASPECTJ時,通過pattern來指定需要匹配的ASPECTJ表達式的值

當type=FilterType.REGEX時,通過pattern來自正則表達式的值

案例:掃描包含註解的類

需求

我們自定義一個註解,讓標註有這些註解的類自動註冊到容器中

代碼實現

下面的代碼都在com.javacode2018.lesson001.demo22.test3包中。

定義一個註解
<code>package com.javacode2018.lesson001.demo22.test3;

import java.lang.annotation.*;

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
}/<code>
創建一個類,使用這個註解標註
<code>package com.javacode2018.lesson001.demo22.test3;

@MyBean
public class Service1 {
}/<code>
再來一個類,使用spring中的`@Compontent`標註
<code>package com.javacode2018.lesson001.demo22.test3;

import org.springframework.stereotype.Component;

@Component
public class Service2 {
}/<code>
再來一個類,使用@CompontentScan標註
<code>package com.javacode2018.lesson001.demo22.test3;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@ComponentScan(includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyBean.class)
})
public class ScanBean3 {
}/<code>

上面指定了Filter的type為註解的類型,只要類上面有@MyBean註解的,都會被作為bean註冊到容器中。

測試用例

ComponentScanTest中新增個測試用例

<code>@Test
public void test3() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean3.class);
    for (String beanName : context.getBeanDefinitionNames()) {
        System.out.println(beanName + "->" + context.getBean(beanName));
    }
}/<code>
運行輸出,截取了主要的幾行
<code>service1->com.javacode2018.lesson001.demo22.test3.Service1@6b81ce95
service2->com.javacode2018.lesson001.demo22.test3.Service2@2a798d51/<code>

Service1上標註了@MyBean註解,被註冊到容器了,但是Service2上沒有標註@MyBean啊,怎麼也被註冊到容器了?

原因:Service2上標註了@Compontent註解,而@CompontentScan註解中的useDefaultFilters默認是true,表示也會啟用默認的過濾器,而默認的過濾器會將標註有@Component、@Repository、@Service、@Controller這幾個註解的類也註冊到容器中

如果我們只想將標註有@MyBean註解的bean註冊到容器,需要將默認過濾器關閉,即:useDefaultFilters=false,我們修改一下ScanBean3的代碼如下:

<code>@ComponentScan(
        useDefaultFilters = false, //不啟用默認過濾器
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyBean.class)
        })
public class ScanBean3 {
}/<code>

再次運行test3輸出:

<code>service1->com.javacode2018.lesson001.demo22.test3.Service1@294425a7/<code>

擴展:自定義註解支持定義bean名稱

上面的自定義的@MyBean註解,是無法指定bean的名稱的,可以對這個註解做一下改造,加個value參數來指定bean的名稱,如下:

<code>@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component //@1
public @interface MyBean {
    @AliasFor(annotation = Component.class) //@2
    String value() default ""; //@3
}/<code>

重點在於@1和@2這2個地方的代碼,通過上面的參數可以間接給@Component註解中的value設置值。

這塊用到了@AliasFor註解,對這塊不瞭解的,可以去看一下:java註解詳解及spring對註解的增強

修改一下Service1的代碼:

<code>@MyBean("service1Bean")
public class Service1 {
}/<code>

運行test3用例輸出:

<code>service1Bean->com.javacode2018.lesson001.demo22.test3.Service1@222545dc/<code>

此時bean名稱就變成了service1Bean。

案例:包含指定類型的類

下面的代碼都位於com.javacode2018.lesson001.demo22.test4包中。

來個接口

<code>package com.javacode2018.lesson001.demo22.test4;

public interface IService {
}/<code>

讓spring來進行掃描,類型滿足IService的都將其註冊到容器中。

來2個實現類

<code>package com.javacode2018.lesson001.demo22.test4;

public class Service1 implements IService {
}/<code>
<code>package com.javacode2018.lesson001.demo22.test4;

public class Service2 implements IService {
}/<code>

來一個@CompontentScan標註的類

<code>package com.javacode2018.lesson001.demo22.test4;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@ComponentScan(
        useDefaultFilters = false, //不啟用默認過濾器
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = IService.class) //@1
        })
public class ScanBean4 {
}/<code>

@1:被掃描的類滿足IService.class.isAssignableFrom(被掃描的類)條件的都會被註冊到spring容器中

來個測試用例

ComponentScanTest中新增個測試用例

<code>@Test
public void test4() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean4.class);
    for (String beanName : context.getBeanDefinitionNames()) {
        System.out.println(beanName + "->" + context.getBean(beanName));
    }
}/<code>

運行輸出

<code>service1->com.javacode2018.lesson001.demo22.test4.Service1@6379eb
service2->com.javacode2018.lesson001.demo22.test4.Service2@294425a7/<code>

自定義Filter

用法

有時候我們需要用到自定義的過濾器,使用自定義過濾器的步驟:

<code>1.設置@Filter中type的類型為:FilterType.CUSTOM
2.自定義過濾器類,需要實現接口:org.springframework.core.type.filter.TypeFilter
3.設置@Filter中的classses為自定義的過濾器類型/<code>

來看一下TypeFilter這個接口的定義:

<code>@FunctionalInterface
public interface TypeFilter {
    boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException;
}/<code>

是一個函數式接口,包含一個match方法,方法返回boolean類型,有2個參數,都是接口類型的,下面介紹一下這2個接口。

MetadataReader接口

類元數據讀取器,可以讀取一個類上的任意信息,如類上面的註解信息、類的磁盤路徑信息、類的class對象的各種信息,spring進行了封裝,提供了各種方便使用的方法。

看一下這個接口的定義:

<code>public interface MetadataReader {

    /**
     * 返回類文件的資源引用
     */
    Resource getResource();

    /**
     * 返回一個ClassMetadata對象,可以通過這個讀想獲取類的一些元數據信息,如類的class對象、是否是接口、是否有註解、是否是抽象類、父類名稱、接口名稱、內部包含的之類列表等等,可以去看一下源碼

     */
    ClassMetadata getClassMetadata();

    /**
     * 獲取類上所有的註解信息
     */
    AnnotationMetadata getAnnotationMetadata();

}/<code>

MetadataReaderFactory接口

類元數據讀取器工廠,可以通過這個類獲取任意一個類的MetadataReader對象。

源碼:

<code>public interface MetadataReaderFactory {

    /**
     * 返回給定類名的MetadataReader對象
     */
    MetadataReader getMetadataReader(String className) throws IOException;

    /**
     * 返回指定資源的MetadataReader對象
     */
    MetadataReader getMetadataReader(Resource resource) throws IOException;

}/<code>

自定義Filter案例

需求

我們來個自定義的Filter,判斷被掃描的類如果是IService接口類型的,就讓其註冊到容器中。

代碼實現

來個自定義的TypeFilter類:

<code>package com.javacode2018.lesson001.demo22.test5;

import com.javacode2018.lesson001.demo22.test4.IService;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

public class MyFilter implements TypeFilter {
    /**
     * @param metadataReader
     * @param metadataReaderFactory
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        Class curClass = null;
        try {
            //當前被掃描的類
            curClass = Class.forName(metadataReader.getClassMetadata().getClassName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //判斷curClass是否是IService類型
        boolean result = IService.class.isAssignableFrom(curClass);
        return result;
    }
}/<code>

來一個@CompontentScan標註的類

<code>package com.javacode2018.lesson001.demo22.test5;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@ComponentScan(
        basePackages = {"com.javacode2018.lesson001.demo22.test4"},
        useDefaultFilters = false, //不啟用默認過濾器
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyFilter.class) //@1
        })

public class ScanBean5 {
}/<code>

@1:type為FilterType.CUSTOM,表示Filter是用戶自定義的,classes為自定義的過濾器

再來個測試用例

ComponentScanTest中新增個測試用例

<code>@Test
public void test5() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanBean5.class);
    for (String beanName : context.getBeanDefinitionNames()) {
        System.out.println(beanName + "->" + context.getBean(beanName));
    }
}/<code>

運行輸出

<code>service1->com.javacode2018.lesson001.demo22.test4.Service1@4cc451f2
service2->com.javacode2018.lesson001.demo22.test4.Service2@6379eb/<code>

excludeFilters

配置排除的過濾器,滿足這些過濾器的類不會被註冊到容器中,用法上面和includeFilters用一樣,這個我就不演示了,可以自己玩玩

@ComponentScan重複使用

從這個註解的定義上可以看出這個註解可以同時使用多個,如:

<code>@ComponentScan(basePackageClasses = ScanClass.class)
@ComponentScan(
        useDefaultFilters = false, //不啟用默認過濾器
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = IService.class)

        })
public class ScanBean7 {
}/<code>

還有一種寫法,使用@ComponentScans的方式:

<code>@ComponentScans({
        @ComponentScan(basePackageClasses = ScanClass.class),
        @ComponentScan(
                useDefaultFilters = false, //不啟用默認過濾器
                includeFilters = {
                        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = IService.class)
                })})
public class ScanBean7 {
}/<code>

Spring中這塊的源碼

@CompontentScan註解是被下面這個類處理的

<code>org.springframework.context.annotation.ConfigurationClassPostProcessor/<code>

這個類非常非常關鍵,主要用戶bean的註冊,前面我們介紹的@Configuration,@Bean註解也是被這個類處理的。

還有下面這些註解:

<code>@PropertySource
@Import
@ImportResource
@Compontent/<code>

以上這些註解都是被ConfigurationClassPostProcessor這個類處理的,內部會遞歸處理這些註解,完成bean的註冊。

以@CompontentScan來說一下過程,第一次掃描之後會得到一批需要註冊的類,然後會對這些需要註冊的類進行遍歷,判斷是否有上面任意一個註解,如果有,會將這個類交給ConfigurationClassPostProcessor繼續處理,直到遞歸完成所有bean的註冊。

想成為高手,這個類是必看的。

總結

  1. @ComponentScan用於批量註冊bean,spring會按照這個註解的配置,遞歸掃描指定包中的所有類,將滿足條件的類批量註冊到spring容器中
  2. 可以通過value、basePackages、basePackageClasses 這幾個參數來配置包的掃描範圍
  3. 可以通過useDefaultFilters、includeFilters、excludeFilters這幾個參數來配置類的過濾器,被過濾器處理之後剩下的類會被註冊到容器中
  4. 指定包名的方式配置掃描範圍存在隱患,包名被重命名之後,會導致掃描實現,所以一般我們在需要掃描的包中可以創建一個標記的接口或者類,作為basePackageClasses的值,通過這個來控制包的掃描範圍
  5. @CompontScan註解會被ConfigurationClassPostProcessor類遞歸處理,最終得到所有需要註冊的類。

會不定期更新幹貨 喜歡的可以轉發加關注哦


分享到:


相關文章: