@bean注解和@component注解的区别_通过源码查看 @Component,@Service 等注解是如何被解析的...
点击上方“匠心零度”,选择“设为星标”
做积极的人,而不是积极废人
来源:my.oschina.net/floor/blog/4325651
- 前言
- 1.@Component解析流程
- 找入口
- 找核心方法
- 概要分析
- 2.查文档找思路
- 3. 探寻@Component派生性流程
- 1. 确定metadataReader
- 2.查看match方法找重点方法
- 逐步分析
- 总结
前言
@Component和@Service都是工作中常用的注解,Spring如何解析?
1.@Component解析流程
找入口
Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。
该关系配置在相对于classpath下的/META-INF/spring.handlers中。
如上图所示 ContextNamespaceHandler对应context:... 分析的入口。
找核心方法
浏览ContextNamespaceHandler
![](/assets/blank.gif)
在parse中有一个很重要的注释
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
大意是:ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现 。
ClassPathBeanDefinitionScanner 的源码如下:
protected Set doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {//findCandidateComponents 读资源装换为BeanDefinition Set 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的核心方法。
概要分析
findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {//省略其他代码public Set findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); }}private Set scanCandidateComponents(String basePackage) { Set 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
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
大意如下:
@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@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 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 { @Overridepublic MetadataReader getMetadataReader(Resource resource) throws IOException { return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());} }
这里可以看出
MetadataReader metadataReader =new SimpleMetadataReader(...);
2.查看match方法找重点方法
![](/assets/blank.gif)
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 的构造方法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> allMetaTypes = this.metaAnnotationMap.values();for (Set metaTypes : allMetaTypes) {if (metaTypes.contains(metaAnnotationType)) {return true; } }return false;}}
逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。
这里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。
查找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);}}
查看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
这里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation
源码如下,注意这里传递了metaAnnotationMap!!
public class AnnotationMetadataReadingVisitor{@Overridepublic 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{@Overridepublic void visitEnd() { super.visitEnd();
Class extends Annotation> annotationClass = this.attributes.annotationType(); if (annotationClass != null) { List 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 visited = new LinkedHashSet<>();for (Annotation metaAnnotation : metaAnnotations) { recursivelyCollectMetaAnnotations(visited, metaAnnotation); }if (!visited.isEmpty()) { Set 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方法:
AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
就是判断当前注解的元注解在不在metaAnnotationMap中。
AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap
4、添加到返回结果的list
END
如果读完觉得有收获的话,欢迎点【好看】,关注【匠心零度】,查阅更多精彩历史!!!
让我“好看”
@bean注解和@component注解的区别_通过源码查看 @Component,@Service 等注解是如何被解析的...相关推荐
- datatable如何生成级联数据_通过源码分析Mybatis是如何返回数据库生成的自增主键值?...
在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象. 需要注意的是,KeyGenerator的作用,是返回数据库生成的自增 ...
- 通过源码理解反射与注解是什么东西?
反射+注解(Java):练习(Java) 1.通过反射获得注解 2.获得注解value的值 3.获得注解value的值 代码示例: package reflect;import java.lang.a ...
- java中j 和 j啥区别_从字节码层次分析++j和j++的区别
一.缘起 最近看到个面试题: int j = 0; for(int i = 0; i <100; i++) j = j++; System.out.println(j); 输出结果是0,如果换成 ...
- it工程师和码农的区别_工程师和码农的最大区别在哪
工程师和码农的区别并不在于技术水准的高低. 工程师注重解决问题. 工程师勇于承担责任. 工程师善于控制进度. 工程师能够自由沟通. 工程师知道如何合理安排时间. 工程师明白实现的功能有什么意义. 工程 ...
- it工程师和码农的区别_你是码农,还是IT工程师?
IT界知名的段子手曾说:对于那些月薪两万以下,自称IT工程师的码农们,其实我们从来没有把他们归为我们IT工程师的队伍.他们虽然总是以IT工程师自居,但只是他们一厢情愿罢了. 此话一出,不知激起了多少小 ...
- @Autowired注解通过源码讲原理
https://blog.csdn.net/o9109003234/article/details/101365065
- @Component与@Configuration区别
@Component与@Configuration区别: @Configuration本质上还是@Component. @Configuration标记的类必须符合下面的要求: 1.配置类不能是 fi ...
- 高并发编程-深入分析wait和sleep的区别并结合源码示例佐证
文章目录 概述 区别 sleep是Thread的方法,wait是Object的方法 sleep不会释放锁(Monitor), wait会让当前线程释放锁 sleep 不依赖 Monitor,但是wai ...
- java spring框架 注解_史上最全的java spring注解
史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...
最新文章
- 5折交叉验证_[Machine Learning] 模型评估——交叉验证/K折交叉验证
- lstm 文本纠错_工业界纠错系统
- session实现购物车
- Python装饰器详解,详细介绍它的应用场景
- sql server 面试_SQL Server审核面试问题
- Memcached主主复制+Keepalived高可用架构
- Linux 下 ascii 的查看方式
- 状态管理模式 — Vuex如何使用?
- jquery调用WebService和WebService输出JSON
- OpenSSL 常用函数——证书操作
- 家用计算机常见故障及解决方式,计算机常见故障及解决方法
- 以图搜图(基于 Milvus 和 VGG 实现以图搜图)
- It's only too late if you decide it is. Get busy living, or get busy dying(转)
- 支持10K高清视频是HDMI 2.1有什么特点?
- 华为S5700交换机链路聚合配置
- 明日书苑|硬笔书法临写指南
- 使用visjs制作网络拓扑图
- QQ看点模块(初稿截图)
- 海信网络科技软件研发实习面试
- R——R的一些边边角角