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" />
<bean />
</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" factory-method="getTv">
<constructor-arg>
<value type="java.lang.String">海爾</value>
</constructor-arg>
</bean>
<!--實體工廠-->
<bean id="jiaDian"/>
<bean id="tv2" 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的創建流程,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/472848.html
標籤:Java
