承接前文Spring源碼情操陶冶-自定義節點的解析,本文講述spring通過
context:component-scan節點干了什么事
ComponentScanBeanDefinitionParser#私有屬性
羅列下context:component-scan可填的基礎屬性
private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
private static final String FILTER_TYPE_ATTRIBUTE = "type";
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
ComponentScanBeanDefinitionParser#parse()-主方法
統一接口parse()方法,看下總體邏輯,代碼如下
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//解析base-package屬性值,掃描的包可以,;分隔
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//通過ClassPathBeanDefinitionScanner掃描類來獲取包名下的所有class並將他們注冊到spring的bean工廠中
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注冊其他注解組件
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
我們關注下ComponentScanBeanDefinitionParser#configureScanner()創建掃描器操作和ClassPathBeanDefinitionScanner#doScan()掃描包方法
ComponentScanBeanDefinitionParser#configureScanner()-創建掃描器
觀察下如何創建掃描器,以及相關的初始操作,代碼奉上
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
XmlReaderContext readerContext = parserContext.getReaderContext();
//默認使用spring自帶的注解過濾
boolean useDefaultFilters = true;
//解析`use-default-filters`,類型為boolean
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
// Delegate bean definition registration to scanner class.
//此處如果`use-default-filters`為true,則添加`@Component`、`@Service`、`@Controller`、`@Repository`、`@ManagedBean`、`@Named`添加到includeFilters的集合過濾
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
scanner.setResourceLoader(readerContext.getResourceLoader());
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
//設置`resource-pattern`屬性,掃描資源的模式匹配,支持正則表達式
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
}
try {
//解析name-generator屬性 beanName生成器
parseBeanNameGenerator(element, scanner);
}
catch (Exception ex) {
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
try {
//解析scope-resolver屬性和scoped-proxy屬性,但兩者只可存在其一
//后者值為targetClass:cglib代理、interfaces:JDK代理、no:不使用代理
parseScope(element, scanner);
}
catch (Exception ex) {
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
//解析子節點`context:include-filter`、`context:exclude-filter`主要用於對掃描class類的過濾
//例如<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller.RestController" />
parseTypeFilters(element, scanner, readerContext, parserContext);
return scanner;
}
此處只簡單的羅列了如何創建一個文件掃描器以及相關的初始操作,具體的讀者可自行去閱讀分析
ClassPathBeanDefinitionScanner#doScan()-掃描操作
真實掃描base-package指定的目錄並返回注冊的所有beanDefinition,具體的掃描簡析如下
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//表明base-package屬性是需要被指定的
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
//對每個基礎包都進行掃描尋找並且對基礎包下的所有class都注冊為BeanDefinition
/**
**
**並對得到的candidates集合進行過濾,此處便用到include-filters和exclude-filters
*/
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//解析一個bean的scope屬性,代表作用范圍
//prototype->每次請求都創建新的對象 singleton->單例模式,處理多請求
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//使用beanName生成器生成
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
/**
**對注冊的bean進行另外的賦值處理,比如默認屬性的配置
*返回的candidate類型為ScannedGenericBeanDefinition,下面兩者
*條件滿足
*/
if (candidate instanceof AbstractBeanDefinition) {
//設置lazy-init/autowire-code默認屬性,從spring配置的<beans>節點屬性讀取
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
//讀取bean上的注解,比如`@Lazy`、`@Dependson`的值設置相應的屬性
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//查看是否已注冊
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//默認采取cglib來做代理
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//注冊bean信息到工廠中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
在這里我們只簡單的看下其父類ClassPathScanningCandidateComponentProvider#findCandidateComponents獲取包下的所有class資源文件並實例化為BeanDefinition對象
ClassPathScanningCandidateComponentProvider#findCandidateComponents()-找尋符合條件的資源文件
掃描包下的所有class文件並對其進行過濾,過濾的條件為includeFilters和excludeFilters集合。代碼簡析如下
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
//值類似為classpath*:com/question/sky/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + "/" + this.resourcePattern;
//通過PathMatchingResourcePatternResolver來找尋資源
//常用的Resource為FileSystemResource
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
//生成MetadataReader對象->SimpleMetadataReader,內部包含AnnotationMetadataReadingVisitor注解訪問處理類
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
//判斷class是否不屬於excludeFilters集合內但至少符合一個includeFilters集合
if (isCandidateComponent(metadataReader)) {
//包裝為ScannedGenericBeanDefinition對象
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
//保存文件資源
sbd.setResource(resource);
sbd.setSource(resource);
//判斷class文件是否不為接口或者抽象類並且是獨立的
if (isCandidateComponent(sbd)) {
//完成驗證加入集合中
candidates.add(sbd);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
對上面的代碼解釋作下補充,主要是驗證beanDefinition的兩個方法
- ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)
對class類進行filter集合過濾
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
//滿足excludeFilter集合中的一個便返回false,表示不對對應的beanDefinition注冊
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
//首先滿足其中includeFilter集合中的一個
if (tf.match(metadataReader, this.metadataReaderFactory)) {
//判斷對應的beanDifinition不存在@Conditional注解或者滿足@Conditional中指定的條件,則返回true
//@Conditional注解的使用可自行查看相關資料
return isConditionMatch(metadataReader);
}
}
return false;
}
- ClassPathScanningCandidateComponentProvider#isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
驗證beanDefinition class類是否為具體類
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
//非抽象類、接口類並且有獨立特性[它是一個頂級類還是一個嵌套類(靜態內部類),可以獨立於封閉類構造。]
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
}
ComponentScanBeanDefinitionParser#registerComponents-注冊其他組件
在掃描包內的class文件注冊為beanDefinition之后,ComponentScanBeanDefinitionParser還需要注冊其他的組件,具體是什么可簡單看下相關的源碼
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
Object source = readerContext.extractSource(element);
//包裝為CompositeComponentDefinition對象,內置多ComponentDefinition對象
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
//將已注冊的所有beanDefinitionHolder對象放到上述對象中
for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
}
// Register annotation config processors, if necessary.
boolean annotationConfig = true;
//獲取annotation-config的屬性值,默認為true
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
//注冊多個BeanPostProcessor接口,具體什么可自行查看,返回的是包含BeanPostProcessor接口的beanDefinitionHolder對象集合
Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
//繼續裝入CompositeComponentDefinition對象
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
}
}
//此處為空
readerContext.fireComponentRegistered(compositeDef);
}
此處的目的主要是注冊多個BeanPostProcessor接口實現類【供后續spring調用統一接口進行解析,比如>>>Spring源碼情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors可執行下述的@Configuration解析】具體的有
- ConfigurationClassPostProcessor解析
@Configuration注解類 - AutowiredAnnotationBeanPostProcessor解析
@Autowired/@Value注解 - RequiredAnnotationBeanPostProcessor解析
@Required注解 - CommonAnnotationBeanPostProcessor解析
@Resource注解 - PersistenceAnnotationBeanPostProcessor解析JPA注解,持久層
小結
context:component-scan節點的屬性及其含義
- base-package 掃描的基礎包名,必填項,也可指定多個包名,以
,;分隔- use-default-filters 默認為true,如果設置為false,則不啟用
@Component及其相關注解- resource-pattern 自定義掃描的文件名,支持正則匹配,默認為
**/*.class- name-generator beanName生成器
- scope-resolver 指定bean的作用范圍溶解器 與
scope-proxy分開使用- scope-proxy 與
scope-resolver分開使用,targetClass:cglib代理、interfaces:JDK代理、no:不使用代理 ,默認使用cglib代理- context:include-filter/context:exclude-filter 子節點,可有多個,表示可對beanDefinition上的注解過濾
具體的通過掃描base-package指定的包名來得到所有的class文件,請看>>>Spring源碼情操陶冶-PathMatchingResourcePatternResolver路徑資源匹配溶解器。注意:掃描一個類的時候,如果其內部有靜態內部類也是會被掃描注冊的
此
context:component-scan的指定表明默認可將base-package指定的包下的所有注解class比如@Service等注冊為bean到bean工廠,方便后續的業務調用注冊BeanFactoryPostProcessor/BeanPostProcessor接口實現類用於后續的bean實例化,比如
ConfigurationClassPostProcessor解析@Configuration注解類、AutowiredAnnotationBeanPostProcessor解析@Autowired/@Value注解、RequiredAnnotationBeanPostProcessor解析@Required注解、CommonAnnotationBeanPostProcessor解析@Resource注解、PersistenceAnnotationBeanPostProcessor解析JPA注解
