最近我看了看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"));
}
}
看到这里的判断也大体明白了吧。
这里就不做总结了,加个最近觉得很有道理的一句话,学习其实是个低成本,高回报的方式。