Spring 源碼-Spring Bean 的創建過程(13)

那隻斑馬還沒有睡 發佈 2022-05-16T17:53:48.917640+00:00

Spring Bean的創建過程非常的複雜,上一篇重點介紹了Spring在創建Bean的過程中,使用InstantiationBeanPostProcessor進行提前創建Bean,我們可以通過CGLIB創建對象對Bean的方法進行增強,當然也可以進行其他方式的創建方式。

Spring Bean的創建過程非常的複雜,上一篇重點介紹了Spring在創建Bean的過程中,使用InstantiationBeanPostProcessor進行提前創建Bean,我們可以通過CGLIB創建對象對Bean的方法進行增強,當然也可以進行其他方式的創建方式。通過提前創建bean,減少了調用doCreateBean方法的複雜邏輯的執行,而且通過這種方式可以定製創建的方式,便於擴展。

使用 supplier 進行Bean的提前暴露

接下來繼續介紹Spring的創建過程,執行doCreateBean方法:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                        throws BeanCreationException {

                // Instantiate the bean.
                BeanWrapper instanceWrapper = null;
                if (mbd.isSingleton()) {
                        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
                }
                if (instanceWrapper == null) {
                        // 實例化對象
                        instanceWrapper = createBeanInstance(beanName, mbd, args);
                }
                Object bean = instanceWrapper.getWrappedInstance();
                Class<?> beanType = instanceWrapper.getWrappedClass();
                if (beanType != NullBean.class) {
                        mbd.resolvedTargetType = beanType;
                }
                // 省略代碼....
}

這裡會先從緩存中獲取FactoryBean實例化的對象,如果有就進行下面的邏輯,一般來說基本是獲取不到的,就會走下面創建createBeanInstance方法。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
  // Make sure bean class is actually resolved at this point.
  // 解析Bean Class 用於創建對象
  Class<?> beanClass = resolveBeanClass(mbd, beanName);
  // 判斷class必須是public修飾的,否則報錯
  if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
  }
  // 獲取到supplier,如果不為空,則創建對象直接返回
  // 擴展點,可以在這裡進行對象的初始化創建,使用BFPP對BeanDefinition進行設置supplier
  Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
  if (instanceSupplier != null) {
    return obtainFromSupplier(instanceSupplier, beanName);
  }
  // 使用FactoryMethod進行對象的創建
  // 擴展點
  if (mbd.getFactoryMethodName() != null) {
    return instantiateUsingFactoryMethod(beanName, mbd, args);
  }
  // 省略部分代碼....
}

我們可以看到這裡兩個return,意味著只要獲取到Bean,那麼就不需要進行下一步的執行,首先看getInstanceSupplier,這個是BeanDefinition中的方法,那說明可以在解析BeanDefinition的時候進行處理,那麼什麼時候進行BeanDefinition的擴展解析呢?根據前面的介紹可以得知在解析BeanFactoryPostProcessor時可以進行BeanDefinition的處理。

那為啥不是loadBeanDefinition時處理呢?因為Spring在加載階段是沒有提供擴展點的,而在BeanFactoryPostProcessor接口註冊和執行的時候,完全是可以自己定義一個BeanFactoryPostProcessor進行擴展實現。

這個屬性位於AbstractBeanDefinition類中,一般來說用戶自定義的BeanDefinition都是GenericBeanDefinition,而GenericBeanDefinition是繼承這個抽象類的,所以我們在進行BFPP擴展實現時可以對GenericBeanDefinition設置這個屬性值,這個屬性值是一個Supplier函數式接口,相當於lambda表達式的用法,接下來自己實現一個驗證一下。

創建一個SupplierUser對象:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class SupplierUser {

        private String username;

        public SupplierUser() {
        }

        public SupplierUser(String username) {
                this.username = username;
        }

        public String getUsername() {
                return username;
        }

        public void setUsername(String username) {
                this.username = username;
        }

        @Override
        public String toString() {
                return "SupplierUser{" +
                                "username='" + username + '\'' +
                                '}';
        }
}

創建一個創建SupplierUser的類:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class CreateSupplier {

        public static SupplierUser createUser(){
                return new SupplierUser("redwinter");
        }
}

創建BFPP的實現:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition("supplierUser");
                // 獲取原生的BeanDefinition
                GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanDefinition;
                // 實例化Supplier
                genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser);
                // 設置類型
                genericBeanDefinition.setBeanClass(CreateSupplier.class);
        }
}

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="supplierUser" class="com.redwinter.test.supplier.SupplierUser"/>
        <bean class="com.redwinter.test.supplier.SupplierBeanFactoryPostProcessor"/>
</beans>

測試類:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class SupplierTest {

        /**
         * 使用BFPP設置Supplier進行對象的創建
         * BFPP可以對BeanDefinition進行設置和修改
         */
        @Test
        public void test() {
                ApplicationContext ac = new ClassPathXmlApplicationContext("supplier.xml");
                SupplierUser bean = ac.getBean(SupplierUser.class);
                System.out.println(bean);
        }
}

當xml中不配置BFPP的時候:

輸出:

SupplierUser{username='null'}

如果配置了BFPP

輸出:

SupplierUser{username='redwinter'}

說明Bean的創建的過程中通過Supplier進行了提前的創建。

接下來看下一個擴展點:

FactoryMethod 對象的創建

根據源碼可以看出這個屬性也是在BeanDefinition中的,但是這個可以通過標籤的方式進行設置,在Spring中factory-method創建Bean有兩種方式,一種是靜態工廠創建,一種是實例工廠創建。

接下來實驗一下:

創建電視類,這個就是需要創建的Bean對象:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class Tv {

        private String name;
        private String age;

        public String getAge() {
                return age;
        }

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

        public String getName() {
                return name;
        }

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

        @Override
        public String toString() {
                return "Tv{" +
                                "name='" + name + '\'' +
                                ", age='" + age + '\'' +
                                '}';
        }
}

創建靜態類用於靜態工廠創建bean:

/**
 * 家電類
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class StaticJiaDian {

        public static Tv getTv(String name){
                Tv tv = new Tv();
                tv.setName(name);
                tv.setAge("15");
                return tv;
        }

}

創建實例類,用於實例工廠創建對象:

/**
 * 家電類
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class JiaDian {

        public Tv getTv(String name){
                Tv tv = new Tv();
                tv.setName(name);
                tv.setAge("13");
                return tv;
        }
}

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--靜態工廠創建對象-->
        <bean id="tv" class="com.redwinter.test.factorymethod.StaticJiaDian" factory-method="getTv">
                <constructor-arg>
                        <value type="java.lang.String">海爾</value>
                </constructor-arg>
        </bean>

        <!--實例工廠-->
        <bean class="com.redwinter.test.factorymethod.JiaDian" id="jiaDian"/>
        <bean id="tv2" class="com.redwinter.test.factorymethod.Tv" factory-bean="jiaDian" factory-method="getTv">
                <constructor-arg>
                        <value type="java.lang.String">美的</value>
                </constructor-arg>
        </bean>
</beans>

測試類:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class FactoryMethodTest {

        /**
         * factory-method 對象的創建方式
         * 靜態工廠創建方式: 直接使用靜態工廠類進行創建
         * 實例工廠創建方式: 需要配合FactoryBean進行創建
         */
        @Test
        public void test() {
                ApplicationContext ac = new ClassPathXmlApplicationContext("factory-method.xml");
                Tv tv = ac.getBean("tv", Tv.class);
                System.out.println(tv);
                Tv tv2 = ac.getBean("tv2", Tv.class);
                System.out.println(tv2);

        }
}

輸出:

Tv{name='海爾', age='15'}
Tv{name='美的', age='13'}

說明確實是調用了我們自定義的方法創建的對象。

總結下目前來說Bean的創建方式有:

  • 使用FactoryBean創建
  • 使用InstantiationAwreBeanPostProcessor的前置實例化方法postProcessBeforeInstantiation進行創建
  • 使用Supplier進行創建
  • 使用factory-method標籤進行創建實例工廠創建(配合factory-bean標籤)靜態工廠創建
  • 反射創建(常規的,完整的創建流程)

本篇就介紹到這裡,下一篇繼續介紹Bean的創建流程。


如果本文對你有幫助,別忘記給我個3連 ,點讚,轉發,評論,,咱們下期見。

收藏 等於白嫖,點讚才是真情。


原文 https://www.cnblogs.com/redwinter/p/16261003.html

關鍵字: