天天看點

【Spring】揭開Spring朦胧的面紗首先然後

使用Spring的IOC容器擷取一個Bean的實質,其實可以簡化成使用反射的一個過程。

首先

這是一個Bean的定義。

Student bean

package cn.edu.au.selection.service;

public class Student {

    private String name;

    private int age;

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
           

然後

啟動Spring的時候,也就是Spring初始化的時候。有經驗的人都知道ApplicationContext的初始化過程主要是在

AbstractApplicationContext

類中

refresh()

方法中進行。而且該方法中最重要的兩個子方法分别是

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

finishBeanFactoryInitialization(beanFactory);

第一個方法

第一個方法的主要作用就是建立可供上下文使用的基本IOC容器,也就是

DefaultListableBeanFactory

。由該IOC容器對我們所注冊的

BeanDefinition

以及Bean進行托管。在該IOC容器的初始化過程中,主要做了三件事:

1. `BeanDefinition`的`Resource`的定位。也就是說我們配置`<bean></bean>`标簽的Spring`*.xml`檔案的定位。或是`FileSystemResource`,或是`ClassPathContextResource`。這些Resource其實就是對Xml檔案的一個封裝。
 2. 當定位到`Resource`之後,便可以解析 `BeanDefinition`了。該`BeanDefinition`其實是對Bean定義的一個抽象。包含你定義的Bean的一些中繼資料、`ClassLoader`等等。該過程又可能分成兩個步驟,第一步,進行Xml檔案解析,該步隻是将檔案當成普通的Xml檔案進行解析。而第二步就是Spring按照`<bean></bean>`标簽的定義對`BeanDefinition`進行解析生成`BeanDefinition`對象。這個對象由`AbstractBeanDefinition`指向,放在IOC容器中統一托管。
 3. 第三步就是`BeanDefination`在IoC容器中的注冊,也就是正式托管的過程。這個過程其實就是将`BeanDefination`放入`DefaultListableBeanFactory`的一個`ConcurrentHashMap`中,叫做`beanDefinitionMap`。供随後建立Bean的實體做準備。
           

注意:在第二個步驟中,不僅僅将

*.xml

檔案中定義的Bean的

BeanDefinition

解析出來,而且将有

<context:component-scan/>

标簽的,有

@Component

等注解的那些Bean的

BeanDefinition

也解析出來。當然還有Spring内部自己使用的一些Bean的

BeanDefinition

第二個方法

第二個方法

finishBeanFactoryInitialization(beanFactory);

的主要作用就是初始化所有剩餘的非延遲加載

(non-lazy-init)

的單例的Bean。注意,在這個方法的調用棧中就包含了我們會使用的

ApplicationContext.getBean()

的調用。也就是說,其實所有Bean的建立都是在

getBean()

方法中進行的。方法調用中有一個比較關鍵的方法就是

AbstractBeanFactory.doGetBean()

方法。這個方法的邏輯路線是這樣的。

  1. 首先當初始化上下文的時候,非延遲加載的單例Bean先會通過

    Object sharedInstance = getSingleton(beanName);

    方法查找。當使用

    getBean

    方法的時候,首先也是在

    getSingleton(beanName)

    方法中查找有沒有之前建立好的執行個體。這一步如果沒有找到,當然隻能找到單例,prototype類型的肯定找不到,還有

    lazy-init

    的也會找不到。
  2. 然後進行一個判斷。首先從IOC容器中擷取

    BeanDefination

    ,從其中判斷待擷取的Bean是單例還是原型

    (prototype)

    模式。如果是原型模式,每次建立一個Bean的執行個體即可。如果是單例模式,當第一次建立完執行個體之後需要儲存到IOC容器中,第二次擷取的時候就無需再次建立了。同樣的,該單例Bean也是儲存在

    DefaultListableBeanFactory

    的另一個

    ConcurrentHashMap

    中,叫做

    singletonObjects

  3. 在建立Bean執行個體,調用

    AbstractAutowiredCapableBeanFactory.doCreateBean()

    方法時有兩個關鍵的方法,分别是

    createBeanInstance()

    populateBean()

    。其中第一個方法就是使用反射,調用類的構造方法來建立一個該類的執行個體對象。而第二個方法就是對該執行個體對象進行初始化,也就是執行個體中屬性的初始化。有些

    @Autowired

    或者

    @Value

    的注解的屬性的初始化都會在這裡進行。并且有

    @Autowired

    注解的屬性還會觸發遞歸調用建立Bean的方法。說白了,就是依賴關系處理的過程。
  4. 在完成依賴關系處理之後,在該方法中接着會執行一個這個方法

    initializeBean()

    ,這個方法中又有三個重要的方法,分别是

    applyBeanPostProcessorsBeforeInitialization()

    invokeInitMethods()

    applyBeanPostProcessorsAfterInitialization()

    。其中含有

    @PostConstruct

    注解的方法會在第一個方法中執行。第二個方法中一般執行

    BeanDefinition

    中指定的

    init-method

    方法或者

    afterPropertitiesSet()

    實作方法。

建立Bean執行個體的簡化過程

至此,Spring之IOC容器的初始化過程以及依賴注入都說完了。

那麼建立Bean執行個體的過程可以簡化成以下代碼:

package cn.edu.au.selection.service;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Reflection {

    private static Logger logger = LoggerFactory.getLogger(Reflection.class);

    public static void main(String[] args){
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<?> clazz;
        String className = "cn.edu.au.selection.service.Student";
        Object object = null;
        String name = "Ethan Hunt";
        int age = ;
        try {
            clazz = classLoader.loadClass(className);
            Constructor<?>[] ctors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : ctors){
                constructor.setAccessible(true);
                object = constructor.newInstance();
            }
            Field field = clazz.getDeclaredField("name");
            field.setAccessible(true);
            field.set(object, name);
            Method method = clazz.getDeclaredMethod("setAge", int.class);
            method.setAccessible(true);
            method.invoke(object, age);

        } catch (ClassNotFoundException e) {
            logger.error("class {} cannot be found!", className);
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            logger.error("被調用的方法{}的内部抛出了異常而沒有被捕獲!", "Student()/setAge()");
            e.printStackTrace();
        } catch (InstantiationException e) {
            logger.error("{}類無法被執行個體化!", className);
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            logger.error("方法{}或屬性{}無法被通路!", "setAge()", "name");
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            logger.error("沒有這樣的屬性{}!", "name");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            logger.error("沒有這樣的方法{}!", "setAge()");
            e.printStackTrace();
        }
        logger.info(JSON.toJSONString(object));
    }
}
           

輸出結果為:

{
    "age": ,
    "name": "Ethan Hunt"
}