Scanning process in XML
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="/schema/beans" xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:context="/schema/context" xsi:schemaLocation="/schema/beans /schema/beans/ /schema/context /schema/context/" default-lazy-init="false"> <context:component-scan base-package=""/> </beans>
Implemented with custom tag context in xml, and finally the() method will be called for parsing.
()
// #parse public BeanDefinition parse(Element element, ParserContext parserContext) { // Get the package name specified by base-package String basePackage = (BASE_PACKAGE_ATTRIBUTE); basePackage = ().getEnvironment().resolvePlaceholders(basePackage); // There may be multiple in base-package, separated by commas, converted to array String[] basePackages = (basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. // Create a scanner ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); // Start scanning Set<BeanDefinitionHolder> beanDefinitions = (basePackages); // Some components have been registered // ConfigurationClassPostProcessor // AutowiredAnnotationBeanPostProcessor // CommonAnnotationBeanPostProcessor // EventListenerMethodProcessor // DefaultEventListenerFactory registerComponents((), beanDefinitions, element); return null; }
ComponentScanBeanDefinitionParser#configureScanner
Create a scanner ClassPathBeanDefinitionScanner
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { boolean useDefaultFilters = true; if ((USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = ((USE_DEFAULT_FILTERS_ATTRIBUTE)); } // Delegate bean definition registration to scanner class. // The default includeFilters is added to the ClassPathBeanDefinitionScanner constructor method as @Component ClassPathBeanDefinitionScanner scanner = createScanner((), useDefaultFilters); (().getBeanDefinitionDefaults()); (().getAutowireCandidatePatterns()); if ((RESOURCE_PATTERN_ATTRIBUTE)) { ((RESOURCE_PATTERN_ATTRIBUTE)); } try { parseBeanNameGenerator(element, scanner); } catch (Exception ex) { ().error((), (element), ()); } try { parseScope(element, scanner); } catch (Exception ex) { ().error((), (element), ()); } parseTypeFilters(element, scanner, parserContext); return scanner; }
How do you know which annotations to scan?
The default Filter will be injected into the ClassPathBeanDefinitionScanner constructor.
protected void registerDefaultFilters() { // Only scan the classes annotated by @Component, and the annotations such as @Sevice, @Configuration, @Controller are all modified by @Component (new AnnotationTypeFilter()); ClassLoader cl = (); try { (new AnnotationTypeFilter( ((Class<? extends Annotation>) ("", cl)), false)); ("JSR-250 '' 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 { (new AnnotationTypeFilter( ((Class<? extends Annotation>) ("", cl)), false)); ("JSR-330 '' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
In the source code, only the @Component annotation will be scanned, while the @Sevice, @Configuration, @Controller and other annotations are modified by @Component and will eventually be scanned.
ClassPathBeanDefinitionScanner#doScan
Start scanning:
// #doScan protected Set<BeanDefinitionHolder> doScan(String... basePackages) { (basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // Find all classes under basePackage that are modified by @Component annotation Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // The above findCandidateComponents only sets several properties for BD. Other properties of BD are not initialized, so you need to traverse the initialization properties and register them in the registry ScopeMetadata scopeMetadata = (candidate); (()); String beanName = (candidate, ); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { ((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = (scopeMetadata, definitionHolder, ); (definitionHolder); // Register to registry registerBeanDefinition(definitionHolder, ); } } } return beanDefinitions; }
ClassPathScanningCandidateComponentProvider#scanCandidateComponents
Find all classes under basePackage that are modified by @Component annotation:
// #scanCandidateComponents private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // basePackage=..service // packageSearchPath=classpath*:com/morris/spring/service/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + ; // Get all class files under basePackage Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = (); boolean debugEnabled = (); for (Resource resource : resources) { if (traceEnabled) { ("Scanning " + resource); } if (()) { try { // Use ASM to encapsulate the contents of the class file into a MetadataReader object // Note that the reflection is not used here, the reflection will load the class and occupy heap space MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // Determine whether the class contains @Component annotation if (isCandidateComponent(metadataReader)) { // Package as BD ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); (resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { ("Identified candidate component class: " + resource); } (sbd); } ... ... return candidates; }
registerAnnotationConfigProcessors
Registered with a variety of important components:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(() instanceof AnnotationAwareOrderComparator)) { (); } if (!(() instanceof ContextAnnotationAutowireCandidateResolver)) { (new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); // BeanFactoryPostProcessor if (!(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); (source); (registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // BeanPostProcessor if (!(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); (source); (registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); (source); (registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { ((PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } (source); (registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } // BeanFactoryPostProcessor if (!(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); (source); (registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); (source); (registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
The final result of the scan is to build a class with @Component annotation on the class into a BeanDefinition, and there are two collections in the Spring container to store these BeanDefinitions:
- beanDefinitionNames: List<String>, store all the corresponding names of BeanDefinitions
- beanDefinitionMap: Map<String, BeanDefinition>, store all BeanDefinitions
Annotation scanning process
Use of annotation scans:
package ; import ; import ; import ; @ComponentScan("") public class ComponentScanDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); CityService cityService = (); (); } }
Use @ComponentScan annotation to specify the scan of the package, and the scanning process will be done by ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry.
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = (registry); if ((registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if ((registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } (registryId); // Focus processConfigBeanDefinitions(registry); }
ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = (); // First collect the BDs with @Configuration, @Component, @ComponentScan, @Import, @ImportResource, @Bean for (String beanName : candidateNames) { BeanDefinition beanDef = (beanName); if ((ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (()) { ("Bean definition has already been processed as a configuration class: " + beanDef); } } else if ((beanDef, )) { // You need @Component, @ComponentScan, @Import, @ImportResource, @Bean (new BeanDefinitionHolder(beanDef, beanName)); } } ... ... // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( , , , , , registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(()); do { // Start parsing (candidates); ();
ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { ... ... // Process any @ComponentScan annotations // Process @ComponentScan annotation, and the annotation with @Component under the scan package is consistent with the scanning process of the custom label context:component-scan in xml Set<AnnotationAttributes> componentScans = ( (), , ); if (!() && !((), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = (componentScan, ().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = ().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = (); } // CheckConfigurationClassCandidate will be handled specifically in @Configutation as full if ((bdCand, )) { parse((), ()); } } } } ... ... return null; }
ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { // parse @ComponentScan annotation and build ClassPathBeanDefinitionScanner // The default includeFilters will be added to the build method as @Component ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(, ("useDefaultFilters"), , ); Class<? extends BeanNameGenerator> generatorClass = ("nameGenerator"); boolean useInheritedGenerator = ( == generatorClass); (useInheritedGenerator ? : (generatorClass)); ScopedProxyMode scopedProxyMode = ("scopedProxy"); if (scopedProxyMode != ) { (scopedProxyMode); } else { Class<? extends ScopeMetadataResolver> resolverClass = ("scopeResolver"); ((resolverClass)); } (("resourcePattern")); for (AnnotationAttributes filter : ("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { (typeFilter); } } for (AnnotationAttributes filter : ("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { (typeFilter); } } boolean lazyInit = ("lazyInit"); if (lazyInit) { ().setLazyInit(true); } Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = ("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = ((pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); (basePackages, tokenized); } for (Class<?> clazz : ("basePackageClasses")) { ((clazz)); } if (()) { ((declaringClass)); } (new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return (className); } }); // Start scanning return ((basePackages)); }
It can be found that the annotated scan will call ClassPathBeanDefinitionScanner#doScan() at the end, which is the same method as the scan in XML.
Summarize
- The XML scanning process occurs when obtainFreshBeanFactory(), that is, when creating a BeanFactory, and the annotated scanning process occurs when invokeBeanFactoryPostProcessors().
- XML scans will inject ConfigurationClassPostProcessor when obtainFreshBeanFactory(), and the annotated scan is injected into ConfigurationClassPostProcessor when creating an AnnotationConfigApplicationContext instance. If the class scanned by the xml has the @ComponentScan annotation, it will continue to scan in the invokeBeanFactoryPostProcessors() stage.
This is the end of this article about the scanning process of spring ComponentScan. For more related spring ComponentScan scanning content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!