preface

@Component and @Service are common annotations in the workplace. How does Spring parse them?

I. @Component resolves the process

To find the entrance

Spring Framework2.0 introduces an extensible XML programming mechanism that requires XML Schema namespaces to be mapped to Handler.

The relationship is configured in/meta-inf /spring.handlers relative to the classpath.

As shown in the figure above, the ContextNamespaceHandler corresponds to the context:… A gateway to analysis.

Finding 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; }

The above code, from the method name, guesses:

FindCandidateComponents: Scan the Component from the classPath and convert it into an alternative BeanDefinition, which is the core method of parsing @Component to do.

profile

FindCandidateComponents in ClassPathScanningCandidateComponentProvider father classes.

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, Public Set<BeanDefinition> findCandidateComponent (String BaseBackage) {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 {metadateReader metadateReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } // return application (((((((())))); // return application (((()))); }}

The general idea of findCandidateComponent is as follows:

  • String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern;Convert the Package to the ClassLoader class resource search path packageSearchPath, for example:com.wl.spring.bootintoclasspath*:com/wl/spring/boot/**/*.class
  • Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);Loads the resources under the search path.
  • isCandidateComponentDetermine if it is an alternative component
  • candidates.add(sbd);Add to the list that returns the result

ClassPathScanningCandidateComponentProvider# isCandidateComponent its source code is as follows:

Protected Boolean isCandidateComponent(MetaDataReader MetaDataReader) throws IOException {for (TypeFilter tf:  this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }

IncludeFilters is set by registerDefaultFilters().

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.} try {// JSR-250 1.1 API (as included in Java EE 6) not available.  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. } }

How does Spring handle @service annotations ????

Second, check the document for ideas

Referring to official documents, the following quote:

https://docs.spring.io/spring…

@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component

The idea is as follows:

@Component is a generic stereotype for any Spring-managed Component. @Repository, @Service, and @Controller are derived from @Component.

@Target({ElementTyp.type}) @Retention(RetentionPolicy.Runtime) @Service // @Service is a branch of @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 ""; }

The @Component is the meta annotation of the @Service. Spring, in all probability, reads the meta annotation of the @Service and treats the @Service as the @Component.

3. Explore the @Component geneticality process

A review of the key in ClassPathScanningCandidateComponentProvider code snippet below:

Private Set<BeanDefinition> (String BasePack) {// MetaDataReader MetaDataReader =getMetadataReaderFactory().getMetadataReader(resource); if(isCandidateComponent(metadataReader)){ //.... } } public final MetadataReaderFactory getMetadataReaderFactory() { if (this.metadataReaderFactory == null) { this.metadataReaderFactory = new CachingMetadataReaderFactory(); } return this.metadataReaderFactory; }

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()); }}

And you can see here

MetadataReader metadataReader =new SimpleMetadataReader(...) ;

2. Check the match method for the key method

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()));
}

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()

// SimpleDetaReader (Resource Resource, simpleDetaReader) @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; }

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; }}

The meta annotation is in the MetaAnnotationMap and returns true if it is in the MetaAnnotationMap.

This core is metaAnnotationMap inside, search AnnotationMetadataReadingVisitor class, didn’t find the assignment place?? ! .

Find the MetaAnnotationMap assignment

So going back to the SimpleMetaReader method,

// The accept method, which is suspicious, Execute SimpleMatchDataReader (Resource Resource, @ Nullable this this) throws IOException {/ / omit other code AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; }

A suspicious statement was found: classreader.accept.

View the Accept method

Public void accept(.. public void accept(.. public void accept(.. public void accept(.. Omit code) {/ / omit other code readElementValues (classVisitor. VisitAnnotation (annotationDescriptor, visible = / * * / true), currentAnnotationOffset, true, charBuffer); }}

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; }}

Here is the core annotationVisitor. The visitEnd ();

Determine annotationVisitor

Here annotationVisitor = AnnotationMetadataReadingVisitor# visitAnnotation

Note the MetaAnnotationMap passed in here!!

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); }}

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); } } } } } }

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

Something like this:

ClassPathScanningCandidateComponentProvider#findCandidateComponents

1. Convert the Package to ClassLoader class resource search path packageSearchPath

2. Load the resources under the search path.

3. IsCandidateComponent determines if it is a candidate component.

Intra-called TypeFilter match method:

  • The metadata in the AnnotationTypeFilter# matchself hasMetaAnnotationProcessing element annotation
  • metadata.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 again) and set it to the MetaAnnotationMap

4. Add to the list that returns results

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, the finishing of the information will be placed in it.

Feel good to write on the point of a like, add a focus on bai! Point of attention, don’t get lost, keep updating!!