【03】spring源碼解析之配置文件context:component-scan 解析流程

如今再使用spring的時候,若是採用註解的方式,咱們一般會在xml文件裏面配置context:component-scan節點,而後定義base-package屬性,告訴給spring去掃描哪些包下面的Java文件,而後自定的完成實例化和屬性的自動注入,配置一般是下面的形式
<context:component-scan base-package="com.wtf.demo.spring.beans"/>
<context:annotation-config/>




以ClassPathXmlApplicationContext爲例,當new一個ClassPathXmlApplicationContext對象,裏面傳入application.xml配置文件,而後spring會讀取這個xml文件的context:component-scan節點和base-package屬性,總體的時序圖以下: java


從上圖能夠看出,第一步是把全部定義的BeanBeanDefinitions讀取出來,須要一下步驟 spring

一、定位資源文件(class api

二、解析xml文件 app

三、根據xml文件中的配置,找到class文件 ide

四、讀取包下面的全部class文件,而後進行調用ClassReader類的accpet方法,分析class文件,過濾出帶有spring註解的類(Componentrespositrservice,controller等註解是屬於Component的註解的子類) 函數

五、從新組裝過濾出來的class類,注入相關默認屬性,包裝爲BeanDefinition,返回 post

本篇文章主要分析第四步中,spring如何根據xml文件中配置的須要掃描的包,掃描受spring註解的Java文件的,更嚴格的說應該是class文件 this

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    //須要被掃描的包的字符串,這個有多是多個包,包之間用「;」或者」,」號隔開,而後再被字符串分隔函數進行分隔,分隔爲多個包
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

   // Actually scan for bean definitions and register them.
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //根據配置文件中的內容,看是否配置了特殊的過濾器,例如xxx包下面的不掃描什麼的,實際上就是includeFilters和excludeFilters,若是沒有配置,則爲USE_DEFAULT_FILTERS_ATTRIBUTE,
    //還有就是解析看有沒有用到什麼包生成器,以及做用域的代理和類型攔截器(typeFilter)之類的,根據以上配置信息,返回一個scanner,也就是包掃描器,真正的掃描工做就提交給了ClassPathBeanDefinitionScanner類的doScan方法了
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}
接下來看包掃描器的掃描方法


protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
根據包路徑,查找此包下候選的全部class,而後返回一個用BeanDefinition描述的set集合,此方法的在下面有所分析
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//返回此包下面的全部class對象描述的BeanDefinition文件
			
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				/**
				scopeMetadata屬性: scopeName=singleton
				scopeProxyMode:Name:NO ,ordinal:1
解析文件,
				**/
					
				candidate.setScope(scopeMetadata.getScopeName());//設置Bean的做用域
				BeanDefinition 自己設置了一些默認屬性,例如scope 默認設置爲了singleton
				
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//返回class的在spring中的bean的名字,根據類名(不包括包),調用Java的Introspector.decapitalize(shortClassName)方法生成bean的名字,生成規則以下:若是是空則直接返回,若是第一個字母是大寫,而且第二個字母也是大寫,則按照原樣返回,不然,把第一個字母更改成小寫返回


				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
					//設置一些默認屬性,調用AbstractBeanDefinition類的applyDefaults方法,設置默認屬性
					/**
						setLazyInit(defaults.isLazyInit());
						setAutowireMode(defaults.getAutowireMode());
						setDependencyCheck(defaults.getDependencyCheck());
						setInitMethodName(defaults.getInitMethodName());
						setEnforceInitMethod(false);
						setDestroyMethodName(defaults.getDestroyMethodName());
						setEnforceDestroyMethod(false);
					**/
					
								}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
					//解析一些公共的註解Lazy,Primary,DependOn
				}
				if (checkCandidate(beanName, candidate)) {
				
				//校驗bean的設置是否合法, 校驗Bean的兼容性和Bean是否衝突,咱們常見的Annotation-specified bean name conflicts with existing異常,就是在這個方法中拋出來的,注意在這個流程中是不有Autowired註解的屬性的值進行注入的,注入的流程是在另外的流程中,下一篇文檔會專門解析如何注入屬性的

					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

查找候選組件的方法 spa

一、根據是否是使用了spring的註解 .net

二、是否是在includeFilter過濾器中

三、是否是在excludeFilter過濾器中這些條件

四、@Return 返回符合條件的class

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + "/" + this.resourcePattern;
        //包的classpath路徑,例如根據個人xml文件的配置,則packageSearchPath的值是classpath*:com/wtf/demo/spring/beans/**/*.class
      Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);

       //resuources是這個包路徑下的全部class,無論class有沒有spring的註解,均包含在內
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                //返回一個SimpleMetadataReader類型的MetadataReader ,這個類是持有class各類方法以及屬性類型的對象
               if (isCandidateComponent(metadataReader)) {
                //根據配置的excludeFilters和includeFilters判斷此class文件是否是候選對象
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
        		........
			........
			//捕獲的一些異常信息             
	   }
   return candidates;
}

以上就是對配置了component-scan節點,屬性爲base-package包下的文件的掃描過程,對於屬性有Autowired註解了,而後實際狀況是如何注入的,是屬於另外的一個流程,將會在下一篇文章中進行分析!

相關文章
相關標籤/搜索