Spring 的beanDefinition

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());
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章