Spring ComponentScan的扫描过程解析

目录
  • XML中的扫描过程
    • ComponentScanBeanDefinitionParser.parse()
    • ComponentScanBeanDefinitionParser#configureScanner
    • ClassPathBeanDefinitionScanner构造方法中会注入默认的Filter。
    • ClassPathBeanDefinitionScanner#doScan
    • ClassPathScanningCandidateComponentProvider#scanCandidateComponents
    • registerAnnotationConfigProcessors
  • 注解的扫描过程
    • ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
    • ConfigurationClassPostProcessor#processConfigBeanDefinitions
    • ConfigurationClassParser#doProcessConfigurationClass
    • ComponentScanAnnotationParser#parse
  • 总结

    XML中的扫描过程

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xmlns:context="http://www.springframework.org/schema/context"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
    	   default-lazy-init="false">
    	<context:component-scan base-package="com.morris.spring.demo.service"/>
    </beans>

    xml中使用自定义标签context实现,最终会调用到ComponentScanBeanDefinitionParser.parse()方法进行解析。

    ComponentScanBeanDefinitionParser.parse()

    // org.springframework.context.annotation.ComponentScanBeanDefinitionParser#parse
    public BeanDefinition parse(Element element, ParserContext parserContext) {
    	// 获得base-package指定的包名
    	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    	basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    	// base-package中可能有多个,用逗号分隔,转换为数组
    	String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
    			ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    
    	// Actually scan for bean definitions and register them.
    	// 创建扫描器
    	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    	// 开始扫描
    	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    
    	// 注册了一些组件
    	// ConfigurationClassPostProcessor
    	// AutowiredAnnotationBeanPostProcessor
    	// CommonAnnotationBeanPostProcessor
    	// EventListenerMethodProcessor
    	// DefaultEventListenerFactory
    	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    
    	return null;
    }
    

    ComponentScanBeanDefinitionParser#configureScanner

    创建扫描器ClassPathBeanDefinitionScanner

    protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
        boolean useDefaultFilters = true;
        if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
            useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
        }
    
        // Delegate bean definition registration to scanner class.
        // ClassPathBeanDefinitionScanner构造方法中添加了默认的includeFilters为@Component
        ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
        scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
        scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
    
        if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
            scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
        }
    
        try {
            parseBeanNameGenerator(element, scanner);
        }
        catch (Exception ex) {
            parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
        }
    
        try {
            parseScope(element, scanner);
        }
        catch (Exception ex) {
            parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
        }
    
        parseTypeFilters(element, scanner, parserContext);
    
        return scanner;
    }
    

    怎么知道扫描哪些注解呢?

    ClassPathBeanDefinitionScanner构造方法中会注入默认的Filter。

    protected void registerDefaultFilters() {
        // 只扫描@Component注解了的类,而@Sevice、@Configuration、@Controller等注解都被@Component修饰
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));
        ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
        try {
            this.includeFilters.add(new AnnotationTypeFilter(
                    ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
            logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
        }
        catch (ClassNotFoundException ex) {
            // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
        }
        try {
            this.includeFilters.add(new AnnotationTypeFilter(
                    ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
            logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - simply skip.
        }
    }
    

    在源码中只会扫描@Component注解,而@Sevice、@Configuration、@Controller等注解都被@Component修饰,最终都会被扫描到。

    ClassPathBeanDefinitionScanner#doScan

    开始扫描:

    // org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
    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) {
    		// 查找basePackage下所有被@Component注解修饰了的类
    		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    		for (BeanDefinition candidate : candidates) {
    			// 上面findCandidateComponents只是为BD设置了几个属性,BD的其他属性并没有初始化,所以需要遍历一次初始化属性并注册到registry
    			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    			candidate.setScope(scopeMetadata.getScopeName());
    			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    			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);
    				// 注册到registry
    				registerBeanDefinition(definitionHolder, this.registry);
    			}
    		}
    	}
    	return beanDefinitions;
    }
    

    ClassPathScanningCandidateComponentProvider#scanCandidateComponents

    查找basePackage下所有被@Component注解修饰了的类:

    // org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    	Set<BeanDefinition> candidates = new LinkedHashSet<>();
    	try {
    		// basePackage=com.morris.spring..service
    		// packageSearchPath=classpath*:com/morris/spring/service/**/*.class
    		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    			resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    		// 获取basePackage下所有的class文件
    		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 {
    					// 使用ASM将class文件的内容封装为MetadataReader对象
    					// 注意这里用的不是反射,反射会加载类,占用堆空间
    					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    					// 判断类是否包含@Component注解
    					if (isCandidateComponent(metadataReader)) {
    						// 封装为BD
    						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    						sbd.setSource(resource);
    						if (isCandidateComponent(sbd)) {
    							if (debugEnabled) {
    								logger.debug("Identified candidate component class: " + resource);
    							}
    							candidates.add(sbd);
    						}
    ... ...
    	return candidates;
    }
    

    registerAnnotationConfigProcessors

    注册了多种重要的组件:

    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    		BeanDefinitionRegistry registry, @Nullable Object source) {
    
    	DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    	if (beanFactory != null) {
    		if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
    			beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
    		}
    		if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
    			beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    		}
    	}
    
    	Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    
    	// BeanFactoryPostProcessor
    	if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    	}
    
    	// BeanPostProcessor
    	if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    	}
    
    	// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    	if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    	}
    
    	// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    	if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition();
    		try {
    			def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
    					AnnotationConfigUtils.class.getClassLoader()));
    		}
    		catch (ClassNotFoundException ex) {
    			throw new IllegalStateException(
    					"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
    		}
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    	}
    
    	// BeanFactoryPostProcessor
    	if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    	}
    
    	if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
    		RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    		def.setSource(source);
    		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    	}
    
    	return beanDefs;
    }
    

    扫描的最终结果就是将类上面有@Component注解的类构建为一个BeanDefinition中,Spring容器中有两个集合来存放这些BeanDefinition:

    • beanDefinitionNames:List<String>,存放所有的BeanDefinition对应的name
    • beanDefinitionMap:Map<String, BeanDefinition>,存放所有的BeanDefinition

    注解的扫描过程

    注解扫描的使用:

    package com.morris.spring.demo.annotation;
    
    import com.morris.spring.demo.service.CityService;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan("com.morris.spring.service")
    public class ComponentScanDemo {
    	public static void main(String[] args) {
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanDemo.class);
    		CityService cityService = applicationContext.getBean(CityService.class);
    		cityService.city();
    	}
    }
    

    使用@ComponentScan注解指定包的扫描,扫描过程将由ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry完成。

    ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    	int registryId = System.identityHashCode(registry);
    	if (this.registriesPostProcessed.contains(registryId)) {
    		throw new IllegalStateException(
    				"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    	}
    	if (this.factoriesPostProcessed.contains(registryId)) {
    		throw new IllegalStateException(
    				"postProcessBeanFactory already called on this post-processor against " + registry);
    	}
    	this.registriesPostProcessed.add(registryId);
    	// 重点
    	processConfigBeanDefinitions(registry);
    }
    

    ConfigurationClassPostProcessor#processConfigBeanDefinitions

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    	String[] candidateNames = registry.getBeanDefinitionNames();
    	// 先收集有@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean的BD
    	for (String beanName : candidateNames) {
    		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
    		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
    			}
    		}
    		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    			// 进来需要@Component、@ComponentScan、@Import、@ImportResource、@Bean
    			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    		}
    	}
        ... ...
    	// Parse each @Configuration class
    	ConfigurationClassParser parser = new ConfigurationClassParser(
    			this.metadataReaderFactory, this.problemReporter, this.environment,
    			this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    	do {
    		// 开始解析
    		parser.parse(candidates);
    		parser.validate();
    

    ConfigurationClassParser#doProcessConfigurationClass

    protected final SourceClass doProcessConfigurationClass(
    		ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
    		throws IOException {
    	... ...
    	// Process any @ComponentScan annotations
    	// 处理@ComponentScan注解,扫描包下带有@Component的注解,与xml中自定义标签context:component-scan的扫描流程一致
    	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    	if (!componentScans.isEmpty() &&
    			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    		for (AnnotationAttributes componentScan : componentScans) {
    			// The config class is annotated with @ComponentScan -> perform the scan immediately
    			Set<BeanDefinitionHolder> scannedBeanDefinitions =
    					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    			// Check the set of scanned definitions for any further config classes and parse recursively if needed
    			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
    				if (bdCand == null) {
    					bdCand = holder.getBeanDefinition();
    				}
    				// checkConfigurationClassCandidate这个里面会特殊处理@Configutation为full
    				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    					parse(bdCand.getBeanClassName(), holder.getBeanName());
    				}
    			}
    		}
    	}
    	... ...
    	return null;
    }
    

    ComponentScanAnnotationParser#parse

    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    	// 解析@ComponentScan注解,构建ClassPathBeanDefinitionScanner
    	// 构建方法中会添加默认的includeFilters为@Component
    	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
    			BeanUtils.instantiateClass(generatorClass));
    	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
    		scanner.setScopedProxyMode(scopedProxyMode);
    	}
    	else {
    		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
    		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    	}
    	scanner.setResourcePattern(componentScan.getString("resourcePattern"));
    	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
    		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    			scanner.addIncludeFilter(typeFilter);
    		}
    	}
    	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
    		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    			scanner.addExcludeFilter(typeFilter);
    		}
    	}
    	boolean lazyInit = componentScan.getBoolean("lazyInit");
    	if (lazyInit) {
    		scanner.getBeanDefinitionDefaults().setLazyInit(true);
    	}
    	Set<String> basePackages = new LinkedHashSet<>();
    	String[] basePackagesArray = componentScan.getStringArray("basePackages");
    	for (String pkg : basePackagesArray) {
    		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
    				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    		Collections.addAll(basePackages, tokenized);
    	}
    	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    		basePackages.add(ClassUtils.getPackageName(clazz));
    	}
    	if (basePackages.isEmpty()) {
    		basePackages.add(ClassUtils.getPackageName(declaringClass));
    	}
    	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    		@Override
    		protected boolean matchClassName(String className) {
    			return declaringClass.equals(className);
    		}
    	});
    	// 开始扫描
    	return scanner.doScan(StringUtils.toStringArray(basePackages));
    }
    

    可以发现注解的扫描最后会调用ClassPathBeanDefinitionScanner#doScan(),与XML中的扫描是同一个方法。

    总结

    • XML的扫描过程发生在obtainFreshBeanFactory(),也就是创建BeanFactory时,而注解的扫描过程发生在invokeBeanFactoryPostProcessors()。
    • XML的扫描会在obtainFreshBeanFactory()时注入ConfigurationClassPostProcessor,而注解的扫描是在创建AnnotationConfigApplicationContext实例时注入ConfigurationClassPostProcessor,如果xml扫描到的类带有@ComponentScan注解,那么还会继续在invokeBeanFactoryPostProcessors()阶段继续扫描。

    本文转自网络,如有侵权请联系客服删除。