Springboot中@Import的使用原理

最近我看了看Springboot的源码,借着周末分享@Import这一小知识点。java

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

1.建立一个maven项目,pom.xml文件配置以下:spring

<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>
  1.  

2.建立一个实体类app

package com.entity;
 
public class User implements Serializable{
 
 private String name;
 private String age;
 
 public User() {
  this.name = "xiaochen";
  this.age = "6";
 }
}
  1.  

3.建立ImportUser类maven

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

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

4.建立配置类spring-boot

package com.demo;
 
@Import(ImportUser.class)
@Configuration
public class ImportConfiguration {}
  1.  

5.建立启动类post

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);
 }
}
  1.  

6,运行结果学习

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

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

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

EnableAutoConfigurationImportSelector类,代码以下:

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

 这里咱们先建立一个Springboot启动类

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

 点击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());
  1.  

到此再点击进入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);
 }
}
  1.  

再点击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);
}
  1.  

这里再点击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);
}
  1.  

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

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

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

但这里会不会有个疑问,@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();
}
}
  1.  

代码中能够看出是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));
   }  
     }
    }
}
  1.  

上面也就是判断并实例化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"));
 }
}
  1.  

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

这里就不作总结了,加个最近以为颇有道理的一句话,学习实际上是个低成本,高回报的方式。

相关文章
相关标签/搜索