深入浅出Spring原理及实战「源码调试分析」结合DataSourceRegister深入分析ImportBeanDefinitionRegistrar的源码运作流程

Posted 洛神灬殇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出Spring原理及实战「源码调试分析」结合DataSourceRegister深入分析ImportBeanDefinitionRegistrar的源码运作流程相关的知识,希望对你有一定的参考价值。

每日一句

人的一生中不可能会一帆风顺,总会遇到一些挫折,当你对生活失去了信心的时候,仔细的看一看、好好回想一下你所遇到的最美好的事情吧,那会让你感觉到生活的美好


注入案例代码

如何通过实现SpringBoot框架带有的ImportBeanDefinitionRegistrar注册器,注入我们想要注册的bean对象实例。只需要采用@Import的注解进行注入对应的一类相关的bean对象。

@Import(DataSourceRegister.class,A.class)
@SpringBootApplication
@ComponentScan("com.libo")
public class LiboApplication 
    public static void main(String[] args) 
        SpringApplication sa = new SpringApplication(LiboApplication.class);
        sa.run(args);
    

DataSourceRegister的开发实现

  • 在springboot启动的时候,loader模块会根据“清单文件”加载该Application类,并反射调用psvm入口函数main,@Import注解也可以导入一个常规类,并且创建注入很多对象实例

  • DataSourceRegister类是用来进行初始化数据源和并提供了执行动态切换数据源的工具类。

DataSourceRegister注入主数据源和从数据源

这里DataSourceRegister继承的EnvironmentAware接口,没有真正意义上去用它的用途,本身可以通过这个setEnvironment方法,进行注入Environment对象,从而可以读取其他的配置信息,目前主要用作一个hook方法。

读取对应的环境变量

public final void setEnvironment(Environment environment) 
        DruidEntity druidEntity = FileUtil.
			readYmlByClassPath("db_info", DruidEntity.class);
        defaultTargetDataSource =
			DataSourceUtil.createMainDataSource(druidEntity);
    

主要用于读取Druid的数据源模型信息。进行创建对应的数据源对象defaultTargetDataSource

注入Bean到Spring容器中

registerBeanDefinitions注册BeanDefinition对象模型

    public final void registerBeanDefinitions(AnnotationMetadata  annotationMetadata, BeanDefinitionRegistry  beanDefinitionRegistry) 
        // 0.将主数据源添加到数据源集合中
        DataSourceSet.putTargetDataSourcesMap(MAINDATASOURCE,defaultTargetDataSource);
        //1.创建DataSourceBean
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        //spring名称约定为defaultTargetDataSource和targetDataSources
        mpv.addPropertyValue("defaultTargetDataSource",defaultTargetDataSource);
        mpv.addPropertyValue("targetDataSources",DataSourceSet.getTargetDataSourcesMap());
        beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
    

完整的DataSourceRegister的案例

public class DataSourceRegister<T> implements EnvironmentAware, ImportBeanDefinitionRegistrar 

    private javax.sql.DataSource defaultTargetDataSource;

    static final String MAINDATASOURCE = "mainDataSource";

    public final void setEnvironment(Environment environment) 
        DruidEntity druidEntity = FileUtil.
			readYmlByClassPath("db_info", DruidEntity.class);
        defaultTargetDataSource =
			DataSourceUtil.createMainDataSource(druidEntity);
    

    public final void registerBeanDefinitions(AnnotationMetadata 
											  annotationMetadata, BeanDefinitionRegistry 
											  beanDefinitionRegistry) 
        // 0.将主数据源添加到数据源集合中
        DataSourceSet.putTargetDataSourcesMap(MAINDATASOURCE,
		defaultTargetDataSource);
        //1.创建DataSourceBean
        GenericBeanDefinition beanDefinition = new
			GenericBeanDefinition();
        beanDefinition.setBeanClass(DataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        //spring名称约定为defaultTargetDataSource和targetDataSources
        mpv.addPropertyValue("defaultTargetDataSource",
		defaultTargetDataSource);
        mpv.addPropertyValue("targetDataSources",
		DataSourceSet.getTargetDataSourcesMap());
        beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
    

执行流程解读

  • 动态数据源注册器类实现了ImportBeanDefinitionRegistrar接口,没错就是这个原因,由于实现了该接口让该类成为了拥有注册bean的能力。

  • 原理上也能说得通作为一个Bean的注册类是没有必要被注册为Spring容器的Bean对象

  • 虽然这样解释也不为过但我仍然想一探究竟,本来想大概找找spring涉及关键类如:ConfigurationClass,ConfigurationClassParser等,接下来我们需要看一下SpringBoot的总体加载流程。

SpringApplication类的run方法

SpringBoot启动时使用了SpringApplication类的run方法来牵引整个spring的初始化过程,源码如下。

public ConfigurableApplicationContext run(String... args) 
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
	   // 错误原因分析器
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
	   // 重点分析:启动所有的运行的监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();
        try 
			// 解析ApplicationArgument数据
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 通过监听器和传递的参数,实现相关的配置环境对象信息,预先
			// 进行配置Environment对象,设置全局环境变量容器
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
			// 输出相关的Banner控制,根据配置以及相关的Environment
            Banner printedBanner = this.printBanner(environment);
			// 创建Spring容器上下文。
            context = this.createApplicationContext();
			//创建和赋值错误解析器
            analyzers = new FailureAnalyzers(context);
			// 准备环境上下文进行配置相关的容器的上下文的参数。
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 刷新上下文容器
            this.refreshContext(context);
			// 后置执行上下文操作
            this.afterRefresh(context, applicationArguments);
			// 完成监听器的后置完成处理操作
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();
            if(this.logStartupInfo) 
                (new StartupInfoLogger(this.mainApplicationClass)).
					logStarted(this.getApplicationLog(), stopWatch);
            
            return context;
         catch (Throwable var9) 
            this.handleRunFailure(context, listeners, 
								  (FailureAnalyzers)analyzers, 
								  var9);
            throw new IllegalStateException(var9);
        
    

根据上面的源码流程可以分析重点的加载过程

  1. 重点分析:启动所有的运行的监听器,获取SpringApplicationRunListeners; 从类路径下META-INF/spring.factories。回调所有的获取SpringApplicationRunListener.starting() 方法。
 SpringApplicationRunListeners listeners = this.getRunListeners(args);
 listeners.starting();
  1. 解析ApplicationArgument数据以及封装命令行参数,通过program参数的部分进行解析,并且加载到PropertiesSourcePlaceHolder中的环境变量容器内部。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

通过上面的监听器和传递的参数,实现相关的配置环境对象信息,预先进行配置Environment对象,设置全局环境变量容器

  1. 根据上面的全局变量容器进行配置以及初始化相关的Environment对象。
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
  1. 创建Spring容器上下文,在createApplicationContext当中,由于我们是web项目,则spring默认给我们创建了一个
context = this.createApplicationContext();

AnnotationConfigEmbeddedWebApplicationContext,当然它也是继承GenericWebApplicationContext类和GenericApplicationContext类的,那么他默认会持有一个DefaultListableBeanFactory对象,这个对象可以用来创建Bean。

  1. 准备环境上下文进行配置相关的容器的上下文的参数
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  1. 刷新上下文容器,refreshContext就是在做spring运行后的初始化工作。
this.refreshContext(context);

接着往下走,进入refreshContext中会调用一系列的refresh方法,最终进入AbstractApplicationContext中,主要将SpringBoot的容器对象数据和原本基础的Spring Framework的框架对象进行加载到容器中。

   @Override
    public void refresh() throws BeansException, IllegalStateException 
		synchronized (this.startupShutdownMonitor) 
			// Prepare this context for refreshing.
            prepareRefresh();
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory =
				obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
            try 
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            

            catch (BeansException ex) 
                if (logger.isWarnEnabled()) 
                    logger.warn("Exception encountered during context 
								initialization - " +"cancelling refresh attempt: " + ex);
                

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            
            finally 
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            
        
    

invokeBeanFactoryPostProcessors() 方法就是Bean在注册前期做的一系列数据收集工作,BeanDefinitionRegistry的容器注册BeanDefinition之前,调用相关的

跟着堆栈继续深入,会进入到这个方法中,这个方法就是初始化bean前的所有轨迹:

在invokeBeanFactoryPostProcessors方法中继续跟进一系列方法就会看到在一开始的时候spring会初始化几个系统固有的Bean

继续调试后的关键点出现在这个方法中:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) 
        List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
        String[] candidateNames = registry.getBeanDefinitionNames();
        for (String beanName : candidateNames) 
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) 
                if (logger.isDebugEnabled()) 
                    logger.debug("Bean definition has already been processed 
								 as a configuration class: " + beanDef);
                
            
            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) 
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            
        
        // Return immediately if no @Configuration classes were found
        if (configCandidates.isEmpty()) 
            return;
        
        // Sort by previously determined @Order value, if applicable
        Collections.sort(configCandidates, new 
						 Comparator<BeanDefinitionHolder>() 
            @Override
            public int compare(BeanDefinitionHolder bd1, 
							   BeanDefinitionHolder bd2) 
                int i1 = 
					ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = 
					ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
            
        );
        // Detect any custom bean name generation strategy supplied through the enclosing application context
        SingletonBeanRegistry sbr = null;
        if (registry instanceof SingletonBeanRegistry) 
            sbr = (SingletonBeanRegistry) registry;
            if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) 
                BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            
        
        // Parse each @Configuration class
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
        Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
        do 
            parser.parse(candidates);
            parser.validate();
            Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);
            // Read the model and create bean definitions based on its content
            if (this.reader == null) 
                this.reader = new ConfigurationClassBeanDefinitionReader(
                        registry, this.sourceExtractor, this.resourceLoader, this.environment,
                        this.importBeanNameGenerator, parser.getImportRegistry());
            
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);
            candidates.clear();
            if (registry.getBeanDefinitionCount() > candidateNames.length) 
                String[] newCandidateNames = registry.getBeanDefinitionNames();
                Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
                Set<String> alreadyParsedClasses = new HashSet<String>();
                for (ConfigurationClass configurationClass : alreadyParsed) 
                  alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                
                for (String candidateName : newCandidateNames) 
                    if (!oldCandidateNames.contains(candidateName)) 
                        BeanDefinition bd = registry.getBeanDefinition(candidateName);
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                !alreadyParsedClasses.contains(bd.getBeanClassName())) 
                            candidates.add(new BeanDefinitionHolder(bd, candidateName));
                        
                    
                
                candidateNames = newCandidateNames;
            
        
        while (!candidates.isEmpty());

        // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
        if (sbr != null) 
            if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) 
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry(深入浅出Spring原理及实战「源码原理实战」从底层角度去分析研究PropertySourcesPlaceholderConfigurer的原理及实战注入机制

深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析IOC容器核心流程以及运作原理

深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析MVC容器核心流程以及运作原理

深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析AOP容器核心流程以及运作原理

深入浅出Spring原理及实战「开发实战系列」分析探究RedisTemplate的序列化和反序列化+泛型机制

深入浅出Spring原理及实战「开发实战系列」SpringSecurity原理以及实战认证分析开发指南