SpringImport三种用法与源码解读

编程

 最近在看Spring Cloud相关的源码,每次引入一个新的starter,发现都会加一些enable的注解,比如:@EnableDiscoveryClient,用于将应用注册到Eureka Server并将Eureka Server有的服务拉取到微服务系统。点开EnableDiscoveryClient源码,便会发现里面用到了@import注解。源码如下:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import(EnableDiscoveryClientImportSelector.class)

public @interface EnableDiscoveryClient {

/**

* If true, the ServiceRegistry will automatically register the local server.

*/

boolean autoRegister() default true;

}

这个@import的作用是什么呢,它是如何工作的呢?我们在项目里如何应用@import导入我们自定义的类?

Import的用法

  Spring 3.0之前,我们的Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内。Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述。我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述。@Import注解提供了@Bean注解的功能,同时还有xml配置文件里<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration。先看看@Import注解的源码吧:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Import {

//这里说了可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用噢 或者常用的(regular component classes )也就是Bean

/**

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

* or regular component classes to import.

*/

Class<?>[]value();

}

从源码里可以看出Import可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用,下面的or表示也可以把Import当成普通的Bean来使用,只是使用方式上有点区别,@Import只允许放到类上面,不能放到方法上。下面我们来看具体的使用方式。在使用之前我们有几个类在下面几种方式里都会用到,下面把这些公共的类放到一起。

  • 首先这里有个User的实体类,做为Bean里的参数,在实际的测试中也可以没有这个类,为了例子的完整性,我还是把代码贴到下面:

packagecom.ivan.entity;

publicclassUser{

privateInteger id;

privateString name;

publicIntegergetId(){

return id;

}

publicvoidsetId(Integer id){

this.id = id;

}

publicStringgetName(){

return name;

}

publicvoidsetName(String name){

this.name = name;

}

}

  • 定义了一个UserService接口类,为了是体现Spring的面向接口编程。代码如下:

packagecom.ivan.service;

importcom.ivan.entity.User;

publicinterfaceUserService{

publicintsave(User user);

}

  • UserService接口对应的实现类,也是我们想注入到Spring IOC容器里的类,代码如下:

packagecom.ivan.service.impl;

importcom.ivan.entity.User;

importcom.ivan.service.UserService;

publicclassUserServiceImplimplementsUserService{

publicintsave(User user){

System.out.println("调用了当前方法");

return1;

}

}

  • 测试类:

packagecom.ivan;

importorg.springframework.context.annotation.AnnotationConfigApplicationContext;

importcom.ivan.config.Config;

importcom.ivan.service.UserService;

importcom.ivan.service.impl.UserServiceImpl;

/**

*

* 功能描述:

*

* @version 2.0.0

* @author zhiminchen

*/

publicclassApp{

publicstaticvoidmain(String[] args){

AnnotationConfigApplicationContext context =newAnnotationConfigApplicationContext(Config.class);

UserService userService =(UserService)context.getBean(UserServiceImpl.class);

userService.save(null);

context.close();

}

}

在测试类App里,用到了Config.class,不同的Import使用方式,这里的代码是不一样的,下面来看具体Import在使用上的区别:

1:最简单的导入类的方式

  这是最简单的一种将类加入到Spring IOC容器的方式,直接将类的class加到Import的value里,Config的代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Import;

import com.ivan.service.impl.UserServiceImpl;

@Configuration

@Import(value={UserServiceImpl.class})

public class Config{

}

启动测试类,发现调用到了我们的UserServiceImpl里的save方法,这样将类注入的方式有个问题就是没法注入参数。也就是说UserServiceImpl提供的应该是无参的构造方法。这种方式注入类在Spring内部用的并不多。最多的使用方式还是下面两种。

2.通过ImportBeanDefinitionRegistrar将类注入到Spring IOC容器

  Import注解通过配合ImportBeanDefinitionRegistrar类将类注入Spring IOC容器里。ImportBeanDefinitionRegistrar类的源码如下:

publicinterfaceImportBeanDefinitionRegistrar{

/**

* Register bean definitions as necessary based on the given annotation metadata of

* the importing {@code @Configuration} class.

* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be

* registered here, due to lifecycle constraints related to {@code @Configuration}

* class processing.

* @param importingClassMetadata annotation metadata of the importing class

* @param registry current bean definition registry

*/

publicvoidregisterBeanDefinitions(

AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry);

}

从上面的代码可以看出在注入Spring IOC容器的时候,我们肯定是通过registry这个变量了,而importingClassMetadata可以得到类的元数据信息。我们自定义的UserServiceBeanDefinitionRegistrar 类定义如下:

packagecom.ivan.bean.registrar;

importorg.springframework.beans.factory.support.BeanDefinitionBuilder;

importorg.springframework.beans.factory.support.BeanDefinitionRegistry;

importorg.springframework.context.annotation.ImportBeanDefinitionRegistrar;

importorg.springframework.core.type.AnnotationMetadata;

importcom.ivan.service.impl.UserServiceImpl;

publicclassUserServiceBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{

publicvoidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata,

BeanDefinitionRegistry registry){

BeanDefinitionBuilder userService =BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);

//通过registry就可以注入到容器里啦

registry.registerBeanDefinition("userService", userService.getBeanDefinition());

}

}

Config类的Import class需要改成UserServiceBeanDefinitionRegistrar.classs。代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Import;

import com.ivan.bean.registrar.UserServiceBeanDefinitionRegistrar;

@Configuration(value="ivan_test")

@Import(value={UserServiceBeanDefinitionRegistrar.class})

public class Config{

}

可以明显的看出通过ImportBeanDefinitionRegistrar的方式我们可以对类进行个性化的定制,比如对需要传入的参数进行修改,也可以通过ImportBeanDefinitionRegistrar注入一批相似的类。有BeanDefinitionRegistry对象也有可以控制Spring IOC容器里Bean的定义,想做些什么也就方便很多了。

3.通过ImportSelector方式注入Bean

上面通过ImportBeanDefinitionRegistrar的方式注入的实例需要我们操作BeanDefinitionRegistry 对象,而通过ImportSelector方式我们可以不操作BeanDefinitionRegistry 对象,只需要告诉容器我们需要注入类的完整类名就好。ImportSelector类的源码如下:

publicinterfaceImportSelector{

/**

* Select and return the names of which class(es) should be imported based on

* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.

*/

String[]selectImports(AnnotationMetadata importingClassMetadata);

}

上面的代码中可以看出需要返回一个String数组给到容器,传入的参数可以得到注解的元数据信息,现在我们来模拟下我们在Cloud代码里看到的enable都是如何通过ImportSelector方式来实现的。首先我们需要自定义一个注解,源码如下:

package com.ivan.enable;

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

import org.springframework.context.annotation.Import;

import com.ivan.select.UserServiceImportSelect;

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Target(ElementType.TYPE)

@Import(UserServiceImportSelect.class)

public @interface EnableUserService{

String name ();

}

EnableUserService 是个注解,里面通过@Import引入了UserServiceImportSelect,具体注入的逻辑在UserServiceImportSelect这个类里面,我们的注解同时定义了一个name属性,这里只是为了测试,在实际中你可以定义你需要的属性,然后在具体的ImportSelect里根据属性的值进行不同的配置。UserServiceImportSelect属性的代码如下:

package com.ivan.select;

import java.util.Map;

import java.util.Map.Entry;

import org.springframework.context.annotation.ImportSelector;

import org.springframework.core.type.AnnotationMetadata;

import com.ivan.enable.EnableUserService;

import com.ivan.service.impl.UserServiceImpl;

public classUserServiceImportSelectimplementsImportSelector{

public String[]selectImports(AnnotationMetadata importingClassMetadata){

Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableUserService.class.getName(),true);

for(Entry<String, Object> entry : map.entrySet()){

System.out.println("key is : "+ entry.getKey()+" value is : "+ entry.getValue());

}

returnnewString[]{UserServiceImpl.class.getName()};

}

}

可以通过importingClassMetadata这个属性得到用了EnableUserService注解的元信息。同时返回了一个我们需要注入的类的名称,这样就可以注入到容器里啦,Config类的代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;

import com.ivan.enable.EnableUserService;

@Configuration()

@EnableUserService(name="ivan_test")

public class Config{

}

通过上面三步我们就可以完成自定义enable注解并完成相应Bean的注入,顺带提一下通过ImportBeanDefinitionRegistrar的方式同样可以自定义注解然后注入我们需要的类。必竟ImportBeanDefinitionRegistrar这种方式从灵活性上来说应该是最全的。

源码解读

  通过上面几种方式的应用,应该可以掌握@Import注解的应用了。但Spring IOC内部是如何做到类的注入的呢,下面就来读一下Spring IOC内部类的源码。读源码如何找到源码的入口是关键,这里有个小技巧,通过IDE的Call Hierarchy调用链我们可以找到关键的代码,我们知道@Import这个注解无论它多么神,一定会调用到ImportSelector类里的selectImports方法的。我们通过这个方法就能找到如下的调用关系,如下图所示:

 

 

image.png

 

  在Spring IOC容器加载过程中会调用BeanFactoryPostProcessor方法,以处理如对BeanDefinition进行修改等操作。从调用链上看我们需要关注ConfigurationClassPostProcessor这个类,从名字中我们也可以看出他一定跟@Configuration这个注解有关。下面对ConfigurationClassPostProcessor的关键代码进行注释:

public classConfigurationClassPostProcessorimplementsBeanDefinitionRegistryPostProcessor,

PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

/**

*在容器初使化的时候会调用到这个方法里,

**/

@Override

public voidpostProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){

int factoryId = System.identityHashCode(beanFactory);

if(this.factoriesPostProcessed.contains(factoryId)){

thrownewIllegalStateException(

"postProcessBeanFactory already called on this post-processor against "+ beanFactory);

}

this.factoriesPostProcessed.add(factoryId);

if(!this.registriesPostProcessed.contains(factoryId)){

// BeanDefinitionRegistryPostProcessor hook apparently not supported...

// Simply call processConfigurationClasses lazily at this point then.

//Configuration肯定是在这个方法里处理的

processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);

}

enhanceConfigurationClasses(beanFactory);

beanFactory.addBeanPostProcessor(newImportAwareBeanPostProcessor(beanFactory));

}

/** 这里的注释已经说了是处理Configuration的啦

* Build and validate a configuration model based on the registry of

* {@link Configuration} classes.

*/

public voidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){

List<BeanDefinitionHolder> configCandidates =newArrayList<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);

}

}

elseif(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)){

//这里会将类里有Configuration, Component, ComponentScan, Import, ImportResource 这些注解过的类加到configCandidates容器里

configCandidates.add(newBeanDefinitionHolder(beanDef, beanName));

}

}

//如果没有上面提到的类,则直接返回

// Return immediately if no @Configuration classes were found

if(configCandidates.isEmpty()){

return;

}

//对晒出来的类根据Order进行排序

// Sort by previously determined @Order value, if applicable

Collections.sort(configCandidates,newComparator<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;

}

});

// Detect any custom bean name generation strategy supplied through the enclosing application context

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;

}

}

//具体的解析是通过ConfigurationClassParser

// Parse each @Configuration class

ConfigurationClassParser parser =newConfigurationClassParser(

this.metadataReaderFactory,this.problemReporter,this.environment,

this.resourceLoader,this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates =newLinkedHashSet<BeanDefinitionHolder>(configCandidates);

Set<ConfigurationClass> alreadyParsed =newHashSet<ConfigurationClass>(configCandidates.size());

do{

//调用ConfigurationClassParser的parse方法进行解析

parser.parse(candidates);

parser.validate();

Set<ConfigurationClass> configClasses =newLinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());

configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content

if(this.reader ==null){

this.reader =newConfigurationClassBeanDefinitionReader(

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 =newHashSet<String>(Arrays.asList(candidateNames));

Set<String> alreadyParsedClasses =newHashSet<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(newBeanDefinitionHolder(bd, candidateName));

}

}

}

candidateNames = newCandidateNames;

}

}

while(!candidates.isEmpty());

// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes

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();

}

}

}

上面的分析最终解析Import类是在ConfigurationClassParser这个类上面,从上面的调用图上我们也可以看出会调用到ConfigurationClassParser这个类上,下面对这个类的关键代码进行注释(代码只截取了部分源码,且没按源码里的顺序,只是为了阅读的方便):

class ConfigurationClassParser {

/**

*这是解析的入口方法,传入的参数是有特殊注解的Class类

**/

public void parse(Set<BeanDefinitionHolder> configCandidates){

this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

for(BeanDefinitionHolder holder : configCandidates){

BeanDefinition bd = holder.getBeanDefinition();

//无论bd属于下面的那三种情况,都会调用processConfigurationClass方法

try{

if(bd instanceof AnnotatedBeanDefinition){

parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());

}

elseif(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方法

protectedfinal void parse(String className, String beanName) throws IOException {

MetadataReader reader =this.metadataReaderFactory.getMetadataReader(className);

processConfigurationClass(new ConfigurationClass(reader, beanName));

}

protectedfinal void parse(Class<?> clazz, String beanName) throws IOException {

processConfigurationClass(new ConfigurationClass(clazz, beanName));

}

protectedfinal void parse(AnnotationMetadata metadata, String beanName) throws IOException {

processConfigurationClass(new ConfigurationClass(metadata, beanName));

}

//这里面处理Configuration 标注的class类

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {

if(this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)){

return;

}

ConfigurationClass existingClass =this.configurationClasses.get(configClass);

//在这里处理Cconfiguration重复import

if(existingClass !=null){

if(configClass.isImported()){

if(existingClass.isImported()){

existingClass.mergeImportedBy(configClass);

}

// Otherwise ignore new imported config class; existing non-imported class overrides it.

return;

}

else{

// Explicit bean definition found, probably replacing an import.

// Let"s remove the old one and go with the new one.

this.configurationClasses.remove(configClass);

for(Iterator<ConfigurationClass> it =this.knownSuperclasses.values().iterator(); it.hasNext();){

if(configClass.equals(it.next())){

it.remove();

}

}

}

}

// Recursively process the configuration class and its superclass hierarchy.

//这里通过循环处理上面的注解标注的类,得到SourceClass对象

SourceClass sourceClass =asSourceClass(configClass);

do{

sourceClass =doProcessConfigurationClass(configClass, sourceClass);

}

while(sourceClass !=null);

this.configurationClasses.put(configClass, configClass);

}

/**

**这个方法里会处理具体的各种注解类,@Configuration @Import等

*/

protectedfinal SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

throws IOException {

// Recursively process any member (nested) classes first

processMemberClasses(configClass, sourceClass);

// Process any @PropertySource annotations

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");

}

}

// 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());

}

}

}

}

//在这个方法里处理Import注解,

// Process any @Import annotations

processImports(configClass, sourceClass,getImports(sourceClass),true);

// Process any @ImportResource annotations

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);

}

}

// Process individual @Bean methods

Set<MethodMetadata> beanMethods =retrieveBeanMethodMetadata(sourceClass);

for(MethodMetadata methodMetadata : beanMethods){

configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));

}

// Process default methods on interfaces

processInterfaces(configClass, sourceClass);

// Process superclass, if any

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();

}

}

// No superclass -> processing is complete

returnnull;

}

//在这里处理具体的Import

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){

//这里看到了熟悉的ImportSelector了吧

if(candidate.isAssignable(ImportSelector.class)){

// Candidate class is an ImportSelector -> delegate to it to determine imports

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){

//这里会把DeferredImportSelector 加入到deferredImportSelectors集合类中,在parse方法里调用接口定义的方法

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);

}

}

elseif(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)){

//在这里处理ImportBeanDefinitionRegistrar,

// Candidate class is an ImportBeanDefinitionRegistrar ->

// delegate to it to register additional bean definitions

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当成Configuration来使用也是我们最简单的一种应用的方式

// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->

// process it as an @Configuration class

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();

}

}

}

}

上面整体上分析了一下Spring里是如何处理@Import这个注解的,只跟踪了处理ImportSelector这种方式的处理逻辑,代码里都有比较详细的注释。其它两种方式分析的方法大同小异。从整体上来理解的话大概就下面几个流程:

  • Spring IOC容器初使化的时候会调用AbstractApplicationContext 的refresh方法
  • 在refresh里会调用各种BeanFactoryPostProcessor, 其中就包括我们需要关注的ConfigurationClassPostProcessor。
  • ConfigurationClassPostProcessor 不但用于处理@Configuration注解,里面也有处理@Import注解。
  • 最终的处理是通过 ConfigurationClassParser 这个类完成对Import各种情况的处理
  • Import有三种导入方式,从代码里我们可以看到对于不同方式的处理。



作者:良辰美景TT


链接:https://www.jianshu.com/p/7eb0c2b214a7


来源:简书


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

以上是 SpringImport三种用法与源码解读 的全部内容, 来源链接: utcz.com/z/514556.html

回到顶部