大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專(zhuān)注Java教育14年 全國(guó)咨詢(xún)/投訴熱線(xiàn):400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁(yè) hot資訊 Spring包掃描機(jī)制詳解

Spring包掃描機(jī)制詳解

更新時(shí)間:2021-09-10 10:54:20 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽2152次

目標(biāo)

此篇文章會(huì)主要介紹Spring中兩個(gè)非常重要的關(guān)于包掃描的基礎(chǔ)類(lèi),由于Spring代碼太龐大,因此本文不會(huì)細(xì)致地說(shuō)明每一行代碼地作用,只會(huì)講清楚關(guān)鍵的地方有什么作用,以及一些子類(lèi)可以重寫(xiě)的方法,用來(lái)覆蓋默認(rèn)掃描行為。最后會(huì)基于Spring提供的包掃描設(shè)施來(lái)寫(xiě)一個(gè)簡(jiǎn)單的例子來(lái)模仿MyBatis-Spring掃描Mapper接口,生成代理注冊(cè)到容器中。我們主要關(guān)注ClassPathScanningCandidateComponentProvider以及ClassPathBeanDefinitionScanner這兩個(gè)類(lèi),講清楚這兩個(gè)類(lèi)的作用以及開(kāi)發(fā)者需要關(guān)注的方法。

ClassPathScanningCandidateComponentProvider

此類(lèi)是Spring中包掃描機(jī)制最底層的類(lèi),用于掃描指定包下面的類(lèi)文件,并且會(huì)根據(jù)用戶(hù)提供的includeFilters以及excludeFilters來(lái)過(guò)濾掉不想注冊(cè)的類(lèi),最后生成一個(gè)基本的BeanDefinition。

先看下兩個(gè)比較重要的屬性吧

/**
 * 包含集合,如果類(lèi)文件匹配includeFilters集合中任意一個(gè)TypeFilter條件,那么就通過(guò)篩選。
 * 其中最常見(jiàn)的TypeFilter有AnnotationTypeFilter、AssignableTypeFilter
 * AnnotationTypeFilter: 代表類(lèi)是否被指定注解標(biāo)注
 * AssignableTypeFilter: 代表類(lèi)是否繼承(實(shí)現(xiàn))自指定的超類(lèi)
 */
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
/**
 * 排除集合, 如果類(lèi)文件匹配excludeFilters集合中任何一個(gè)TypeFilter條件,那么就不會(huì)通過(guò)篩選
 * 并且excludeFilters優(yōu)先級(jí)高于includeFilters
 */
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();

初始化,只看參數(shù)最長(zhǎng)的那個(gè)構(gòu)造方法

public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, 
                                                   Environment environment) {
    // 主要關(guān)注useDefaultFilters這個(gè)參數(shù), 如果為true, 會(huì)注冊(cè)一個(gè)默認(rèn)的
    // includeFilter
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(null);
}
protected void registerDefaultFilters() {
    // 注冊(cè)一個(gè)注解的TypeFilter,意思是如果類(lèi)定義時(shí)有被@Component注解標(biāo)注
    // 那么就會(huì)通過(guò)篩選,需要注意的是衍生注解也是會(huì)通過(guò)篩選的
    // 比如@Service、@Controller、@Repository,它們有一個(gè)共同點(diǎn),那就是這三個(gè)
    // 注解本身就是被@Component注解標(biāo)注的
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    // 下面是注冊(cè)JAVA EE里的一些注解,一般開(kāi)發(fā)也不用, 就省略了
}

接下來(lái)就是最重要的方法了,也就是掃描包文件,并且將符合篩選條件的類(lèi)生成BeanDefinition。下面代碼將日志打印剔除了。

/**
 * 此方法會(huì)掃描指定包以及子包
 * @param basePackage 需要掃描的包,形如com.wangtao.dao
 * @return 返回一個(gè)符合篩選條件后的BeanDefinition集合
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
        // 將包名解析成路徑
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 獲取此包以及子包下的所有.class文件資源
        Resource[] resources = this.resourcePatternResolver.
            getResources(packageSearchPath);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    // 得到類(lèi)文件的元數(shù)據(jù),是基于ASM字節(jié)碼技術(shù)實(shí)現(xiàn)的,此時(shí)還沒(méi)有加載類(lèi)文件
                    // 包括類(lèi)名、注解信息、父類(lèi)、接口等等一系列信息
                    MetadataReader metadataReader = this.metadataReaderFactory.
                        getMetadataReader(resource);
                    // 匹配篩選條件,也就是上述includeFilters、excludeFilters這兩個(gè)集合
                    if (isCandidateComponent(metadataReader)) {
                        // 創(chuàng)建一個(gè)BeanDefinition
                        // 只是簡(jiǎn)單的設(shè)置了beanClassName屬性為類(lèi)的完全限定名
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBean
                            Definition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // 這里會(huì)再有一個(gè)篩選條件,一般是根據(jù)類(lèi)文件的元數(shù)據(jù)篩選
                        // 比如是不是具體類(lèi),是不是頂層類(lèi),是不是抽象類(lèi)等
                        // 默認(rèn)情況下只添加頂層的具體類(lèi),頂層的意思是可以獨(dú)立實(shí)例化而不會(huì)依賴(lài)外部類(lèi)
                        // 成員內(nèi)部類(lèi)需要外部類(lèi)對(duì)象才能實(shí)例化,就不會(huì)通過(guò)。
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                        "Failed to read candidate component class: " + resource, ex);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath 
                                               scanning", ex);
    }
    return candidates;
}

再看看兩個(gè)isCandidateComponent方法的默認(rèn)實(shí)現(xiàn),一般來(lái)說(shuō)我們可能需要重寫(xiě)這兩個(gè)方法來(lái)改變默認(rèn)的篩選條件。

// 根據(jù)excludeFilters、excludeFilters初步篩選
// 一目了然,基本不用再需要解釋
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return false;
        }
    }
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}
// 根據(jù)類(lèi)文件元數(shù)據(jù)篩選
// 只掃描頂層具體類(lèi)或者雖然是抽象類(lèi)但是存在@Lookup標(biāo)記的方法
// 后面那個(gè)是用于方法注入,我從來(lái)沒(méi)用過(guò)。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(
                    Lookup.class.getName()))));

總結(jié):此類(lèi)在默認(rèn)情況下會(huì)將指定包以及子包下中被@Component(以及衍生注解)標(biāo)記的頂層類(lèi)創(chuàng)建一個(gè)BeanDefinition。

說(shuō)到這插下Spring開(kāi)啟注解掃描的配置,有時(shí)我們可能只想在SpringMVC的配置文件中掃描@Controller標(biāo)記的類(lèi),其它層掃描@Service、@Component、@Repository標(biāo)記的類(lèi),就可以像下面這樣分層配置。

springmvc.xml

<context:component-scan base-package="com.wangtao.controller" 
                        use-default-filters="false">
    <context:include-filter 
        type="annotation" 
        expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

applicationContext.xml

<context:component-scan base-package="com.wangtao.service,com.wangtao.dao">
    <context:exclude-filter 
        type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

也就是說(shuō)SpringMVC中禁掉默認(rèn)的includeFilters,添加了一個(gè)使用@Controller標(biāo)記的條件,而applicationContext.xml使用默認(rèn)的includeFilters,但是排除對(duì)@Controller標(biāo)記的類(lèi)。

ClassPathBeanDefinitionScanner

此類(lèi)繼承自ClassPathScanningCandidateComponentProvider,除了擁有父類(lèi)掃描包的功能外,還會(huì)對(duì)掃描后的BeanDefinition加工并注冊(cè)到Spring容器中,所謂的加工就是指會(huì)設(shè)置一些類(lèi)文件中用注解標(biāo)記的一些屬性值,如@Lazy、@Scope、@Primary等。

先看一些重要屬性

/** 用于注冊(cè)bean **/
private final BeanDefinitionRegistry registry;
/** 
 * 此類(lèi)存儲(chǔ)了BeanDefinition一些默認(rèn)屬性值
 * lazyInit: false
 * autowireMode: AbstractBeanDefinition.AUTOWIRE_NO
 * initMethodName: null
 * destroyMethodName: null
**/
private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
/** bean name 生成器 **/
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
/**
 * 默認(rèn)值:true
 * 相當(dāng)于開(kāi)啟<context:annotation-config>
 * 意味著我們可以使用@Autowired、@Resource、@PostConstruct、@PreDestroy注解
 * 會(huì)自動(dòng)幫我們注冊(cè)解析這幾個(gè)注解的BeanPostProcessor
 */
private boolean includeAnnotationConfig = true;

構(gòu)造方法

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, 
                                      boolean useDefaultFilters,
                                      Environment environment, 
                                      ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    // 同ClassPathScanningCandidateComponentProvider
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}

接下來(lái)看最重要的掃描方法

/**
 * 返回掃描真正注冊(cè)bean的數(shù)量
 */
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    doScan(basePackages);
	// 注冊(cè)幾個(gè)BeanPostProcessor用來(lái)解析@Autowired、@Reource等幾個(gè)注解
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
/**
 * 將BeanDefinition集合返回,子類(lèi)若有必要可以繼續(xù)對(duì)BeanDefinition做修改
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new 
        LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
        // 得到所有符合掃描條件的BeanDefinition集合,接下來(lái)會(huì)對(duì)這些BeanDefinition加工
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 主要包括bean的scope屬性(默認(rèn)單例)以及代理策略(不需要代理,JDK動(dòng)態(tài)代理、CGLIB)
            // 來(lái)適配AOP
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver
                .resolveScopeMetadata(candidate);
            // 設(shè)置scope屬性
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成bean name
            String beanName = this.beanNameGenerator
                .generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                // 根據(jù)beanDefinitionDefaults設(shè)置一些默認(rèn)值
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 如果是注解定義的Bean, findCandidateComponents默認(rèn)實(shí)現(xiàn)返回的BeanDefinition
            // 是一個(gè)ScannedGenericBeanDefinition,其實(shí)現(xiàn)了AnnotatedBeanDefinition接口
            if (candidate instanceof AnnotatedBeanDefinition) {
                // 解析@Scope、@Primary、@Lazy等屬性并設(shè)置到BeanDefinition中
                AnnotationConfigUtils.processCommonDefinitionAnnotations(
                    (AnnotatedBeanDefinition) candidate);
            }
            // 檢查BeanDefinition
            // 主要檢查這個(gè)容器中是否已經(jīng)存在此BeanDefinition
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = 
                    new BeanDefinitionHolder(candidate, beanName);
                // 設(shè)置代理策略
                definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(
                    scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 注冊(cè)到Spring容器中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

因此如果我們需要自定義掃描實(shí)現(xiàn)bean的注冊(cè),基本上就是要繼承ClassPathBeanDefinitionScanner并且重寫(xiě)doScan方法了。大致框架就是

public class MyScanner extends ClassPathBeanDefinitionScanner {    
    public MyScanner(BeanDefinitionRegistry registry) {
        super(registry)
    }   
    @Overide
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 父類(lèi)已經(jīng)將這些bean注冊(cè)了
        Set<BeanDefinitionHolder> holders = super.doScan(basePackages);
        for(BeanDefinitionHolder holder : holders) {
            // 在這里修改BeanDefinition引用的對(duì)象即可
        }
        return holders;
    }
}

以上就是動(dòng)力節(jié)點(diǎn)小編介紹的"Spring包掃描機(jī)制詳解",希望對(duì)大家有幫助,想了解更多可查看Spring框架教程。動(dòng)力節(jié)點(diǎn)在線(xiàn)學(xué)習(xí)教程,針對(duì)沒(méi)有任何Java基礎(chǔ)的讀者學(xué)習(xí),讓你從入門(mén)到精通,主要介紹了一些Java基礎(chǔ)的核心知識(shí),讓同學(xué)們更好更方便的學(xué)習(xí)和了解Java編程,感興趣的同學(xué)可以關(guān)注一下。

提交申請(qǐng)后,顧問(wèn)老師會(huì)電話(huà)與您溝通安排學(xué)習(xí)

  • 全國(guó)校區(qū) 2025-10-20 搶座中
免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 一本一道 | 国产97色在线 | 亚洲 | 成人精品综合免费视频 | 亚洲天天做夜夜做天天欢人人 | 无遮挡又黄又爽又色的视频免费 | 国产男女在线观看 | 久久99国产精品久久欧美 | 欧美精品亚洲网站 | caoporm超免费公开视频 | 国产精品久久久久激情影院 | 亚洲欧美一 | 鸥美毛片| 99久久精品免费看国产高清 | 51国产午夜精品免费视频 | 特黄特级a级黄毛片免费观看多人 | 欧美日本中文字幕 | 精品一区二区久久久久久久网精 | 欧美视频第二页 | 深夜免费在线视频 | 欧美中文字幕在线 | 亚洲 自拍 另类 制服在线 | 欧美精品视 | 欧美亚洲国产一区二区三区 | 一级大黄视频 | 国产精品成| 日本精品一区二区三区在线观看 | 在线播放色 | aaa级片 | 亚洲免费二区三区 | 香蕉视频在线观看www | 四虎永久免费影院在线 | 日本一区二区在线 | 久久不卡免费视频 | 精品国产成人综合久久小说 | 午夜在线 | 一区视频在线播放 | 久久精品99精品免费观看 | 九天玄帝诀王凡小说免费阅读 | 香蕉综合视频 | 国产精品美女流白浆视频 | 理论片 我不卡影院 |