@Component,@Service等注解是如何被解析的
前言
1.@Component解析流程
找入口
找核心方法
// Actually scan for bean definitions and register them.ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
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; }
上边的代码,从方法名,猜测:
概要分析
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.CLASSPATH_ALL_URL_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
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //省略部分代码 for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
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.查文档找思路
@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 @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 ""; }
@Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。
3. 探寻@Component派生性流程
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
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方法找重点方法
@Override protected 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
//SimpleMetadataReader 的构造方法 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(。。)
public class AnnotationMetadataReadingVisitor{ // 省略部分代码 @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; } }
逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。
这里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。
查找metaAnnotationMap赋值
//这个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); } }
查看readElementValues方法
public class ClassReader{ //省略其他代码 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; } }
这里面的核心就是 annotationVisitor.visitEnd();
确定annotationVisitor
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()
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中。
总结

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
浪潮信息:公司目前生产经营正常 Intel已恢复对其供货
7月3日消息,浪潮信息在互动平台表示,公司目前生产经营正常,Intel已恢复对浪潮的供货。 6月29日晚间11点59分,Intel已临时暂停向中国最大的服务器厂商浪潮(Inspur)供货。随后Intel方面已回应称:他们需要根据美国相关法律对我们的供应链做出一些相应的调整,因而不得不临时性暂停对此客户的供货。这次临时性暂停预计两周以内,届时我们将恢复对此客户的供货。 行业人士直言,这次不是绝对意义上的“断供”,只是临时“暂停供货”。 由于Intel x86在服务器芯片市场的几乎垄断地位,包括浪潮在内的绝大多数中国服务器厂商,一直无法摆脱对Intel在底层芯片架构上的重度依赖。 根据Gartner的2020年Q1报告数据,浪潮占中国服务器37.6%的市场份额位居第一,全球9.6%的市场份额位居第三。而且Intel也是浪潮第一大供应商,2019年采购额178.96亿元,占比37.53%;2018年采购额145.76亿元,占比31.51%。 这样的体量,让作为芯片供应商的Intel肯定不想断供自己的大客户,但包括Intel在内的美国企业有时候也是身不由己。毕竟为了打压中国企业尤其是打压中国科...
- 下一篇
键盘手焊还不够,里面跑Python更过瘾
谁要自己做个键盘呢?还是要手焊的键盘~ 如果,你日常写的是软件,最接近硬件制板的一次是大学时焊接收音机,那你跟我一样,当然可以试试做个键盘;或者,你平时设计的是硬件,手焊个键盘当然不在话下,写个Python让键盘跑起来也许会让你兴奋;亦或,你刚学完朋友圈里Python课程,正准备大展身手,你的第一个Python项目,可以是个键盘,感受把Python握在手里的感觉…… 焊接本文中的键盘,笔者从早到晚花了一整天,而第一版让键盘跑起来的Python程序仅用了2小时,于是就有一个支持USB和蓝牙的键盘,再给键盘装上斑斓的色彩! 如果你只关心跑在键盘里的Python长什么模样,可以直接跳到文章的倒数第100行,代码放了在最后~ 涉及硬件,准备材料少不了。 准备材料 黄铜线5米+(直径0.8mm),也可以用普通导线,但黄铜更酷一些 定位板,选了60%的,因为选择多,键还少~ (比如睫毛外设的铝合金卫星轴定位板) 卫星轴,因为不用PCB,只能选定位板类型的 轴61个,为了办公室炫耀(挨打),当然选清脆(扰人)的青轴啦 二极管61个,这是防多按键冲突(Anti-ghost),如果很少用3及以上键同时按...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作