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
的建立流程。