Spring 的beanDefinition
對於學習Spring而言,BeanDefinition是非常重要的,而且在Spring內部提供多種類型BeanDefinition,下邊是Spring官網對BeanDefinition的描述
Within the container itself, these bean definitions are represented as
BeanDefinition
objects, which contain (among other information) the following metadata:
- A package-qualified class name: typically, the actual implementation class of the bean being defined.
- Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
- References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.
- Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.
這裏說明一下幾個重要的類/接口:
-
BeanDefnition:定義了一些基本常量和對BeanDefinition的基本操作方法
-
-AttributeAccessor:提供屬性方法-
-
AttributeAccessorSupport:實現AttributeAccessor
-
GenericBeanDefinition:我們通過xml配置的bean class
最終都會轉換爲這種類型的BeanDefninition,對於這種類型的BeanDefinition,它既可以作爲Parent,
也可以作爲Child,在BeanDefinitoin中存在父子的說法,這個類好像是在Spring2.5以後提供的 -
ScannedGenericBeanDefintion:繼承GenericBeanDefinition,
我們通過@Component註解的類,最後都會轉換爲這種類型的BeanDefinition -
AnnotatedGenericBeanDefinition:@Configuration標註的配置類將會被轉換爲這種類型的BeanDefintion
-
RootBeanDefinition:
這種類型的BeanDefinition只能作爲Parent出現,Spring自定義類最後都轉換爲這種類型的BeanDefinition,在最後實例化bean的時候,所有其他類型都會被轉換爲RootBeanDefinition -
ChildBeanDefintion:這種類型的BeanDefinition只能作爲Child出現
問題:如果同時提供@Component和xml配置, 這時候是什麼類型BeanDefinition
根據源碼提供,Spring會先解析@CompontScan, 然後將對應包下的所有滿足條件的類轉換爲ScannedGenericBeanDefintion,然後Spring會檢查@ImportResource,這個時候會把xml配置文件中的class轉換爲GenericBeanDefinition, 如果在xml和註解中同時包含相同的類,最後會轉換爲ScannedGenericBeanDefintion
1.org.spring…由Spring提供
2.config爲@Configuration標註的配置類
3.beanTest爲@Component和xml同時配置的類
4.entity 爲只有@Component標註的類
5.xmlBean爲xml配置類
問題:爲什麼要定義這麼多的BeanDefinition,只使用一種不好麼?
個人認爲,spring該開始也沒有考慮這麼多,最開始xml和註解類型沒有留下的時候,可以通過一些簡單的類就可以實現,但是隨着這下新型技術出現,Spring也不得不跟上發展的步伐,但是原有的也不能徹底拋棄,所以就創建除了這麼多不同的BeanDefinition,針對不同類型的BeanDefinition做不同類型的特殊處理,而且最後在實例化Bean的時候都還要合併成RootBeanDefinition
問題:怎麼證明?
1.Spring內部提供的爲RootBeanDefinition
這是一段實例化BeanFactory的代碼,可以看到內部使用spring自己提供的類被封裝成了RootBeanDefinition
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
/**從傳入的registry[也就是最開始創建的ApplicationContext, 這裏是AnnotationConfigApplicationContext]
* 中獲取到 beanFactory;
*
* 如果registry是DefaultListableBeanFactory的繼承類, 則直接返回registry
* 如果是GenericApplicationContext,則將其強轉爲DefaultListableBeanFactory 返回
* 否則返回false
*/
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
//設置依賴比較器,什麼時候會用到
//處理時候過程的先後順序, 主要處理@Order, 需要驗證
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
//設置自動裝配候選分解器,主要處理@Lazy和做@qualifier支持
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
//BeanDefinitionHolder 是BeanDefinition的持有類,
/**
* BeanDefinitionHolder 提供用瞭如下屬性
* private final BeanDefinition beanDefinition;
* private final String beanName;
* private final String[] aliases;
*/
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
/**
* org.springframework.context.annotation.internalConfigurationAnnotationProcessor
* RootBeanDefinition spring內部自定義bean
* spring 拓展點之一
*/
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
/**
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor
*/
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
2.@Configuration標註類被轉換AnnotatedGenericBeanDefinition
我們可以看到在源碼中有這樣一段,他在register(annotatedClasses);內部,所以源碼證明了@Configuration被轉換類AnnotatedGenericBeanDefinition
private <T> void doRegisterBean(Class<T> annotatedClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
//實例化一個BeanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
....
3.entity 爲只有@Component標註的類
查看Spring內部關於basePackage的掃描實現方法,可以知道ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); 所以標註註解被掃描處理的類最終會被轉換爲ScannedGenericBeanDefinition
//掃描包, 獲取到beanDefinition的class
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//憑藉查詢路徑:classpath*:com/mjlf/spring/addBeantoContext/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//找到當前包下的所有類
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//拿到metaDeata讀取器
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//比對排除過濾器和包含過濾器,判斷該類是不是符合BeanDefinition候選條件
if (isCandidateComponent(metadataReader)) {
//將掃描出來, 當前的class生成爲ScannedGenericBeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
//判斷是否爲候選組件,主要依據爲是能實例化的類或者內部類,不能是抽象類或者接口, 如果是抽象類就必須標註@Lookup
if (isCandidateComponent(sbd)) {//正常識別, 需要添加到beanDefinitionHolder中
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
} else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
} else {//不包含處理
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
5.xmlBean爲xml配置類最後會被轉換爲GenericBeanDefinition
跟着源碼可以發現,最終找到xml配置類型的最終會調用這個方法,方法內部使用:
GenericBeanDefinition bd = new GenericBeanDefinition();
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.
*
* xml解析最後會調用這個方法創建BeanDefinition, 所以默認xml配置的bean最後創建出來的BeanDefinition爲GenericBeanDefinition
*
* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
3.beanTest爲@Component和xml同時配置的類
根據上邊的描述,標註註解的會被轉換爲ScannedGenericBeanDefinition,而xml的會被轉換爲GenericBeanDefinition,那同時使用兩種方法在同一個類上,spring是怎麼處理的,我們知道parser.parse(candidates);方法調用用於掃描包,將符合條件的類轉換爲BeanDefinition,但是@ImportResource對應的xml配置文件是在this.reader.loadBeanDefinitions(configClasses);方法調用中處理的
do {
//獲取解析器[ConfigurationClassParser],主要負責掃描包, 然後將需要轉換的class轉換爲BeanDefinition
//很關鍵, 掃描指定包下的所有類,並將那些需要轉換爲BeanDefinition的類轉換爲BeanDefinition
parser.parse(candidates);
//驗證,驗證@Configuration的類???
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
//移除已經處理過的config
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//獲取configuration中@Bean這樣的BeanDefinition
//同時處理@Import,@ImportResource
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
//清除已經處理過的ConfigClass
candidates.clear();
...
/**
* Read {@code configurationModel}, registering bean definitions
* with the registry based on its contents.
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//加載@Bean 標註的方法生成的BeanDefinition
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//處理ImportResource
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
anMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//處理ImportResource
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}