merged:什么是MergedBeanDefinition?

前言

MergedBeanDefinition是spring获取bean流程中的一个重要处理过程,他将基础的BeanDefinition合并为一个新的BeanDefinition对象(RootBeanDefinition),后续的处理过程都依赖这个新对象,

了解他有助于我们控制和管理bean实例化之前的行为。

在Spring 内部实际使用的BeanDefinition,其实都是合并后RootBeanDefinition对象,通过它进行

对象的实例化,注入等。我们需要关注这个过程,这将决定Bean的产生。

知识铺垫

什么是Spring BeanDefinition?

什么是Spring BeanDefinition我就不解释了。这属于Spring的常识知识!

Spring有哪些BeanDefinition?他们有什么区别,以下列出部分重要的BeanDefinition

ConfigurationClassBeanDefinition(@Bean标注的产生)

AnnotatedGenericBeanDefinition(@Configuration标注的)

ScannedGenericBeanDefinition(通过@ComponentScan扫描或扫描行为产生的,注:理论上你实现自己的扫描行为,那么你可以自主选择具体的BeanDefinition实现类。)

RootBeanDefinition(Spring最终使用的BeanDefinition,其他BeanDefinition都会转换为它)

MergedBeanDefinition过程解析

//AbstractBeanFactory.javaprotected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException { //以上省略若干行 //获取合并的Beandefinition,实际是产生一个RootBeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); //检查RootBeanDefinition的有效性,后续的代码会使用这个产生的RootBeanDefinition checkMergedBeanDefinition(mbd, beanName, args); //以下省略若干行}protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Quick check on the concurrent map first, with minimal locking. RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null && !mbd.stale) {return mbd; }return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));}protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)throws BeanDefinitionStoreException {return getMergedBeanDefinition(beanName, bd, null);}protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)throws BeanDefinitionStoreException { //辗转反侧最后调用的是这个方法,具体代码太多,省略 //你只需要最终各种操作之后得到了RootBeanDefinition }

解析了这么多源码,只明白了,原来原始的BeanDefinition最终会转换为RootBeanDefinition,奇怪的知识增加了,但是好像觉得没有什么用,这个时候可以提出一个需求,造出一个场景,让这段知识排上用场。

假设,现在有一个接口,他有多个实现类,并且都没有指定@Primary,在注入接口的类也没有指定@Qualifier限定操作,如何动态选择一个实例的注入到Bean?

应用场景 

通过源码观察,Spring提供了一个可供操作合并后RootBeanDefinition的扩展点(后置处理器),通过这个扩展点,可以实现对RootBeanDefinition的操作

实现上述假设:

public interface Say { String tell();}@Componentpublic class CatSay implements Say { @Override public String tell() { return "cat"; }}@Componentpublic class DogSay implements Say { @Override public String tell() { return "dog"; }}@Componentpublic class Zoo{ //注入,如果不做一些介入,那么必然是报错的 @Autowired private Say say;}public static void main( String[] args ){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); Zoo zoo = context.getBean(Zoo.class);}//为了不报错,比如引入如下操作@Componentpublic class MergedBeanDefinitionPostProcess implements MergedBeanDefinitionPostProcessor, ApplicationContextAware { private ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { if(CatSay.class.equals(beanType)){ /** * 动态修改RootBeanDefinition的值,例如本例中确认一个Primary 为 true * 这样当spring 查找到多个候选bean的时候,能够依据它的优先级来选的最终的bean,这样不至于报错 */ beanDefinition.setPrimary(true); ConfigurableListableBeanFactory app = (ConfigurableListableBeanFactory) this.applicationContext.getAutowireCapableBeanFactory(); BeanDefinition definition = app.getBeanDefinition(beanName); definition.setPrimary(true); /** * 强烈建议修改RootBeanDefinition值时,同步求改原始BeanDefinition的值。同步他们的值 * getMergedBeanDefinition方法为获取合并后的RootBeanDefinition * getBeanDefinition方法为获取原始的BeanDefinition * 他们都定义在ConfigurableListableBeanFactory 接口上 */ } }}

总结

Spring的MergedBeanDefinition过程非常重要,它揭示了Spring最终是如何运用Bean元数据产生Bean的,通过例子的引申,我们可以通过 MergedBeanDefinitionPostProcessor接口做很多有意思的事情,它是改变Bean元数据的一个重要窗口。

其实不止这一个窗口,BeanDefinitionRegistryPostProcessor接口也可以改变BeanDefinition,不过他发生的时机是原始BeanDefinition构建完毕,所以只能改变原始的BeanDefinition。

最后,如果你想动态改变BeanDefinition,控制Bean的行为,可以用如下2个扩展点

扩展点用途
BeanDefinitionRegistryPostProcessor可以更改原始BeanDefinition
MergedBeanDefinitionPostProcessor可以更改RootBeanDefinition

相关推荐

相关文章