Springboot中@Import的使用原理

 最近我看了看Springboot的源码,借着周末分享@Import这一小知识点,也正好体验一下CSDN博客最新的编辑器。编辑起来很好用,舒服。

再看源码之前先写一个小的demo,试着看看@Import是怎么用的。(为避免文章过长,下面代码均有一定程度的代码删减,可以自行补全或查看源码哦!)

1.创建一个maven项目,pom.xml文件配置如下:

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.6.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

2.创建一个实体类

package com.entity;

public class User implements Serializable{

	private String name;
	private String age;
	
	public User() {
		this.name = "xiaochen";
		this.age = "6";
	}
}

3.创建ImportUser类

package com.demo;

public class ImportUser implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[] {"com.entity.User"};
	}
}

这里看到ImportSelector接口,是不是好像在哪里见过呢?

4.创建配置类

package com.demo;

@Import(ImportUser.class)
@Configuration
public class ImportConfiguration {}

5.创建启动类

package com.demo;
public class ImportDemo {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//这里使用register()和scan()方法都可,用scan()方法就不用在配置类中使用@Configuration注解了。
//		applicationContext.register(ImportConfiguration.class);
		applicationContext.scan("com.demo");
		applicationContext.refresh();
		User user = applicationContext.getBean(User.class);
	    System.out.println(user);
	}
}

6,运行结果

上边的代码很简单,就不过多解释,下面直接看Springboot中是怎么使用的。

我们知道@SpringbootApplication注解主要是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan组成。而@EnableAutoConfiguration中就用到了@Import注解,用于引入

EnableAutoConfigurationImportSelector类,代码如下:

@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

 这里我们先创建一个Springboot启动类

@SpringBootApplication
public class ApplicationStart {
	public static void main(String[] args) {
		SpringApplication.run(ApplicationStart.class, args);
	}
}

 点击run()方法进入SpringApplication类中,依次查看调用方法;

public ConfigurableApplicationContext run(String... args) {
			refreshContext(context);
}
private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
}
protected void refresh(ApplicationContext applicationContext) {
		((AbstractApplicationContext) applicationContext).refresh();
}

@Override
public void refresh() throws BeansException, IllegalStateException {
		invokeBeanFactoryPostProcessors(beanFactory);
}

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
} 

到此再点击进入org.springframework.context.support.PostProcessorRegistrationDelegate类中;

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
     invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
}

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
	for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
	}
}

再点击postProcessBeanDefinitionRegistry()方法进入org.springframework.context.annotation.ConfigurationClassPostProcessor类中;

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		processConfigBeanDefinitions(registry);
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
	    parser.parse(candidates);
}

这里再点击parse()方法进入org.springframework.context.annotation.ConfigurationClassParser类当中;

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		processDeferredImportSelectors();
}
private void processDeferredImportSelectors() {
        String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
        processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}

这里看到selectImports()方法就在上面demo中出现过,再查看一下之前使用@Import注解导入的EnableAutoConfigurationImportSelector类,其就间接实现了ImportSelector接口;这和demo中是不是很相似。

这里的 selectImports()方法再往里就是读取各个jar包中有含 META-INF/spring.factories.文件的信息了,这里就不多做分析了。

但这里会不会有个疑问,@Import是怎么起作用的,这里就要看看deferredImport是怎么实例化的!!!,也是在上面的方法中有这样一段代码;

private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
	ConfigurationClass configClass = deferredImport.getConfigurationClass();
}
}

代码中可以看出是deferredImportSelectors对象间接赋值 , 那就找它实例化的位置;

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates,
       boolean checkForCircularImports) throws IOException {
    for (SourceClass candidate : importCandidates) {
		if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
		   Class<?> candidateClass = candidate.loadClass();
            //这里用于实例化ImportSelector的实现类
		   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));
			}		
	    }
    }
}

上面也就是判断并实例化ImportSelector接口的实现类,而怎么识别的呢? importCandidates又是如何得到的?接着找。。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);
}
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
		Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
		collectImports(sourceClass, imports, visited);
		return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports,Set<SourceClass> visited) throws IOException {
	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
        imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

看到这里的判断也大体明白了吧。

这里就不做总结了,加个最近觉得很有道理的一句话,学习其实是个低成本,高回报的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值