Springboot中@Import的使用原理

共 3071字,需浏览 7分钟

 ·

2020-11-19 18:31

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

66套java从入门到精通实战课程分享

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

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

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


  org.springframework.boot
  spring-boot-starter-parent
  1.5.6.RELEASE
 

 
  
   org.springframework.boot
   spring-boot-starter-web
  

 

 
  
   
    org.springframework.boot
    spring-boot-maven-plugin
   

  

 


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 beanFactoryPostProcessors) {
     invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
}
 
private static void invokeBeanDefinitionRegistryPostProcessors(
   Collection 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 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 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 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 getImports(SourceClass sourceClass) throws IOException {
  Set imports = new LinkedHashSet();
  Set visited = new LinkedHashSet();
  collectImports(sourceClass, imports, visited);
  return imports;
}
private void collectImports(SourceClass sourceClass, Set imports,Set 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"));
 }
}

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

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



版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:

https://blog.csdn.net/qq_21299835/article/details/109695279





粉丝福利:实战springboot+CAS单点登录系统视频教程免费领取

???

?长按上方微信二维码 2 秒
即可获取资料



感谢点赞支持下哈 

浏览 25
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报