曹工說Spring Boot原始碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,我們來試一下
寫在前面的話
相關背景及資源:
曹工說Spring Boot原始碼系列開講了(1)-- Bean Definition到底是什麼,附spring思維導圖分享
工程程式碼地址 思維導圖地址
工程結構圖:
大體思路
選擇bean definition實現類,並例項化bean definition
- 註冊bean definition
get bean檢視是否work
選擇bean definition實現類
這次,先說目的:我們要通過程式碼方式手動生成bean definition並註冊到bean factory。
我的思路是這樣的,既然前面兩節,分析了bean definition介面中的各個方法,也算對其有了基本的瞭解了。但
org.springframework.beans.factory.config.BeanDefinition
只是一個介面,介面是不能例項化的,也無從談起註冊了。
我們從bean definition的實現類中選一個吧:
非抽象的實現類主要有以下三個:
org.springframework.beans.factory.support.GenericBeanDefinition
:幸運兒,被我們選中的,也是官方推薦的,註釋裡提到可以動態設定GenericBeanDefinition
的parent bean definition的名稱;這個呢,
org.springframework.beans.factory.support.RootBeanDefinition
org.springframework.beans.factory.support.ChildBeanDefinition
也能實現bean的繼承關係,但是可能這種預先定義一個bean為child/parent
的方式,太死了。官方自己在
ChildBeanDefinition
的註釋裡寫到:NOTE: Since Spring 2.5, the preferred way to register bean definitions programmatically is the {@link GenericBeanDefinition} class, which allows to dynamically define parent dependencies through the* {@link GenericBeanDefinition#setParentName} method. This effectively supersedes the ChildBeanDefinition class for most use cases.
注意最後那句話,supresede這個單詞我還他麼不太認識,專門查了下詞典,意思是
取代、代替
,那這句話意思就是,大部分時候,
GenericBeanDefinition
取代了ChildBeanDefinition
的作用。這個下面有兩個子類,之前也提過,主要是供那種通過註解方式,比如
@controller
這種掃描進來的bean definition。org.springframework.beans.factory.support.ChildBeanDefinition
,官方都不建議用了,直接跳過吧;org.springframework.beans.factory.support.RootBeanDefinition
,在@configuration
中有用,後面再講
基於上面的思路,我們選了GenericBeanDefinition
,這個類可以直接new,new了之後再通過set方法設定beanClassName等。
public class GenericBeanDefinition extends AbstractBeanDefinition {
private String parentName;
/**
* 無參建構函式,但是你看到下面那一堆set方法了吧,就是讓你自己設
* Create a new GenericBeanDefinition, to be configured through its bean
* properties and configuration methods.
* @see #setBeanClass
* @see #setBeanClassName
* @see #setScope
* @see #setAutowireMode
* @see #setDependencyCheck
* @see #setConstructorArgumentValues
* @see #setPropertyValues
*/
public GenericBeanDefinition() {
super();
}
}
還有一個方式是,我們看看框架裡怎麼用的,經過我一番搜尋,
發現框架裡,主要使用了org.springframework.beans.factory.support.BeanDefinitionBuilder
和
org.springframework.beans.factory.support.BeanDefinitionReaderUtils
,而且,框架裡,還是前者用的多,也比較方便(後面有示例程式碼)。
註冊bean definition
然後,知道怎麼構造GenericBeanDefinition
了,那麼要怎麼註冊呢,這個也簡單,我們看看beanFactory
,
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
不只實現了ConfigurableListableBeanFactory
,還實現了BeanDefinitionRegistry
。
public interface BeanDefinitionRegistry extends AliasRegistry {
/**
* 註冊beanDefinition,要自己指定beanName
* Register a new bean definition with this registry.
* Must support RootBeanDefinition and ChildBeanDefinition.
* @param beanName the name of the bean instance to register
* @param beanDefinition definition of the bean instance to register
* @throws BeanDefinitionStoreException if the BeanDefinition is invalid
* or if there is already a BeanDefinition for the specified bean name
* (and we are not allowed to override it)
* @see RootBeanDefinition
* @see ChildBeanDefinition
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
...
}
所以,我們只要呼叫org.springframework.beans.factory.support.DefaultListableBeanFactory
的註冊方法即可。
這裡說下beanNameGenerator
,一開始我用的org.springframework.beans.factory.support.DefaultBeanNameGenerator
,結果生成的bean的名稱是這樣的:
org.springframework.simple.TestService#0
,這和我們平時使用autowired方式,生成的beanName不一樣啊,不習慣。於是改成了org.springframework.context.annotation.AnnotationBeanNameGenerator
,就對了!
如何表達bean間依賴
這裡先介紹兩種方式,分別是構造器注入和property注入。對了,先不要和我提什麼autowired
哈,那個是自動,這個呢,手動。也許,後面你會更懂autowired
,也更懂自動。
構造器注入
核心程式碼:
@ToString
public class TestControllerByConstructor {
TestService testService;
/**
* 基本型別依賴
*/
private String name;
public TestControllerByConstructor(TestService testService, String name) {
this.testService = testService;
this.name = name;
}
public TestService getTestService() {
return testService;
}
public String getName() {
return name;
}
}
package org.springframework.simple;
import lombok.ToString;
@ToString
public class TestService implements ITestService{
}
/**
* 2. 構造bean definition,並在bean definition中表達bean之間的依賴關係
*/
GenericBeanDefinition iTestServiceBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
.genericBeanDefinition(TestService.class).getBeanDefinition();
log.info("iTestServiceBeanDefinition:{}",iTestServiceBeanDefinition);
GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
.genericBeanDefinition(TestControllerByConstructor.class)
// 這裡,看這裡,這裡在表達依賴了
.addConstructorArgReference("testService")
.addConstructorArgValue("wire by constructor")
.getBeanDefinition();
完整程式碼:
package org.springframework.simple.byconstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.simple.ITestService;
import org.springframework.simple.TestService;
import org.springframework.util.Assert;
@Slf4j
public class ManualRegisterBeanDefinitionDemoByConstructor {
public static void main(String[] args) {
wireDependencyByConstructor();
}
/**
* 通過構造器的方式來注入依賴
*/
private static void wireDependencyByConstructor() {
/**
* 1:生成bean factory
*/
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
/**
* 2. 構造bean definition,並在bean definition中表達bean之間的依賴關係
*/
GenericBeanDefinition iTestServiceBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
.genericBeanDefinition(TestService.class).getBeanDefinition();
log.info("iTestServiceBeanDefinition:{}",iTestServiceBeanDefinition);
GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
.genericBeanDefinition(TestControllerByConstructor.class)
.addConstructorArgReference("testService")
.addConstructorArgValue("wire by constructor")
.getBeanDefinition();
/**
* 3. 註冊bean definition
*/
// DefaultBeanNameGenerator generator = new DefaultBeanNameGenerator();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
String beanNameForTestService = generator.generateBeanName(iTestServiceBeanDefinition, factory);
factory.registerBeanDefinition(beanNameForTestService, iTestServiceBeanDefinition);
String beanNameForTestController = generator.generateBeanName(iTestControllerBeanDefinition, factory);
factory.registerBeanDefinition(beanNameForTestController, TestControllerBeanDefinition);
/**
* 4. 獲取bean
*/
TestControllerByConstructor bean = factory.getBean(TestControllerByConstructor.class);
log.info("TestControllerByConstructor:{}",bean);
ITestService testService = factory.getBean(ITestService.class);
log.info("testService bean:{}",testService);
Assert.isTrue(bean.getTestService() == testService);
}
}
property注入
原理類似,核心程式碼不同之處如下:
GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
.genericBeanDefinition(TestControllerWireByProperty.class)
// 這裡是呼叫的property相關方法
.addPropertyReference("t","testService")
.addPropertyValue("name","just test")
.getBeanDefinition();
總結
今天就到這裡,有問題請指出哈,歡迎大家和我一起閱讀spring boot原始碼。
原始碼地址:
https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-manual-register-bean-definit