1. 程式人生 > >20--Spring建立Bean的過程(二),無參建構函式(預設建構函式)例項化

20--Spring建立Bean的過程(二),無參建構函式(預設建構函式)例項化

上一章我們已經分析了Spring例項化bean的步驟,以及對例項化方式的解析,本章分析Spring使用無參建構函式例項化bean的過程。

在分析之前先來了解一下Spring例項化bean的策略

  • JDK的反射機制
  • CGLIB動態代理

對於反射機制,如果拿到其建構函式,引數等相關資訊,就可以通過反射直接建立其例項,但是為什麼Spring提供了兩種例項化的方式呢,答案就是我們之前分析的方法注入。14–Spring lookup-method注入和replace-method注入(二),如果當前bean是通過lookup-method或者replace-method話,那麼這時就需要用到CGLIB進行動態代理,以便在建立代理的同時將動態方法織入到類中。

Spring中的例項化策略通過InstantiationStrategy類來定義,它有兩個實現類,CglibSubclassingInstantiationStrategy和SimpleInstantiationStrategy,其中前者繼承了後者。對於這兩個類有什麼區別呢?

描述 CglibSubclassingInstantiationStrategy SimpleInstantiationStrategy
是否Spring預設例項化策略
是否支援方法注入例項化
是否反射例項化

接著上一章的分析,我們來看原始碼,開啟AbstractAutowireCapableBeanFactory類的instantiateBean方法

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        final BeanFactory parent = this;
        // 如果許可權管理器不為空,需要校驗
        if (System.getSecurityManager() != null) {
            beanInstance = AccessController.doPrivileged
((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, parent), getAccessControlContext()); } else { // 獲取例項化策略並例項化bean beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }

可以看到例項化bean的方法為instantiate,進入到SimpleInstantiationStrategy類的instantiate方法中,繼續檢視:

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // 如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射建立bean的例項
    // Don't override the class with CGLIB if no overrides.
    if (!bd.hasMethodOverrides()) {
        Constructor<?> constructorToUse;
        synchronized (bd.constructorArgumentLock) {
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    if (System.getSecurityManager() != null) {
                        constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    }
                    else {
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // 否則必須使用CGLIB例項化策略
        // Must generate CGLIB subclass.
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

在該方法中,Spring對使用JDK的反射例項化還是CGLIB例項化進行了判斷。如果沒有方法覆蓋(replace-method或lookup-method注入)則使用JDK的反射機制進行例項化,如果有的話,則使用CGLIB動態代理進行例項化。

具體的例項化程式碼這裡就不分析了,大家可以使用一個普通的bean和使用了replace-method或lookup-method注入的bean進行debug。參考14–Spring lookup-method注入和replace-method注入(二)

Spring預設建構函式的例項化還是比較簡單的,下一篇我們分析對建構函式引數的解析,就比較複雜了!