一、问题描述
刚一看标题相信大家都有点懵逼,具体问题是这样的:今天一个群友在群里贴出了一个他遇到的问题,他在使用spring的时候,配置方式是java方式配置,其中的一个配置类大概如下:他这个时候发现environment没有被注入进来,然后他把MapperScannerConfigurer的定义去掉后发现,environment可以正常被注入。看到他提出的这个问题后,我写了一个demo测试了一下,果然如他描述,当配置类中定义了MapperScannerConfigurer,这个配置类中Environment是注入不进来的。因为平时喜欢看spring的源码,并且对spring的原理有一定的理解,所以我打算帮他找出导致这个问题的原因。
二、初步分析
为什么加了MapperScannerConfigurer的定义就会出现这种注入不了的问题?MapperScannerConfigurer到底有什么特殊之处,我点开了MapperScannerConfigurer的源码,看了一下:
这个时候我一眼看到了MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,XXXPostProcessor是spring生命周期中提供的各类接口,这里我贴出MapperScannerConfigurer的类图,看一下MapperScannerConfigurer的具体继承关系:
原来BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的一个子接口,BeanFactoryPostProcessor这个接口的作用是:spring在所有的Bean的定义都被解析完BeanDefinition后,调用所有实现了BeanFactoryPostProcessor接口的类的postProcessBeanFactory方法,来给用户提供最后一个改变BeanDefinition的机会。到这里,我们可以大概有了一个分析的方向。(关于Spring的生命周期,请参考我之前的一篇文章:《BeanPostProcessor和BeanFactoryProcessor浅析》)
三、详细分析
我们先从springboot的启动入口开始分析:SpringBoot项目的启动入口为SpringApplication的run方法,我们一步一步进入可以发现最终执行的是SpringApplication的public ConfigurableApplicationContext run(String... args)
这个方法。我们把代码拿出来看一下:
由于本文的重点是排查问题,所以不会详细解释整个启动过程的源码,这里只简单的说一下每一步spring做了什么操作,有兴趣的同学可以自己查阅源码查看细节。
首先,我们可以看到environment初始化和赋值在applicationContext创建的时候就已经完成了,如图中1标记处,environment初始化完成。所以这里可以排除引起标题中说道的问题的一个可能性:MapperScannerConfigurer初始化先于environment。
接着,图中2标记处,创建了applicationContext对象,这里只是创建了一个空的applicationContext对象。
然后,图中3标记处,prepareContext()方法向applicationContext中设置了一些定义性的属性:如设置environment、设置applicationContext的配置定义入口XXApplication(就是我们spring项目中的带main方法的那个入口类)。
最后就是图中标记的第四步,这里是整个springboot项目启动的核心方法。在这一步,spring会加载bean的定义,执行对外开放的生命周期接口。我们进入这个方法,会发现这个方法最后是执行applicationContext的refresh方法。在之前的文章中我们分析过applicationContext的refresh方法。这里,我们以AbstractApplicationContext的refresh方法为例,结合现在的问题再次分析一下这个方法:
图中1处,做刷新applicationContext的准备,这里可以不用特别关心。
图中2处,获取当前applicationContext持有的BeanFactory对象。
图中3处,prepareBeanFactory方法给BeanFactory设置了一些属性,我们也可以忽略。
图中4处,postProcessBeanFactory方法,是一个空方法,提供给子类重写,来作为一个开放点。
图中5处,是本篇文章的关键处,invokeBeanFactoryPostProcessors是干什么的呢?这个方法的作用是执行所有的BeanFactoryPostProcessor接口的postProcessBeanFactory方法。这里有一点我们需要知道,首先不同的Application的子类在图2处的获取BeanFactory的方法处有不同的实现逻辑,有的子类在obtainFreshBeanFactory这个方法中就已经加载了所有的Bean的定义,有的方法没有在此加载Bean的定义,而是将加载Bean的定义放在invokeBeanFactoryPostProcessors中,通过ConfigurationClassPostProcessor这个BeanFactoryPostProcessor的实现类来进行Bean定义的读取。不过不管Bean定义的读取发生在何处,可以确定的是在Bean定义读取完之后会执行所有的BeanFactoryPostProcessor接口提供的生命周期方法。
所以问题就来了,我们把MapperScannerConfigurer定义在我们自己的JAVA配置类中,如果要在图中5处,也就是invokeBeanFactoryPostProcessors方法中被执行,就必须在此之前创建我们的JAVA配置类的实例,也就是我们的栗子中的TestConfig。这个也就是出现我们标题中的问题的关键所在!!!我们要知道这样一个事情:@Autowired注入属性,是通过BeanPostProcessor的一个子类AutowiredAnnotationBeanPostProcessor来实现的,看过之前的《BeanPostProcessor和BeanFactoryProcessor浅析》这篇文章的朋友会知道,BeanPostProcessor这个Spring生命周期中开放的接口是在Bean实例化的时候调用的(postProcessBeforeInitialization方法是在所有的bean的InitializingBean的afterPropertiesSet方法之前执行而postProcessAfterInitialization方法则是在所有的bean的InitializingBean的afterPropertiesSet方法之后执行的),而我们从图中6处可以看到,BeanPostProcessor的注册是发生在BeanFactoryPostProcessor接口被调用之后。而我们的例子中,MapperScannerConfigurer这个类是BeanFactoryPostProcessor的子类,如果想要被创建,并且被应用到图中5处的方法中,就必须先创建TestConfig的实例,而这个时候,BeanPostProcessor接口是没有被注册的,所以这个时候,TestConfig的实例想要通过@Autowired来注入属性对象是不可能的。
四、问题解决
那么问题来了,如果我们在这种情况下想要使用Environment怎么办?这里我先把答案给出来:让配置类实现EnvironmentAware接口,XXXAware也是Spring生命周期中提供的一系列接口,作用是注入一些spring的关键对象,比如说ApplicationContext、Environment等对象。注入的原理也是通过BeanPostProcessor的一个子类ApplicationContextAwareProcessor实现的,在bean被创建的时候如果发现这个bean实现了Aware接口,就调用Aware提供的对应的注入方法,我们可以看一下具体代码:
细心的朋友也许会问,BeanPostProcessor不是在图中6出注册的吗?应该不会被调用呀。其实是这样的ApplicationContextAwareProcessor作为Spring内部的一个比较重要的类,早在图中3处prepareBeanFactory这个方法中注册了:
那么问题又来了,这个时候如果我想在例子中的TestConfig配置类中注入其他普通的bean可以吗?答案是不可以!!!我们只能通过实现Aware接口这种手段注入一些Spring给我们提供的特殊对象。或者通过ApplicationContextAware注入applicationContext,从applicationContext中获取需要的bean,但是要注意,尽管applicationContext可以被注入进来,但是在类似MapperScannerConfigurer这种实现了BeanPostProcessor的类的定义方法中,通过applicationContext获取的对象是有问题的,这些对象如果有通过@Autowired注入属性对象,这些属性对象都将是空,甚至直到Spring容器初始化完成之后,这些属性也都是空,所以通过这种方法获取对象,带来的影响太恶劣了,有些得不偿失!
今天的分析就到这里,希望大家能通过本篇,对Spring的生命周期有更深的理解!