标签:adl 日志 cbe col tar efault sync invoke 名称
今天在调试公司spring项目的时候发现了这样一个问题,由于我们的项目使用的是springboot就以springboot为例,代码如下:
@Import({DataSourceRegister.class,A.class}) @SpringBootApplication @ComponentScan("com.viewhigh.bi") //@EnableCaching public class BiApplication { public static void main(String[] args) { LogUtil.setEnabled(true);//开启日志输出 SpringApplication sa = new SpringApplication(BiApplication.class); sa.setBannerMode(Banner.Mode.LOG); sa.run(args); } }
在springboot启动的时候,loder模块会根据“清单文件”加载该BIApplication类,并反射调用psvm入口函数main,但是一个很有意思的问题出现了,项目正常运行之后,在springcontext中可以找到Bean类A,但是无法找到DataSourceRegister这个类;
我们知道在spring4.2以后@Import注解也可以导入一个常规类,并将其注册为bean,那么为什么DataSourceRegister没有被注册为Bean呢?
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的注册类是没有必要和A类一样都被注册为Bean的!
虽然这样解释也不为过但我仍然想一探究竟,本来想大概找找spring涉及关键类如:ConfigurationClass,ConfigurationClassParser等,可能由于不熟悉看到类中的代码就呵呵了,似乎无从下手!
所以准备调试下spring启动部分的代码 ,这样会更清晰些!(以spring boot V1.5.6为例)
======
spring boot启动时使用了SpringApplication类的run方法来牵引整个spring的初始化过程!!!
没错了就是从run开始吧!
代码如下:
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 { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); 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); } }
手一懒,就把整个方法直接copy了。这就是springboot在启动时的完整足迹。。。闲话少说我们直击关键点;
context = this.createApplicationContext(); this.refreshContext(context);
这两段是最关键的地方,阅读过一些spring书籍的兄弟都知道refreshContext就是在做spring运行后的初始化工作。那么在createApplicationContext当中,由于我们是web项目,则spring默认给我们创建了一个AnnotationConfigEmbeddedWebApplicationContext
当然它也是继承GenericWebApplicationContext类和GenericApplicationContext类的,那么他默认会持有一个DefaultListableBeanFactory对象,这个对象可以用来创建Bean,吼吼,这个块说的似乎没有意义哈!!!
接着往下走,进入refreshContext中会调用一系列的refresh方法,最终进入AbstractApplicationContext中:
@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在注册前期做的一系列数据收集工作!
跟着堆栈继续深入,会进入到这个方法中,这个方法就是初始化bean前的所有轨迹:
在invokeBeanFactoryPostProcessors方法中继续跟进一系列方法就会看到在一开始的时候spring会初始化几个系统固有的Bean:
使用@import导入实现了ImportBeanDefinitionRegistrar接口的类,不能被注册为bean
标签:adl 日志 cbe col tar efault sync invoke 名称
原文地址:http://www.cnblogs.com/zzq-include/p/8004506.html