码迷,mamicode.com
首页 > 其他好文 > 详细

使用@import导入实现了ImportBeanDefinitionRegistrar接口的类,不能被注册为bean

时间:2017-12-08 22:54:42      阅读:3504      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!