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.  

看到這裏的判斷也大致明白了吧。

這裏就不作總結了,加個最近以爲頗有道理的一句話,學習實際上是個低成本,高回報的方式。

相關文章
相關標籤/搜索