本篇文章主要介绍 context:component-scan
标签的解析过程。
前言 我们在了解 Spring 容器的扩展功能 (ApplicationContext) 之前,先介绍下 context:component-scan
标签的解析过程,其作用很大是注解能生效的关键所在。
正文 我们此次直接从 BeanDefinitionParseDelegate#parseCustomElement()
开始往下分析,不知道前面流程的可以看一下 Spring XML Bean 定义的加载和注册 这篇文章,之前的流程都有解析。
BeanDefinitionParseDelegate#parseCustomElement() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public BeanDefinition parseCustomElement (Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null ) { return null ; } NamespaceHandler handler = this .readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null ) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]" , ele); return null ; } return handler.parse(ele, new ParserContext(this .readerContext, this , containingBd)); }
ComponentScanBeanDefinitionParser#parse 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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); ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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)); } 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; }
ComponentScanBeanDefinitionParser#createScanner 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected ClassPathBeanDefinitionScanner createScanner (XmlReaderContext readerContext, boolean useDefaultFilters) { return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,readerContext.getEnvironment(), readerContext.getResourceLoader()); } 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); }
ClassPathScanningCandidateComponentProvider#registerDefaultFilters 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void registerDefaultFilters () { 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) { } 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) { } }
ClassPathBeanDefinitionScanner#doScan 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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) { 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); 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); registerBeanDefinition(definitionHolder, this .registry); } } } return beanDefinitions; }
ClassPathScanningCandidateComponentProvider#findCandidateComponents 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public Set<BeanDefinition> findCandidateComponents (String basePackage) { if (this .componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this .componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); } } private Set<BeanDefinition> scanCandidateComponents (String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this .resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) { if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); 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; }
上面代码中判断该类是不是候选的组件的方法 isCandidateComponent(metadataReader)
一般情况下 是判断该类是否标注了上文中 ClassPathScanningCandidateComponentProvider#registerDefaultFilters()
中的注解 @Component
、@ManagedBean
、@Named
;@Component
注解是肯定会存在与当前环境中的,所以标注了 @Compont
或者其派生的注解如:@Repository
、@Service
、@Controller
都会被当做候选组件。
AnnotationBeanNameGenerator#generateBeanName 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public String generateBeanName (BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); if (StringUtils.hasText(beanName)) { return beanName; } } return buildDefaultBeanName(definition, registry); }
AnnotationConfigUtils#processCommonDefinitionAnnotations 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static void processCommonDefinitionAnnotations (AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy != null ) { abd.setLazyInit(lazy.getBoolean("value" )); } else if (abd.getMetadata() != metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy != null ) { abd.setLazyInit(lazy.getBoolean("value" )); } } if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true ); } AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null ) { abd.setDependsOn(dependsOn.getStringArray("value" )); } AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null ) { abd.setRole(role.getNumber("value" ).intValue()); } AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null ) { abd.setDescription(description.getString("value" )); } }
ComponentScanBeanDefinitionParser#registerComponents 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 protected void registerComponents (XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) { Object source = readerContext.extractSource(element); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source); for (BeanDefinitionHolder beanDefHolder : beanDefinitions) { compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder)); } boolean annotationConfig = true ; if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) { annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE)); } if (annotationConfig) { Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source); for (BeanDefinitionHolder processorDefinition : processorDefinitions) { compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition)); } } readerContext.fireComponentRegistered(compositeDef); }
AnnotationConfigUtils#registerAnnotationConfigProcessors 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 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 ); 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)); } 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)); } 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)); } 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)); } 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; }
总结 本文主要介绍了 Spring 解析 context:component-scan
标签的解析流程,我们可以重新整理一下思路:
首先添加默认的过滤器,也就是判定哪些类会被作为组件来注册;比如默认过滤器添加后,会将标注了 @Component
注解以及它的派生注解当做组件注册。
找到指定包路径下所有符合条件的类并封装成 BeanDefinition
;解析 @Scope
注解,没有默认为 singleton
;生成 bean
的名称,首先会用 @Component
的 value
属性,如果为空或空字符串则默认使用类名,并把首字母转成小写;然后对一些注解的解析,如 @Lazy
、@Primary
、@DependsOn
等;最后检查注册中心中是否包含当前 beanName
,如果没有直接添加进注册中心,否则如果不是同一个类会抛出异常。比如 com.leisurexi.a.User
和 com.leisurexi.b.User
同时添加 @Component
注解并且不手动指定不同的 beanName
在启动时就会抛出异常。
最后注册中心注册一些注解的后置处理器,如下:
处理 @Configuration
注解的 ConfigurationClassPostProcessor
。
处理 @Autowired
、@Value
、@Inject
注解的 AutowiredAnnotationBeanPostProcessor
。
处理 @Resource
、@PostConstructor
、@PostDestroy
注解的 CommonAnnotationBeanPostProcessor
。
处理JPA注解的 PersistenceAnnotationBeanPostProcessor
。
处理 @EventListener
注解的 EventListenerMethodProcessor
。
最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring 。访问新博客地址,观看效果更佳 https://leisurexi.github.io/