• Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器


    承接前文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文件并对其进行过滤,过滤的条件为includeFiltersexcludeFilters集合。代码简析如下

    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注解,持久层

    小结

    1. 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上的注解过滤
    1. 具体的通过扫描base-package指定的包名来得到所有的class文件,请看>>>Spring源码情操陶冶-PathMatchingResourcePatternResolver路径资源匹配溶解器注意:扫描一个类的时候,如果其内部有静态内部类也是会被扫描注册的

    2. context:component-scan的指定表明默认可将base-package指定的包下的所有注解class比如@Service等注册为bean到bean工厂,方便后续的业务调用

    3. 注册BeanFactoryPostProcessor/BeanPostProcessor接口实现类用于后续的bean实例化,比如ConfigurationClassPostProcessor解析@Configuration注解类、AutowiredAnnotationBeanPostProcessor解析@Autowired/@Value注解、RequiredAnnotationBeanPostProcessor解析@Required注解、CommonAnnotationBeanPostProcessor解析@Resource注解、PersistenceAnnotationBeanPostProcessor解析JPA注解

  • 相关阅读:
    (数据科学学习手札48)Scala中的函数式编程
    Java中的集合(十三) 实现Map接口的Hashtable
    Java中的集合(十二) 实现Map接口的WeakHashMap
    Java中的集合(十一) 实现Map接口的TreeMap
    Java集合(十)实现Map接口的HashMap
    Java集合(九)哈希冲突及解决哈希冲突的4种方式
    Java集合(八)哈希表及哈希函数的实现方式
    Java中的集合(七)双列集合顶层接口------Map接口架构
    Java中的集合(六)继承Collection的Set接口
    Java中的集合(五)继承Collection的List接口
  • 原文地址:https://www.cnblogs.com/question-sky/p/6953315.html
Copyright © 2020-2023  润新知