preface
@Component and @service are common annotations at work. How does Spring parse them?
@Component parses the flow
To find the entrance
Spring Framework2.0 begins with the introduction of extensible XML programming mechanisms that require XML Schema namespaces to map to handlers.
The relationship is configured in/meta-INF/Spring. handlers relative to classpath.
ContextNamespaceHandler: ContextNamespaceHandler: ContextNamespaceHandler An entry point for analysis.
Find the core method
Browse ContextNamespaceHandler
There is an important comment in parse
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Carelessness is: ClassPathBeanDefinitionScanner# doScan is to scan BeanDefinition and register.
ClassPathBeanDefinitionScanner source code is as follows:
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) {/ / changing all findCandidateComponents reading resources for BeanDefinition 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; }Copy the code
From the above code, guess from the method name:
FindCandidateComponents: Scan the Component from the classPath and transform it into an alternative BeanDefinition, which is the core method to resolve @Component.
profile
FindCandidateComponents in ClassPathScanningCandidateComponentProvider father classes.
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware 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: If (resource.isreadable ()) {try {MetadataReader MetadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); }} catch (IOException ex) {// omit some code} return argument; }}Copy the code
The general idea of findCandidateComponents is as follows:
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Convert package to a ClassLoader resource search path, packageSearchPath, for example:com.wl.spring.boot
intoclasspath*:com/wl/spring/boot/**/*.class
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
Load the resources in the search path.isCandidateComponent
Determine if it is an optional componentcandidates.add(sbd);
Add to the list of returns
ClassPathScanningCandidateComponentProvider# isCandidateComponent its source code is as follows:
Protected Boolean isCandidateComponent(MetadataReader) throws IOException {// omits part of the code for (TypeFilter tf: this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }Copy the code
IncludeFilters are initialized by registerDefaultFilters(), @Component, no @Service.
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) {// 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. } }Copy the code
How does Spring handle @service annotations ????
Second, check the document for ideas
Looking at official documents, here’s what it says:
Docs. Spring. IO/spring/docs…
@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component
The general idea is as follows:
@Component is a generic prototype for any Spring-managed Component. @Repository, @Service, and @Controller are derived from @Component.
@target ({elementtype.type}) @Retention(retentionPolicy.runtime) @documented // @Service Derived from @Component @Component public @interface Service { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any (or empty String otherwise) */ @AliasFor(annotation = Component.class) String value() default ""; }Copy the code
@Component is the meta annotation of @Service. Spring will probably read the meta annotation of @Service as well and treat @Service as @Component.
Explore the @Component generative process
A review of the key in ClassPathScanningCandidateComponentProvider code snippet below:
Private Set<BeanDefinition> scanCandidateComponents(String basePackage) {MetadataReader MetadataReader =getMetadataReaderFactory().getMetadataReader(resource); if(isCandidateComponent(metadataReader)){ //.... } } public final MetadataReaderFactory getMetadataReaderFactory() { if (this.metadataReaderFactory == null) { this.metadataReaderFactory = new CachingMetadataReaderFactory(); } return this.metadataReaderFactory; }Copy the code
1. Determine the metadataReader
CachingMetadataReaderFactory inherited from SimpleMetadataReaderFactory, is the SimpleMetadataReaderFactory adds a layer of caching.
Its internal SimpleMetadataReaderFactory# getMetadataReader as follows:
public class SimpleMetadataReaderFactory implements MetadataReaderFactory{ @Override public MetadataReader getMetadataReader(Resource resource) throws IOException { return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader()); }}Copy the code
And you can see here
MetadataReader metadataReader =new SimpleMetadataReader(...) ;
2. Check the match method for key methods
AnnotationTypeFilter#matchself
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
Copy the code
Is the metadata hasMetaAnnotation method, from the name is yuan notes, we focus on
Analysis step by step
To find the metadata. HasMetaAnnotation
metadata=metadataReader.getAnnotationMetadata();
metadataReader =new SimpleMetadataReader(...)
metadata= new SimpleMetadataReader#getAnnotationMetadata()
SimpleMetadataReader(Resource Resource, @Nullable ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader; try { classReader = new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIOException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); } finally { is.close(); } AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor) this.classMetadata = visitor; this.resource = resource; }Copy the code
metadata=new SimpleMetadataReader(...) GetAnnotationMetadata () = new AnnotationMetadataReadingVisitor (.).
That is to say,
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
The method is as follows:
Public class AnnotationMetadataReadingVisitor {/ / omit part of the code @ Override public Boolean hasMetaAnnotation (String metaAnnotationType) { Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values(); for (Set<String> metaTypes : allMetaTypes) { if (metaTypes.contains(metaAnnotationType)) { return true; } } return false; }}Copy the code
The logic is simple: the metaannotation is in metaAnnotationMap, and returns true if it is.
This core is metaAnnotationMap inside, search AnnotationMetadataReadingVisitor class, didn’t find the assignment place?? ! .
Find metaAnnotationMap assignments
Going back to the SimpleMetadataReader method,
// The accept method is suspicious. SimpleMetadataReader(Resource Resource, @ Nullable this this) throws IOException {/ / omit other code AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; }Copy the code
A suspicious statement was found: classreader.accept.
Look at the Accept method
Public class reader {// omit other code public void accept(.. Omit code) {/ / omit other code readElementValues (classVisitor. VisitAnnotation (annotationDescriptor, visible = / * * / true), currentAnnotationOffset, true, charBuffer); }}Copy the code
Look at the readElementValues method
Private int readElementValues(final AnnotationVisitor AnnotationVisitor, final int annotationOffset, final boolean named, final char[] charBuffer) { int currentOffset = annotationOffset; // Read the num_element_value_pairs field (or num_values field for an array_value). int numElementValuePairs = readUnsignedShort(currentOffset); currentOffset += 2; if (named) { // Parse the element_value_pairs array. while (numElementValuePairs-- > 0) { String elementName = readUTF8(currentOffset, charBuffer); currentOffset = readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer); } } else { // Parse the array_value array. while (numElementValuePairs-- > 0) { currentOffset = readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer); } } if (annotationVisitor ! = null) { annotationVisitor.visitEnd(); } return currentOffset; }}Copy the code
Here is the core annotationVisitor. The visitEnd ();
Determine annotationVisitor
Here annotationVisitor = AnnotationMetadataReadingVisitor# visitAnnotation
The source code is as follows, note that here passed metaAnnotationMap!!
public class AnnotationMetadataReadingVisitor{
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
String className = Type.getType(desc).getClassName();
this.annotationSet.add(className);
return new AnnotationAttributesReadingVisitor(
className, this.attributesMap,
this.metaAnnotationMap, this.classLoader);
}
}
Copy the code
annotationVisitor=AnnotationAttributesReadingVisitor
Refer to annotationVisitor. The visitEnd ()
annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()
public class AnnotationAttributesReadingVisitor{ @Override public void visitEnd() { super.visitEnd(); Class<? extends Annotation> annotationClass = this.attributes.annotationType(); if (annotationClass ! = null) { List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType); if (attributeList == null) { this.attributesMap.add(this.annotationType, this.attributes); } else { attributeList.add(0, this.attributes); } if (! AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) { try { Annotation[] metaAnnotations = annotationClass.getAnnotations(); if (! ObjectUtils.isEmpty(metaAnnotations)) { Set<Annotation> visited = new LinkedHashSet<>(); for (Annotation metaAnnotation : metaAnnotations) { recursivelyCollectMetaAnnotations(visited, metaAnnotation); } if (! visited.isEmpty()) { Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size()); for (Annotation ann : visited) { metaAnnotationTypeNames.add(ann.annotationType().getName()); } this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex); } } } } } }Copy the code
Internal methods recursivelyCollectMetaAnnotations recursive reading notes, and annotations of RMB notes (read @ Service, read comments @ Component), and set to metaAnnotationMap, That is in the metaAnnotationMap AnnotationMetadataReadingVisitor.
conclusion
Roughly as follows:
ClassPathScanningCandidateComponentProvider#findCandidateComponents
1. Convert package to packageSearchPath for searching resources of the ClassLoader class
2. Load the resources in the search path.
3. IsCandidateComponent Check whether the component is an alternative component.
Internally called TypeFilter match method:
The metadata in the AnnotationTypeFilter# matchself hasMetaAnnotation
Processing meta annotationmetadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
Is to determine whether the meta-annotation of the current annotation is in the metaAnnotationMap.
AnnotationAttributesReadingVisitor# visitEnd () method of internal recursivelyCollectMetaAnnotations recursive reading notes, and annotations of RMB notes (read @ Service, Read the meta annotation @Component) and set it to the metaAnnotationMap
4. Add to the list of returns
Write in the last
Welcome to pay attention to my public number [calm as code], massive Java related articles, learning materials will be updated in it, sorting out the data will be placed in it.
If you think it’s written well, click a “like” and add a follow! Point attention, do not get lost, continue to update!!