BeanDefinition
- 首先我們需要了解
BeanDefinition
到底是個什麼東西? - 瞭解Spring基於
BeanDifination
對象做了哪些實現? - 基於Spring是如何使用
Beandifination
對象來操作的?基於Mybatis
的mapper
分析。
首先我們需要了解BeanDefinition
到底是個什麼東西?
從IDEA的關係圖上來看Beandefinition
對象具有如下特點:
- 擁有屬性存儲的功能[AttributeAccessor]
- 擁有資源獲取的能力,也就是讀取配置資源的能力【BeanMatadataElement】
- 對Bean對象的描述能力[BeanDefinition]
瞭解Spring基於BeanDifination
對象做了哪些實現?
通過了解Spring的實現,能夠知道這個東西的實現方式以及作者對實現模型的定位。
圖中紅線框柱的部分則是針對Beandifination
的具體實現:
我們先從AbstractBeanDefinition
對象開始瞭解
從上述實現方法來看,該類就是實現了BeanDefinition
的所有方法。
而其他的子類:
GenericBeanDefinition
: 通用的bean實現,自2.5以後新加入的bean文件配置屬性定義類,是ChildBeanDefinition
和RootBeanDefinition
更好的替代者,
ScannedGenericBeanDefinition
: 被包掃描到的bean定義
AnnotatedGenericBeanDefinition
: 查找類註解初始化的定義
RootBeanDefinition
: 代表一個從配置源(XML
,Java Config
等)中生成的BeanDefinition
ChildBeanDefinition
: 可以從父BeanDefinition
中集成構造方法,屬性等。
基於Spring是如何使用Beandifination
對象來操作的?
我們從最常用的三個類取看:
GenericBeanDefinition
: 針對配置Bean的處理
ScannedGenericBeanDefinition
它的類是在ClassPathScanningCandidateComponentProvider.findCandidateComponents
方法中被調用
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
}
}
// 部分代碼省略
return candidates;
}
這是統一的入口,看這個方法名就可以知道,通過定義填寫了basePackage
的相關注解,最終都會經過方法去查找,並且會包裝成ScannedGenericBeanDefinition
對象。
AnnotatedGenericBeanDefinition
我們先在實例化處打個斷點看看它是如何被執行的。
分析鏈路(只選舉關鍵鏈路):
invokeBeanDefinitionRegistryPostProcessors: 執行Bean中實現了BeanDefinitionRegistryPostProcessor
的類的postProcessBeanDefinitionRegistry
方法,目標類可以看到是:ConfigurationClassPostProcessor
processConfigBeanDefinitions: 斷點在循環遍歷Bean的時候,會去判斷Bean上面的註解。
如果包含有@Import
、@Configuration
等註解,則會採用
這裏可以大概知道GenericBeanDefinition
、AnnotatedGenericBeanDefinition
、ScannedGenericBeanDefinition
等類都是針對配置類型的Bean定義。
那麼常用的Bean呢?
RootBeanDefinition
: 普通Bean的定義
它的作用也是封裝對象的信息,不過不同於配置對象,他是更爲普通的Bean類型。
在創建bean的實例的同時,都是以RootBeanDefinition
來做處理
AbstractBeanFactory.java
// 創建Bean的時候觸發
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 部分省略
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
}
// Create bean instance.
if (mbd.isSingleton()) {
//創建單例工廠
}
else if (mbd.isPrototype()) {
// 是否原型
} else {
// 獲取scope
String scopeName = mbd.getScope();
}
return (T) bean;
}
其實我的理解就是描述了Bean的對象,方便在實例化的時候做一些特殊操作。比如@Autowired等註解。
BeanDefinition
也有對應的拓展點: BeanDefinitionRegistryPostProcessor
舉個常用的案例:Mybatis
大家都知道Mybatis
的Mapper
是不需要實現類的,只需要定義一個接口就行了,但是它是如何通過Spring拿到對應的實現的呢?
在Spring容器啓動的時候,在解析完了配置文件之後。開始執行Spring內部拓展接口的調用其中包括了
BeanPostProcess
、BeanDefinitionRegistryPostProcessor
.
BeanPostProcess: 針對每個bean的實例化之前和之後會觸發。
BeanDefinitionRegistryPostProcessor : 發生的比上面要早,在bean還處於BeanDefinition
加載完畢之後的階段。
Mybatis就是基於這個時機通過MapperScannerConfigurer
觸發了BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry
方法的調用。
// MapperScannerConfigurer
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
// 掃描包 , 會觸發下面的doScan方法
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
這個時候需要告訴Mybatis你的basePackage
、sqlSessionFactory
、sqlSessionTemplate
等等屬性,
- 要掃描的包
- 配置文件解析對象
- SQL執行對象
有了這三個功能基本上已經具備了執行SQL的條件。
這時候MapperScannerConfigurer需要爲掃描包下面的接口指定一個具體實現MapperFactoryBean
。不然這個構建的BeanDefinition是沒有用的。
這裏在ClassPathMapperScanner的doScan中體現
// 這裏會在上面的scan方法被回調
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 爲對象屬性賦值
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
// 指定具體的實現
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
return beanDefinitions;
}
指定了具體實現之後將上面三個關鍵屬性交給MapperFactoryBean
管理。
這個時候大概明白了,所有的Mapper的具體實現都是MapperFactoryBean。
而MapperFactoryBean
也具備了執行SQL的條件。
後面都是些細節的執行過程就不細究了,比如通過接口的包+類名+方法名和xml中的對象相對應得到具體的執行SQL。
以上是僅從個人觀點,希望會給大家帶來一些幫助。有問題請及時指正。