公众号:Java小咖秀,网站:[javaxks.com](https://www.javaxks.com)
作者 :Jitwxs ,链接: https://jitwxs.cn/4135e0a9.html
前言
@Component 和 @Service 都是工作中常用的注解,Spring 如何解析?
1.@Component 解析流程
找入口
Spring Framework2.0 开始,引入可扩展的 XML 编程机制,该机制要求 XML Schema 命名空间需要与 Handler 建立映射关系。
该关系配置在相对于 classpath 下的/META-INF/spring.handlers 中。
如上图所示 ContextNamespaceHandler 对应 context:... 分析的入口。
找核心方法
浏览 ContextNamespaceHandler
在 parse 中有一个很重要的注释
// Actually scan for bean definitions and register them.ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
大意是:ClassPathBeanDefinitionScanner#doScan 是扫描 BeanDefinition 并注册的实现 。
ClassPathBeanDefinitionScanner 的源码如下:
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) { //findCandidateComponents 读资源装换为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;}
复制代码
上边的代码,从方法名,猜测:
findCandidateComponents:从 classPath 扫描组件,并转换为备选 BeanDefinition,也就是要做的解析 @Component 的核心方法。推荐:Java 面试练题宝典
概要分析
findCandidateComponents 在其父类 ClassPathScanningCandidateComponentProvider 中。
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 : resources) { //省略部分代码 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) {//省略部分代码 } return candidates;}}
复制代码
findCandidateComponents 大体思路如下:
String packageSearchPath = ResourcePatternResolver.CLASSPATHALLURL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern; 将 package 转化为 ClassLoader 类资源搜索路径 packageSearchPath,例如:com.wl.spring.boot 转化为 classpath:com/wl/spring/boot/*/*.class
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加载搜素路径下的资源。
isCandidateComponent 判断是否是备选组件
candidates.add(sbd); 添加到返回结果的 list
ClassPathScanningCandidateComponentProvider#isCandidateComponent 其源码如下:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //省略部分代码 for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false;}
复制代码
includeFilters 由 registerDefaultFilters()设置初始值,有 @Component,没有 @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. }}
复制代码
Spring 如何处理 @Service 的注解的呢????
2.查文档找思路
查阅官方文档,下面这话:
https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations
>@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented// @Service 派生自@Component@Componentpublic @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 "";
}
复制代码
@Component 是 @Service 的元注解,Spring 大概率,在读取 @Service,也读取了它的元注解,并将 @Service 作为 @Component 处理。
3. 探寻 @Component 派生性流程
回顾 ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:
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;}
复制代码
1. 确定 metadataReader
CachingMetadataReaderFactory 继承自 SimpleMetadataReaderFactory,就是对 SimpleMetadataReaderFactory 加了一层缓存。
其内部的 SimpleMetadataReaderFactory#getMetadataReader 为:
public class SimpleMetadataReaderFactory implements MetadataReaderFactory{ @Override public MetadataReader getMetadataReader(Resource resource) throws IOException { return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader()); }}
复制代码
这里可以看出
MetadataReader metadataReader =new SimpleMetadataReader(...);
2.查看 match 方法找重点方法
AnnotationTypeFilter#matchself 方法如下:
@Overrideprotected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));}
复制代码
是 metadata.hasMetaAnnotation 法,从名称看是处理元注解,我们重点关注
逐步分析
找 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;}
复制代码
metadata=new SimpleMetadataReader(...).getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)
也就是说
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
其方法如下:
public class AnnotationMetadataReadingVisitor{ // 省略部分代码@Overridepublic boolean hasMetaAnnotation(String metaAnnotationType) { Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values(); for (Set<String> metaTypes : allMetaTypes) { if (metaTypes.contains(metaAnnotationType)) { return true; } } return false;}}
复制代码
逻辑很简单,就是判断该注解的元注解在,在不在 metaAnnotationMap 中,如果在就返回 true。
这里面核心就是 metaAnnotationMap,搜索 AnnotationMetadataReadingVisitor 类,没有发现赋值的地方??!。
推荐:Java 面试练题宝典
查找 metaAnnotationMap 赋值
回到 SimpleMetadataReader 的方法,
//这个accept方法,很可疑,在赋值之前执行SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {//省略其他代码AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; } ``` 发现一个可疑的语句:classReader.accept。
查看accept方法
复制代码
public class ClassReader {
//省略其他代码
public void accept(..省略代码){
//省略其他代码
readElementValues(
classVisitor.visitAnnotation(annotationDescriptor, / visible = / true),
currentAnnotationOffset,
true,
charBuffer);
}
}
public class ClassReader{
//省略其他代码
private int readElementValues(
final AnnotationVisitor annotationVisitor,
final int annotationOffset,
final boolean named,
final char[] charBuffer) {
int currentOffset = annotationOffset;
// Read the numelementvaluepairs field (or numvalues field for an array_value).
int numElementValuePairs = readUnsignedShort(currentOffset);
currentOffset += 2;
if (named) {
// Parse the elementvaluepairs 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;
}
}
这里面的核心就是 annotationVisitor.visitEnd();
#### 确定annotationVisitor这里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation
源码如下,注意这里传递了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);
}
}
annotationVisitor=AnnotationAttributesReadingVisitor
#### 查阅annotationVisitor.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);
}
}
}
}
}
}
```
内部方法 recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读 @Service,再读元注解 @Component),并设置到 metaAnnotationMap,也就是 AnnotationMetadataReadingVisitor 中的 metaAnnotationMap 中。
总结
大致如下:
ClassPathScanningCandidateComponentProvider#findCandidateComponents
1.将 package 转化为 ClassLoader 类资源搜索路径 packageSearchPath
2.加载搜素路径下的资源。
3.isCandidateComponent 判断是否是备选组件。
内部调用的 TypeFilter 的 match 方法:
就是判断当前注解的元注解在不在 metaAnnotationMap 中。
AnnotationAttributesReadingVisitor#visitEnd()内部方法 recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读 @Service,再读元注解 @Component),并设置到 metaAnnotationMap
4.添加到返回结果的 list
评论