1. 程式人生 > >Spring原始碼解析-getBean如何獲取Ioc容器中的bean

Spring原始碼解析-getBean如何獲取Ioc容器中的bean

在Spring框架中,主要是要IOC容器和AOP切面兩塊,然而從IOC容器中如何獲取bean也對我們而言也是可以來學習解析的。

一、解讀getBean的背景

  在工作中,一次通過從IOC容器中獲取通過繼承FactoryBean介面的類,發現在對注入後的類名新增&與不新增&的區別很大,在新增&的情況下,獲取的是當前繼承FactoryBean介面的類;在不新增&的情況下,獲取的是當前繼承FactoryBean介面的類中通過getObject()返回的類物件。
  因此引起了我對getBean的原始碼探討,看看Spring框架中究竟是如何來獲取繼承FactoryBean介面的類的bean。

二、簡單程式碼示例

  由於工作中的程式碼太過繁瑣,同時也涉及到一些非必要的東西(呵呵,大家應該懂得),所以我通過簡單的示例來通過繼承FactoryBean介面完成IOC的注入,然後在後面的原始碼解讀中來理解getBean方法(本篇文章主要是講解getBean方法,由於getBean依賴通過繼承FactoryBean介面完成IOC的注入的bean來獲取,所以就在這部分來簡單示例)。

1、pojo類
作為後面注入到IOC容器,需要通過getBean獲取的物件

package com.cyw.demo.bean;

public class Girl {

}

2、基於FactoryBean介面繼承的類


通過繼承FactoryBean介面,實現getObject()獲取Girl的物件。

package com.cyw.demo.config;

import org.springframework.beans.factory.FactoryBean;

import com.cyw.demo.bean.Girl;

public class DemoFactoryBean implements FactoryBean<Girl> {

    public Girl getObject() throws Exception {
        return new Girl();
    }

    public
Class<?> getObjectType() { return Girl.class; } }

3、配置類
通過配置來注入DemoFactoryBean類。

package com.cyw.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoConfig {
    @Bean
    public DemoFactoryBean demoFactoryBean() {
        return new DemoFactoryBean();
    }
}

4、測試類

package maven_spring;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.cyw.demo.config.DemoConfig;

public class DemoTest {
    @Test
    public void test() {
        ApplicationContext app = new AnnotationConfigApplicationContext(DemoConfig.class);

        Object bean1 = app.getBean("demoFactoryBean");
        System.out.println(bean1);

        Object bean2 = app.getBean("&demoFactoryBean");
        System.out.println(bean2);
    }
}

通過只是test()方法,輸出bean1和bean2變數的值。
這裡寫圖片描述
發現bean1是Gril的例項物件,bean2是DemoFactoryBean的例項物件。

三、getBean()的原始碼解析

  現在應該是激動人心的時刻了,因為研究完getBean()的原始碼,就能很清楚的認識到上述示例中為什麼在有沒有新增&,輸出的兩個物件是不同了。
1、先上時序圖
原始碼是基於5.0.6.RELEASE版本,大家可以隨自己的意願來選擇
這裡寫圖片描述

2、解析時序圖
1)client為我們訪問IOC容器,獲取Bean

public class DemoTest {
    @Test
    public void test() {
        ApplicationContext app = new AnnotationConfigApplicationContext(DemoConfig.class);

        Object bean1 = app.getBean("demoFactoryBean");
        System.out.println(bean1);

        Object bean2 = app.getBean("&demoFactoryBean");
        System.out.println(bean2);
    }
}

2)通過getBean來進入呼叫的類AbstractBeanFactory.class
此處的原始碼只基於Object bean2 = app.getBean(“&demoFactoryBean”);來講解,如果大家有興趣,可以研究下Object bean1 = app.getBean(“demoFactoryBean”);
現在繼續進入正題:
在AbstractBeanFactory.class類中getBean方法

@Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

3)doGetBean(name, null, null, false);方法
呼叫 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

4)進入AbstractAutowireCapableBeanFactory.class

@Override
    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        String currentlyCreatedBean = this.currentlyCreatedBean.get();
        if (currentlyCreatedBean != null) {
            registerDependentBean(beanName, currentlyCreatedBean);
        }

        return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
    }

5)調會AbstractBeanFactory.class的getObjectForBeanInstance方法
主要看這段程式碼 判斷當前類是否繼承FactoryBean或者beanName是否有&字首

if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

6)isFactoryDereference在BeanFactoryUtils.class類中,判斷當前beanName是否有&字首

public static boolean isFactoryDereference(@Nullable String name) {
        return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    }

通過上述程式碼的呼叫後會返回true,這樣就會直接返回當前的beanInstance(在第5步)
關於Object bean1 = app.getBean(“demoFactoryBean”);這段對應的原始碼,是在上述的基礎上繼續深入即可。

這裡寫圖片描述
掃描關注:全棧工程師成長記
一個可以交流的平臺,目的是為了做一個影響最有影響力的人的平臺。