04-Spring 初始化过程中GetBean方法分析

Spring 初始化过程中GetBean方法分析

经过前面的铺垫我们终于来到了IOC容器初始化最核心的部分(我个人认为)。在getBean时会调用AbstractBeanFactory#doGetBean()方法来获取单例Bean,在doGetBean中会先做一个缓存检查,判断是否之前手动插入到ioc,若存在缓存会根据缓存来拿Bean,暂不分析。
随后会判断当前Bean是否存在依赖,存在依赖时会先实例化依赖的Bean,此处暂不讨论,后续单独讨论。
不存在依赖的情况下,调用父类DefaultSingletonBeanRegistry#getSingleton方法,并且传入一个单例工厂,通过AbstractAutowireCapableBeanFactory#createBean方法来创建Bean,这个方法的注解中说明这是此类的核心方法,用来创建、填充Bean,并且调用BeanPostProcessor。关键代码如下所示

    /**
     * Central method of this class: creates a bean instance,
     * populates the bean instance, applies post-processors, etc.
     * @see #doCreateBean
     */
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        // 此处根据beanName将class load到jvm,并且给mbd的instanceclass字段赋值
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        ...........................................................
        // 此处真正创建对象
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    }

Spring中不管前面如何铺垫,最终执行操作的方法均为doXXX,所以,看到这个关键字我们就应该知道我们马上要解开面纱了。

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
        // 实例化一个空对象出来
        instanceWrapper = createBeanInstance(beanName, mbd, args);
        // 给对象的属性赋值
        populateBean(beanName, mbd, instanceWrapper);
        // 调用各种回调
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

上面只列出了doCreateBean中我认为最关键的三个方法,下面我们注意分析这三个方法。

首先我们来看实例化对象核心代码


    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }

    /**
     * Instantiate the given bean using its default constructor.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @return a BeanWrapper for the new instance
     */
    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
         // 通过实例化策略来实例化对象,默认策略是cglib,但是不存在Override方法的对象调用的实例化方法在父类SimpleInstantiationStrategy中
        beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
    }

在SimpleInstantiationStrategy#instantiate方法没什么特别的,基本就是通过bd获取class,再获取constructor,之后通过反射创建对象后返回。

其次我们来关注给对象的属性赋值

此方法会处理AutoWired注解的注入逻辑,实现依赖注入。具体核心代码如下

    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                  }
                pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                            return;
                }
           }
           pvs = pvsToUse;
       }
    }

如上所示,ioc容器会遍历已有的BeanPostProcessor,在遍历到AutoWiredAnnotationBeanPostProcessor(第一章介绍入口时registerBeanPostProcessors方法中提到了)时会实现自动装配。
可以参考AutoWiredAnnotationBeanPostProcessor类的官方注解

/**
 * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
 * that autowires annotated fields, setter methods and arbitrary config methods.
 * Such members to be injected are detected through a Java 5 annotation: by default,
 * Spring's {@link Autowired @Autowired} and {@link Value @Value} annotations.
 *
 * <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
 * if available, as a direct alternative to Spring's own {@code @Autowired}.
 */

从官方注解中可以看到,这个后置处理器的功能是为标有Spring的@AutoWired、@Value注解和JSR-330的@Injected的注解的属性赋值。另外其他的BeanPostProcessor也各有功能,后面有机会的话单独分析。我们先来关注自动装配模块。
通过跟源码我们发现,实际上的处理过程在DefaultListableBeanFactory#doResolveDependency()方法中,关键代码如下

    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

    /**
     * Find bean instances that match the required type.
     * Called during autowiring for the specified bean.
     * @param beanName the name of the bean that is about to be wired
     * @param requiredType the actual type of bean to look for
     * (may be an array component type or collection element type)
     * @param descriptor the descriptor of the dependency to resolve
     * @return a Map of candidate names and candidate instances that match
     * the required type (never {@code null})
     * @throws BeansException in case of errors
     * @see #autowireByType
     * @see #autowireConstructor
     */
    protected Map<String, Object> findAutowireCandidates(
            @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
        // 从ioc容器中根据bean类型来加载所有的单例(包括祖先类实例)
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        for (String candidate : candidateNames) {
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                // 这个方法里面会调用getBean方法从容器中获取待注入的对象
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        return result;
    }

通过上述代码会获取到需要注入的对象,后面要做的就是通过反射,将值赋给需要注入的字段。
需要注意的是,在跟踪源码的过程中发现@Autowired和@Inject注解其实都是按类型注入,而@Resource则是按名称注入,@Autowired注解是Spring实现的注解,如果需要注入的Bean不是单例而存在多个,比如说多数据源,此时需要配套@Qualified注解来使用。而@Inject是JSR-330标准的注解,在类似多数据源的情况下需要使用@Named来确定。另外,@Autowired注解支持required属性,如果该属性为false,则在容器中不存在对应类型时会注入为空,而@Inject注解不存在这个特性,除此之外@Auto wired和@Inject完全没区别

最后我们来关注调用回调

从方法注解上我们可以看到,在该阶段主要是负责调用工厂的各类回调(主要是BeanNameAware、BeanClassLoaderAware以及BeanFactoryAware),同时调用init方法(Spring在XML或@Bean的方式声明一个Bean时可以指定InitMethod和DestoryMethod)以及Bean的后置处理器(主要是BeanPostProcessor中的postProcessBeforeInitialization和postProcessAfterInitialization方法),下面我们通过代码来分析。

    /**
     * Initialize the given bean instance, applying factory callbacks
     * as well as init methods and bean post processors.
     * <p>Called from {@link #createBean} for traditionally defined beans,
     * and from {@link #initializeBean} for existing bean instances.
     * @param beanName the bean name in the factory (for debugging purposes)
     * @param bean the new bean instance we may need to initialize
     * @param mbd the bean definition that the bean was created with
     * (can also be {@code null}, if given an existing bean instance)
     * @return the initialized bean instance (potentially wrapped)
     * @see BeanNameAware
     * @see BeanClassLoaderAware
     * @see BeanFactoryAware
     * @see #applyBeanPostProcessorsBeforeInitialization
     * @see #invokeInitMethods
     * @see #applyBeanPostProcessorsAfterInitialization
     */
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        // 调用工厂的回调
        invokeAwareMethods(beanName, bean);
        // 调用后置处理器的beforeInitialization
        applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 调用InitializingBean#afterPropertiesSet或initMethod
        invokeInitMethods(beanName, wrappedBean, mbd);    
        // 调用后置处理器的afterInitialization
        applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

在applyBeanPostProcessorsBeforeInitialization方法中会调用ApplicationContext级别的相关回调,包括EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
在Spring MVC中会通过invokeInitMethods方法来调用InitializingBean#afterPropertiesSet方法实例化RequestMappingHandlerMapping等信息。后面在分析Spring MVC源码时在单独分析。
而applyBeanPostProcessorsAfterInitialization方法大多都是直接返回bean,Spring保留了用户扩展的需求。
至此Spring容器的getBean就已经介绍完了。结束这一步基本上Spring容器的启动过程也就快结束了。通过分析我们了解了很多Spring的原理,更重要的是开始慢慢了解Spring的设计思想,相信这些思想能让我们更好的编码,写出更好、更优雅的code。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容