zoukankan      html  css  js  c++  java
  • @Import底层实现原理

    日常项目中,使用注解@EnableAspectJAutoProxy @EnableAsync

    这里面涉及对@Import注解支撑的底层原理:ConfigurationClassPostProcessor 这个类,说到这个类,我们要先从SpringBoot启动流程说起。

    首先,看springboot启动流程中的一步:

    SpringApplication对象的run方法,创建上下文context = createApplicationContext(); 这一步,会创建AnnotationConfigServletWebServerApplicationContext对象:

    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

     类图关系如下:

     AnnotationConfigServletWebServerApplicationContext类有两个重要的属性:private final AnnotatedBeanDefinitionReader reader;  private final ClassPathBeanDefinitionScanner scanner;

    创建AnnotationConfigServletWebServerApplicationContext对象时, 调用构造方法,会初始化该对象的上面两个属性。见:

    public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    new AnnotatedBeanDefinitionReader(this)对象时,里面会涉及AnnotatedBeanDefinitionReader构造函数AnnotatedBeanDefinitionReader构造函数AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);其中会对spring内置管理的几个特殊类封装成BeanDefinition,缓存到map中;其中就包含对ConfigurationClassPostProcessor的处理:该类实现了BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor,重写了 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法与postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法

    然后,看springboot启动流程中的另外一步:
    SpringApplication对象的run方法,加载spring容器refreshContext(context);定位到AbstractApplicationContext类的refresh()方法,方法内部流程中有一步invokeBeanFactoryPostProcessors(beanFactory);
    内部通过 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 其中最重要的一步是:String[] postProcessorNames = 
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 遍历 然后currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));这里会对所有BeanDefinitionRegistryPostProcessor的实现类进行getBean实例化的操作(ConfigurationClassPostProcessor在这里得到实例化
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 遍历调用每一个BeanDefinitionRegistryPostProcessor的实现类的postProcessBeanDefinitionRegistry方法,所以ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法会得到调用
    下面看ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:
    processConfigBeanDefinitions(registry); 内部会构建一个ConfigurationClassParser对象 Parse each @Configuration class 通过parser.parse(candidates);定位到processConfigurationClass(ConfigurationClass configClass);再到doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) ;这里会处理 @PropertySource, @ComponentScan@Import@ImportResource@Bean 及  Process default methods on interfaces;// Process any @Import annotations ---> processImports(configClass, sourceClass, getImports(sourceClass), true); 该方法里会有对@Import注解3种方式ImportSelector实现类和ImportBeanDefinitionRegistrar实现类以及未实现这些接口的类的处理;
    if(实现ImportBeanDefinitionRegistrar){
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());后面的ConfigurationClassPostProcessoorl类的processConfigBeanDefinitions方法中的this.reader.loadBeanDefinitions(configClasses)会调用这些ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法
    }else if(实现ImportSelector){
    String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 第1步
    Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
    processImports(configClass, currentSourceClass, importSourceClasses, false); // 第2步
    这里涉及到递归调用,第一次进来时,走到第1步,拿到importClassNames执行第2步时,会跳转到处理没有实现ImportBeanDefinitionRegistrar和ImportSelector这些接口的普通bean了
    }else{
      //对没有实现ImportBeanDefinitionRegistrar和ImportSelector这些接口的普通bean了
      processConfigurationClass(candidate.asConfigClass(configClass));
    }
    processImports方法执行完毕,Import注解导入的bean都被保存在ConfigurationClassParser实例中,回到processConfigBeanDefinitions(registry),parse之后,this.reader.loadBeanDefinitions(configClasses);// parse那里准备好了bean信息,这里是真正的处理;ConfigurationClassBeanDefinitionReader类中是对每个配置类逐个执行loadBeanDefinitionsForConfigurationClass方法,
    // 普通的类,通过下面的方法将bean定义注册在spring环境
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 实现了ImportBeanDefinitionRegistrar接口的实例,会执行
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
     
    到此,@Import注解分析完成了,后续接着优化~
     
     
     
     
  • 相关阅读:
    linux下修改Mysql的字符编码方式
    创建XMPP工程步骤
    ClickOnce清单签名取消后依然读取证书的问题
    FxCop卸载后依然生成文件夹的问题
    使用了旧版nuget的.net项目在git中的问题
    CorelDraw X8 破解激活问题
    ASUS T100TA 换屏要记
    百度SMS SDK for .Net
    网易闪电邮
    《The Practice and Theory of Bolshevism》的笔记-第114页
  • 原文地址:https://www.cnblogs.com/kobe-lin/p/14225822.html
Copyright © 2011-2022 走看看