<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
從上圖能夠看出,第一步是把全部定義的Bean的BeanDefinitions讀取出來,須要一下步驟 spring
一、定位資源文件(class) api
二、解析xml文件 app
三、根據xml文件中的配置,找到class文件 ide
四、讀取包下面的全部class文件,而後進行調用ClassReader類的accpet方法,分析class文件,過濾出帶有spring註解的類(Component,respositr,service,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註解了,而後實際狀況是如何注入的,是屬於另外的一個流程,將會在下一篇文章中進行分析!