大家在spring开发的过程中,肯定使用了大量的注解,比如@ComponentScan、@Import、@Bean、@Configuration、@Conditional等大量注解来帮助我们快速开发,但是大家想过没有,他们底层是如何实现的。

比如之前大家注册一个bean需要在spring.xml配置文件里面配置一堆,现在只需要一个注解就可以完成了。

<bean id="cat"  class="com.amarsoft.code.spring.pojo.Cat"></bean>
@Component
public class Cat {int age;public Cat() {System.err.println("Cat初始化成功");}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

上面是如何做到的?它是依靠ConfigurationClassPostProcessor实现的。

ConfigurationClassPostProcessor是一个实现BeanDefinitionRegistryPostProcessor接口的类,在spring容器初始化的时候,调用BeanFactory的后置处理器实现注解扫描的,注册bean定义信息的。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {.............
}

方法如何跟入:

AbstractApplicationContext类:

refresh()  ----   invokeBeanFactoryPostProcessors(beanFactory)   ---  找到实现BeanDefinitionRegistryPostProcessor和PriorityOrdered接口的方法进行调用invokeBeanDefinitionRegistryPostProcessors方法到ConfigurationClassPostProcessor类进行处理。

核心处理逻辑代码:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass, filter);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotationsSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), filter, true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}

下面挑选几个注解说明一下,其他的注解也大同小异。

@ComponentScan

  • 它的出现主要是对标component-scan标签,标签只能使用在xml的配置文件中,而现在越来越流行使用注解的形式注册bean。

  • 首先检查传入的bean定义是否实现ComponentScan,如果实现了拿出ComponentScan信息。
  • 判断如果不为空 和 是否符合注入条件,这里shouldSkip方法里面应用了@Conditional注解判断条件。
  • 进行遍历ComponentScan注解信息,使用componentScanParser解析,找出符合条件的bean定义信息。
  • 对找到的bean定义信息进行递归调用,防止找到的bean定义信息上面还有注解信息。
// Process any @ComponentScan annotationsSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}

特别说明一下这个寻找bean定义信息过程方法:

这里扫描使用的ClassPathBeanDefinitionScanner这个扫描器,它的作用是指定过滤条件,在指定的classpath下寻找符合条件的bean定义信息,并且之后注册到registerBeanDefinition集合中。

mybatis框架的@MapperScan扫描mapper也是使用的这个扫描器来实现的。

参考:org.mybatis.spring.mapper.ClassPathMapperScanner

七、Spring-注解开发实现原理相关推荐

  1. 关于Spring注解开发教程,打包全送你

    摘要:spring是我们web开发中必不可少的一个框架,基于传统的xml方式配置bean总觉得太过繁琐,从spring2.5之后注解的出现可以大大简化我们的配置. 本文分享自华为云社区<如何高效 ...

  2. spring注解开发:容器中注册组件方式

    1.包扫描+组件标注注解 使用到的注解如下,主要针对自己写的类 @Controller @Service @Repository @Component @ComponentScan 参考 spring ...

  3. spring注解开发配置spring父子容器

    spring注解开发配置spring父子容器 官网 https://docs.spring.io/spring-framework/docs/current/spring-framework-refe ...

  4. 第二章 ---- spring注解开发

    文章目录 参考视频 注解驱动的意义 常用注解(重点) 启动注解驱动 IoC bean定义(@Component .@Controller.@Service. @Repository) @Scope b ...

  5. 二、Java框架之Spring注解开发

    文章目录 1. IOC/DI注解开发 1.1 Component注解 @Component @Controller @Service @Repository 1.2 纯注解开发模式 1.3 注解开发b ...

  6. Spring注解开发【源码分析】

    一.注册组件 @ComponentScan 扫描指定包下的组件. String[] value():扫描基础包范围 Filter[] includeFilters():扫描并获取符合过滤规则的组件 F ...

  7. 尚硅谷Spring注解开发学习笔记

    文章目录 前言 1.课程安排 1.1.容器 1.2.扩展原理 1.3.Web 2.配置文件开发 2.1.导入Spring-context依赖包 2.2.编写Spring配置文件 2.3.编写Perso ...

  8. Spring注解开发-Bean注册

    1.使用注解开发我们需要在applicationContext.xml文件中添加context标签. 在配置文件中开启注解扫描. <?xml version="1.0" en ...

  9. Spring—注解开发

    Spring原始注解 Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文 件可以简化配置,提高开发效率. @Component 使用在类上用于 ...

  10. Spring注解开发入门教程

    注解开发: 什么是驱动注解? 注解启动时使用注解的形式替代xml配置,将繁杂的spring配置文件从工程中彻底消除掉,简化书写 注解驱动的弊端 为了达成注解驱动的目的,可能会将原先很简单的书写,变的更 ...

最新文章

  1. 程序是一座城,八年来我深陷其中
  2. ppt 简单动画制作
  3. To handle Unhandled Exception
  4. c#语言中读取txt文件,简单的c#文本文件读写-.NET教程,C#语言
  5. Linux网络不可达解决方法
  6. 微信整人假红包图片_警惕:千万别点!这些红包是假的
  7. tableau货架图制作_3小时精通Tableau图表制作(18类)
  8. 运行php程序cpu 100%,php 应用 cpu 100% 调试方法
  9. ECS入门之Hello World
  10. php如何数字转字符串,php如何实现数字转字符串
  11. 发布Drools Workbench到Tomcat on Linux
  12. 2060 super和5700xt哪个值得买?
  13. 大理,徐娘半老的蝴蝶泉
  14. android ListView 九大重要属性详细分析
  15. 安装Lync Server 2013
  16. 怎么制作自己的数据集
  17. sublime 格式化Json
  18. 小米路由器显示DNS服务器设置错误,小米路由器dns地址怎么设置
  19. python编写一个简单的程序验证码_遇到验证码怎么办?Python编写一个验证码图片数据标注GUI程序!...
  20. 一、初学计算机——认识键盘布局及快捷键使用

热门文章

  1. SSRS从入门到放弃?? 零基础:教你5分钟内轻松制作一张报表(Intouch、IFix、WinCC、组态王等均适用)
  2. papaparse 使用_插件 jQuery.Papa Parse 中文 API 文档
  3. 传感器阵列波束优化设计与应用_传感器阵列波束优化设计及应用
  4. 成人高等教育计算机,成人高等教育计算机实验教学研究
  5. Xmind思维导图软件2023下载安装教程【附海量模板素材】
  6. 二、JAVA调用海康威视SDK实现摄像头预览完整版
  7. 微信小程序 自助停车,输入车牌号功能实现
  8. 黄山游记(三):登黄山
  9. Python游戏设计案例实战 夏敏捷 (第一本专著),国内这方面著作少有
  10. android 仿蘑菇街效果,Vue项目-仿蘑菇街移动端Web开发