component-scan标签解析

上篇博客中,介绍了对于自定义标签的解析,以及自己实现了一个标签供spring扫描,此篇博客会主要分析下component-scan 标签的解析过程中做了哪些事情.

解析前的准备

首先在xml文件的beans标签中引入context标签
在这里插入图片描述

 <context:component-scan base-package="com"></context:component-scan>

在xml文件中加入上面的代码 base-package指的是扫描的包名
当spring启动后,解析到该段时,是会进入到自定义标签的解析的.

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
		registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
		registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
		registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
	}
}

context所对应的handler会注册上述的八个标签,其中当使用的是component-scan时,ComponentScanBeanDefinitionParser会负责去解析该标签

ComponentScanBeanDefinitionParser的 parse(Element element, ParserContext parserContext)方法

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		//BASE_PACKAGE_ATTRIBUTE就是base-package,获取base-package的值
		String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
		//这里貌似是对路径进行解析,可以支持"${}"这种写法,从Enviroment中获取resources注入进去
		// <context:component-scan base-package="${LOCALAPPDATA}"></context:component-scan>这种配置下
		//${LOCALAPPDATA}被正常解析了,主要还是要Enviroment中得propertySources拥有,应该都可以在此注入
		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
		//如果是多个包名,会在此被分析出来
		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// 主角在此,获取一个 配置好的ClassPathBeanDefinitionScanner 对象
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		//用ClassPathBeanDefinitionScanner对象去执行扫描(核心方法)
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		//这里面将比较常用的内置的后置处理器注册到了BeanDenifitions里面
		//ConfigurationClassPostProcessor.class
		//AutowiredAnnotationBeanPostProcessor.class
		//CommonAnnotationBeanPostProcessor.class等.这些后置出理解中ConfigurationClassPostProcessor.class
		//非常重要,后续会详细分析
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}

扫描器的创建

ComponentScanBeanDefinitionParser的configureScanner(parserContext, element)方法

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
	//使用默认的过滤注解
	boolean useDefaultFilters = true;
	//在这里解析use-default-filters标签,如果xml中设置了,则会按照设置的值去设置useDefaultFilters
	if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
		useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
	}
	//在此创建一个ClassPathBeanDefinitionScanner,创建的过程后续会解析
	ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
	//后面是对scanner的进行一些配置,主要是根据xml文件的配置去配置的
	......
	return scanner;
	}

ComponentScanBeanDefinitionParserconfigure的 createScanner(XmlReaderContext readerContext, boolean useDefaultFilters)方法

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
		//本质也只是new一个对象而已
		return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
				readerContext.getEnvironment(), readerContext.getResourceLoader());
	}

ClassPathBeanDefinitionScanner的构造方法

	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;
		//是否使用默认的过滤注解
		if (useDefaultFilters) {
			//注册默认的注解
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

ClassPathBeanDefinitionScanner的registerDefaultFilters()方法

protected void registerDefaultFilters() {
		//在此处添加Component.class注解为会被扫描到的注解,@Configuration,@Service等注解都是该注解的子类也都会
		//被扫描出来,this.includeFilters在ClassPathScanningCandidateComponentProvider的
		//isCandidateComponent(MetadataReader metadataReader)方法中会被使用到		   
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		....
	}

扫描器的扫描

ClassPathScanningCandidateComponentProvider类是ClassPathBeanDefinitionScanner的父类

ClassPathBeanDefinitionScanner的doScan(String… basePackages)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	//对包进行遍历
	for (String basePackage : basePackages) {
		//递归扫描包得出候选的BeanDefinition(带有Component.class注解或者它的子注解都会被扫描到)
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			//对被扫描出来的BeanDenifiton进行信息的补全
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				//将符合条件的BeanDefinition注册到IOC容器中(此时还只是BeanDenifiton)
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

ClassPathScanningCandidateComponentProvider的scanCandidateComponents(String basePackage)方法

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			//补全扫描规则前面加classpath*:,将"."替换为"/"后面加/**/*.class,
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			//递归操作解析路径名下所有文件并转化为Resource
			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 {
						//将Resource对象转化为metadataReader对象,便于使用
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						//此方法判断是否有@component注解或其字注解
						if (isCandidateComponent(metadataReader)) {
							//自己new一个被扫描的BeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								//加入候选的BeanDefinition中
								candidates.add(sbd);
							}
							......
					return candidates;
	}

ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)方法

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		//如果定义了排斥的注解,则返回false,即不加入候选的BeanDenitions
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		//this.includeFilters就是初始化时注册了@Component注解的类,包含了该注解会返回true,即会被扫描到
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

至此,component-scan标签的解析如此便完成了

扫描器的运用

稍稍改造一下上一篇博客的ZkParser类

public class ZkParser implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
    	//自己实例化一个扫描器,不使用默认的检索注解
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(
                parserContext.getRegistry(),false
        );
        //将自己定义的注解添加进去
        scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
        scanner.scan("com.zk.fiveDay");
        System.out.println("zk标签被解析了");
        return null;
    }
}
	

实验对象代码

@MyAnnotation
public class Phone {
    private String color;
}

启动类

    public void a() throws InterruptedException {
        new AnnotationConfigApplicationContext();
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("spring.xml");
        System.out.println(applicationContext.getBean(Phone.class));}

最后的结果是
在这里插入图片描述
成功!!!

其实,component-scan标签的解析,就是实例化了一个ClassPathBeanDefinitionScanner的扫描器去做扫描.然后将扫描出来的class文件进行筛选,筛选方法是判断class文件中是否拥有@Component或其子类注解,而这个筛选条件是在实例化的时候注册进去的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值