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