Spring源碼解析-@Import注解源碼解析

Spring 3.0之前浇借,創(chuàng)建Bean可以通過xml配置文件與掃描特定包下面的類來將類注入到Spring IOC容器內(nèi)歹嘹。而在Spring 3.0之后提供了JavaConfig的方式恤浪,也就是將IOC容器里Bean的元信息以java代碼的方式進(jìn)行描述扎阶。我們可以通過@Configuration與@Bean這兩個注解配合使用來將原來配置在xml文件里的bean通過java代碼的方式進(jìn)行描述

@Import注解提供了@Bean注解的功能揩晴,同時還有xml配置文件里標(biāo)簽組織多個分散的xml文件的功能筐钟,當(dāng)然在這里是組織多個分散的@Configuration

先看一下@Import注解的源碼:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    /**

{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}

or regular component classes to import.

Class<?>[] value();
}

從源碼里可以看出@Import可以配合 Configuration ,ImportSelector, ImportBeanDefinitionRegistrar 來使用潘拱,下面的or表示也可以把Import當(dāng)成普通的Bean使用
@Import只允許放到類上面疹鳄,不能放到方法上。下面我們來看具體的使用方式

普通使用方法

這種方式可以直接把類加入到Spring IOC容器

@Configuration
@Import(value={UserServiceImpl.class})
public class Config {

}

但是這種方式有一些問題芦岂,那就是只能使用類的無參構(gòu)造方法來創(chuàng)建bean瘪弓,對于有參數(shù)的構(gòu)造方法就無能為力了

結(jié)合ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar接口的源碼如下:

public interface ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
     
}

可以看到這個接口唯一的方法是有兩個參數(shù)的

AnnotationMetadata:通過這個參數(shù)可以拿到類的元數(shù)據(jù)信息
BeanDefinitionRegistry:通過這個參數(shù)可以操作IOC容器
我們可以使用一個類來實(shí)現(xiàn)這個接口

public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);
        registry.registerBeanDefinition("userService", userService.getBeanDefinition());
    }

}

可以看到我們在這個方法里面做一些特殊操作什么的都是可以的,相比較于普通的方式可是靈活了很多

接著我們在@Import注解引入的地方只需要修改為引入UserServiceBeanDefinitionRegistrar就可以了

@Configuration
@Import(value={UserServiceBeanDefinitionRegistrar.class})
public class Config {

}

結(jié)合ImportSelector接口

相比較與實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口之后直接操作Bean容器來說禽最,使用ImportSelector會更加優(yōu)雅一些腺怯,只需要返回需要注入類的全限定名就可以了

ImportSelector接口的源碼如下:

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}
public class UserServiceImportSelect implements ImportSelector{

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        
       return new String[]{UserServiceImpl.class.getName()};
    }

}
@Configuration()
@Import(value={UserServiceImportSelect.class})
public class Config {

}

相比較三種方式來說可以看到最后這種才是最優(yōu)雅的方式

源碼解析

首先我們就以第三種比較優(yōu)雅的方式出發(fā),使用Call Hierarchy看一下ImportSelector接口的selectImports方法調(diào)用鏈關(guān)系:

看過之前Spring源碼解析文章的同學(xué)都知道川无,refresh方法是用來初始化容器上下文的呛占。跟著這個調(diào)用鏈走下來到中間有一個類是ConfigurationClassPostProcessor,根據(jù)類名我們就可以猜到這個類應(yīng)該是處理配置類(也就是標(biāo)注@Configuration)的懦趋。那么從這開始看吧

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
     
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
       //查看是否是配置類
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    //如果沒有配置類就直接返回
    if (configCandidates.isEmpty()) {
        return;
    }
    
    //對這些配置類根據(jù)Order排序
    Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
        @Override
        public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
            int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
        }
    });
    
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
        }
    }
             //創(chuàng)建配置類的解析類
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
    do {
       //ConfigurationClassParser的parse方法進(jìn)行解析晾虑,重點(diǎn)哈
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
    
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
    
        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<String>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());
    
    if (sbr != null) {
        if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
            sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
        }
    }
    
    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }

}

現(xiàn)在該進(jìn)入ConfigurationClassParser類了

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
    

    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    
    processDeferredImportSelectors();

}

//下面三種方法用于處理不同的BeanDefinition 類型,但最終都是使用的processConfigurationClass方法

protected final void parse(String className, String beanName) throws IOException {
    MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
    processConfigurationClass(new ConfigurationClass(reader, beanName));
}

protected final void parse(Class<?> clazz, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(clazz, beanName));
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

可以看到配置類可能會是三種形式的存在,這三種形式的Bean在操作上有著部分不一樣帜篇,但是大部分又是一樣糙捺,所以Spring用這種模式來處理。不得不感嘆人家設(shè)計的真好

接著往下看

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    //在這里處理Configuration重復(fù)import
    //如果同一個配置類被處理兩次笙隙,兩次都屬于被import的則合并導(dǎo)入類继找,返回。如果配置類不是被導(dǎo)入的逃沿,則移除舊使用新的配置類
    if (existingClass != null) {
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {
            this.configurationClasses.remove(configClass);
            for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
                if (configClass.equals(it.next())) {
                    it.remove();
                }
            }
        }
    }
    
    SourceClass sourceClass = asSourceClass(configClass);
    do {
      //接著往下看吧
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);
    
    this.configurationClasses.put(configClass, configClass);

}
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    // 處理遞歸類
    processMemberClasses(configClass, sourceClass);
    
    // 處理@PropertySource注解
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }
    
    // 處理 @ComponentScan 注解
    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());
                }
            }
        }
    }
    //處理Import注解,這個是咱們的菜
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    
    // 處理@ImportResource 注解
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }
    
    //處理包含@Bean注解的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    
    // 處理普通方法
    processInterfaces(configClass, sourceClass);

   


    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }
    
    return null;

}

看到這里好像突然發(fā)現(xiàn)了新大陸呀幻锁,原來我們經(jīng)常見的@Bean凯亮、@ImportResource 、@Import哄尔、@ComponentScan假消、@PropertySource都是在這里處理的呀

咱們的重點(diǎn)還是放在@Import上,對其他幾個注解感興趣的同學(xué)可以自行研究一下

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }
    
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                //如果實(shí)現(xiàn)了ImportSelector接口
                if (candidate.isAssignable(ImportSelector.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            selector, this.environment, this.resourceLoader, this.registry);
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectors.add(
                                new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    else {
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                //如果實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                     //將import當(dāng)成Configuration來使用就是我們的第一種應(yīng)用的方式
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }

}

至此@import 源碼閱讀完畢

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岭接,一起剝皮案震驚了整個濱河市富拗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸣戴,老刑警劉巖啃沪,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窄锅,居然都是意外死亡创千,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門入偷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來追驴,“玉大人,你說我怎么就攤上這事疏之〉钛” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵锋爪,是天一觀的道長丙曙。 經(jīng)常有香客問我,道長几缭,這世上最難降的妖魔是什么河泳? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮年栓,結(jié)果婚禮上拆挥,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好纸兔,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布惰瓜。 她就那樣靜靜地躺著,像睡著了一般汉矿。 火紅的嫁衣襯著肌膚如雪崎坊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天洲拇,我揣著相機(jī)與錄音奈揍,去河邊找鬼。 笑死赋续,一個胖子當(dāng)著我的面吹牛舆吮,可吹牛的內(nèi)容都是我干的罐农。 我是一名探鬼主播凭语,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼掀亥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸦列?” 一聲冷哼從身側(cè)響起租冠,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薯嗤,沒想到半個月后顽爹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骆姐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年话原,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诲锹。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡繁仁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出归园,到底是詐尸還是另有隱情黄虱,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布庸诱,位于F島的核電站捻浦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桥爽。R本人自食惡果不足惜朱灿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钠四。 院中可真熱鬧盗扒,春花似錦跪楞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至褥影,卻和暖如春池户,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凡怎。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工校焦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人统倒。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓斟湃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親檐薯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容