我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring中Bean扫描原理是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring中Bean扫描原理是什么

本篇内容主要讲解“Spring中Bean扫描原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring中Bean扫描原理是什么”吧!

    环境建设

    由于创建包扫描的条件很简单,只要在Xml中配置一个属性即可。

    Spring中Bean扫描原理是什么

    正式开始

    在我前面的文章的阅读基础,我们直接这里节省时间,直接定位到ComponentScanBaeanDefinitionParser类中的parse方法。

    @Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);   // Actually scan for bean definitions and register them.   // 实际上,扫描bean定义并注册它们。   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);   return null;}

    这个代码的前半部分比较简单,就是可能当前传进来的basePackage可能是多个,所以这里使用方法去处理这个字符串。比较重要的代码在下半部分。

    也就是三个方法:

    • configureScanner:配置一个扫描器

    • doScan:使用扫描器去扫描

    • registerComponents:注册扫描到的BeanDefintion

    configureScanner

    第一段代码

    boolean useDefaultFilters = true;if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {   useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));}

    这一段声明了个变量,默认为True,在下方的If中去会去修改这个值。由于我们在applicatio.xml中没有设置这个属性,这里还是默认值。

    第二段代码

    // Delegate bean definition registration to scanner class.// 将 bean 定义注册委托给扫描程序类。ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

    createScanner方法中,就是new了一个ClassPathBeanDefinitionScanner对象给返回回来了。 随后又为该扫描器加入了两个属性。

    第三段代码

    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {   scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));}

    这里判断有无配置ResourcePattern属性,有的话设置。

    第四段代码

    try {   parseBeanNameGenerator(element, scanner);}catch (Exception ex) {    // ...}try {   parseScope(element, scanner);}catch (Exception ex) {    // ...}

    这两个方法代码跟进去有个共性。都是判断有没有配置一个属性,然后给sanner设置属性,具体看下方代码截图。

    Spring中Bean扫描原理是什么

    Spring中Bean扫描原理是什么

    这里这两个方法是干嘛的,我心里想了想,不知道,也不知道在什么地方会用到,所以这里接着往下看。

    parseTypeFilters

    protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {   // Parse exclude and include filter elements.   ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();   NodeList nodeList = element.getChildNodes();   for (int i = 0; i &lt; nodeList.getLength(); i++) {      Node node = nodeList.item(i);      if (node.getNodeType() == Node.ELEMENT_NODE) {         String localName = parserContext.getDelegate().getLocalName(node);         try {            if (INCLUDE_FILTER_ELEMENT.equals(localName)) {               TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);               scanner.addIncludeFilter(typeFilter);            }            else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {               TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);               scanner.addExcludeFilter(typeFilter);            }         }         catch (ClassNotFoundException ex) {            // ...         }         catch (Exception ex) {            // ...         }      }   }}

    首先看这个方法名parseTypeFilters,转换类型类型过滤器。

    Spring中Bean扫描原理是什么

    通过查看Spring的DTD文件,看到component-scan标签下还有两个子标签,想必就是对应上方的代码中解释了。

    随后该方法运行完后,就把创建好的scanner对象,给返回回去了。

    doScan

    protected Set&lt;BeanDefinitionHolder&gt; doScan(String... basePackages) {   Assert.notEmpty(basePackages, "At least one base package must be specified");   Set&lt;BeanDefinitionHolder&gt; beanDefinitions = new LinkedHashSet&lt;&gt;();   for (String basePackage : basePackages) {      Set&lt;BeanDefinition&gt; 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;}

    在第一行代码中,创建了个BeanDefinitions的Set,大概是用来存放结果的。

    随后根据basePacage,查找到了所有的候选BeanDefinition,至于获取的方法我在下方有讲到。

    随后遍历了刚刚获取到的BeanDefinition。

    findCandidateComponents

    private Set&lt;BeanDefinition&gt; scanCandidateComponents(String basePackage) {   Set&lt;BeanDefinition&gt; candidates = new LinkedHashSet&lt;&gt;();   try {      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +            resolveBasePackage(basePackage) + '/' + this.resourcePattern;      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);      // ...       for (Resource resource : resources) {         // ... 暂时不看      }   }   catch (IOException ex) {      // ... throw   }   return candidates;}

    上面一段代码,方法一进入,创建了一个set集合,这个set机会也就是方法最后的返回值,后续的代码中会向这个set去追加属性。

    随后到了packageSearchPath,这里是通过拼接字符串的方式最终得到这个变量,拼接规则如下:

    classpath*: + 转换后的xml路径 + ***.class

    随后根据resourceLoader,可以加载上方路径下的所有class文件。

    Spring中Bean扫描原理是什么

    随后进入For遍历环节。

    For遍历每一个资源

    当前的resource资源也就是读取到的class文件。

    for (Resource resource: resources) {    try {        MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);        if (isCandidateComponent(metadataReader)) {           ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);           sbd.setSource(resource);           if (isCandidateComponent(sbd)) {              // ... 打印日志              candidates.add(sbd);           }           else {              // ... 打印日志           }        }        else {           // ... 打印日志        }    }    catch (FileNotFoundException ex) {    // ... 打印日志    }    catch (Throwable ex) {    // ... throw    }}

    进入For后,首先获取metadataReader。这里代码简单追一下。

    Spring中Bean扫描原理是什么

    Spring中Bean扫描原理是什么

    主要做的是两件事,一个new了一个SimpleMetaDataReader。然后把这个MetaDataReader放入了缓存中。随后返回了这个Reader对象。

    然后就进入了第一个比较关键的方法代码,isCandidateComponent方法,仔细一看,这个方法怎么被调用了两次,因为这个if进入后还会调用isCandidateComponent方法,然后我看了看入参,不一致,一个入参事Reader,一个入参事BeanDefinition。我们第一个if中点用的Reader的isCandidateComponent方法。

    isCandidateComponent(MetadataReader metadataReader)

    protected booleanisCandidateComponent(MetadataReader metadataReader) throws IOException {   for (TypeFilter tf : this.excludeFilters) {      if (tf.match(metadataReader, getMetadataReaderFactory())) {         return false;      }   }   for (TypeFilter tf : this.includeFilters) {      if (tf.match(metadataReader, getMetadataReaderFactory())) {         return isConditionMatch(metadataReader);      }   }   return false;}

    上方的excludeFilters排除的我们不用看,主要是看下方的include

    Spring中Bean扫描原理是什么

    后续的代码我就不读了,大概实现我猜测是通过Reader去读到类上的注解,看看有没有当前filter中设置的注解。有的话返回true。

    继续后面的逻辑

    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if (isCandidateComponent(sbd)) {   if (debugEnabled) {      logger.debug("Identified candidate component class: " + resource);   }   candidates.add(sbd);}

    刚刚外层的If为True后,这里会创建一个ScannedGenericBeanDefinition,既然是BeanDefinition,那就可以被Spring加载。

    后面把创建的BeanDefinition放入了isCandidateComponent方法。

    isCandidateComponent(AnnotatedBeanDefintion)

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {   AnnotationMetadata metadata = beanDefinition.getMetadata();   return (metadata.isIndependent() &amp;&amp; (metadata.isConcrete() ||         (metadata.isAbstract() &amp;&amp; metadata.hasAnnotatedMethods(Lookup.class.getName()))));}@Overridepublic boolean isIndependent() {   // enclosingClassName 为 null   return (this.enclosingClassName == null || this.independentInnerClass);}default boolean isConcrete() {   return !(isInterface() || isAbstract());}

    到这个方法基本第一个判断就返回了。isIndependent方法中一开始看到其中的两个单词我有点懵,enclosingClass和innerClass,可能是我英文不好的缘故或者基础差吧,百度搜了才知道的。我这里就不讲了,有兴趣你们可以自己搜索一下。自己搜索的记忆更深刻。只要是普通的Component的时候,这里为True。

    至于下民的isConcrete方法,就是判断一下当前类是不是接口,或者抽象类。很明显如果是正常的Component,这里是false,随后取反为True。

    继续后面的逻辑

    if (isCandidateComponent(sbd)) {   if (debugEnabled) {      logger.debug("Identified candidate component class: " + resource);   }   candidates.add(sbd);}

    当把BeanDefinition传入后返回为True,进入If,也就是添加当前的BeanDefinition进入结果集,返回结果集。

    doScan 继续后面的逻辑

    Set&lt;BeanDefinition&gt; 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);   }}

    把刚刚获取到BeanDefinition拿出来遍历.

    第一步获取MetaData,这个在刚刚的代码中有写到。随后把他的ScopeName赋值给了MetaData。

    接下来有两个if是对这个BeanDefinition设置一些参数的。可以简单扫一眼。捕捉一些关键信息即可。

    Spring中Bean扫描原理是什么

    这个里面设置一个属性,这里记录一下,后面有用到再看。

    Spring中Bean扫描原理是什么

    这个里面是针对类里添加的一些别的注解,来给BeanDefinition添加一些配置。看到几个比较眼熟的,Lazy,Primary,Description这些注解比较眼熟。

    doScan 最后一个IF

    if (checkCandidate(beanName, candidate)) {   BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);   definitionHolder =         AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);   beanDefinitions.add(definitionHolder);   registerBeanDefinition(definitionHolder, this.registry);}

    粗略的扫一眼,这里可以看几个重要的地方,一个是进入If的条件,注册BeanDefinition。
    至于applyScopedProxyMode方法,因为我没的类上没有加Scope注解,所以这里都是不会配置代理。也就是直接返回当前传入的BeanDefinition。

    Spring中Bean扫描原理是什么

    checkCandidate方法

    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {   if (!this.registry.containsBeanDefinition(beanName)) {      return true;   }   BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);   BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();   if (originatingDef != null) {      existingDef = originatingDef;   }   if (isCompatible(beanDefinition, existingDef)) {      return false;   }   // ... throw Exception.}

    因为是通过Bean扫描进入的,也就是BeanDefinitionRegister当中是没有这个BeanDefinition的。所以这里直接就返回True,不会有走到下面的机会。
    这个时候大家可以思考一下,如果走到下面了会怎么样。欢迎评论区讨论。

    继续代码逻辑

    beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);

    接下来就去registerBeanDefinition了,然后还把registry传进入了方法,那很明显了。这里是去注册BeanDefinition了。

    Spring中Bean扫描原理是什么

    到此,相信大家对“Spring中Bean扫描原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Spring中Bean扫描原理是什么

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Spring中Bean扫描原理是什么

    本篇内容主要讲解“Spring中Bean扫描原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring中Bean扫描原理是什么”吧!环境建设由于创建包扫描的条件很简单,只要在Xml中配
    2023-07-02

    spring配置不扫描service层的原因是什么

    这篇文章将为大家详细讲解有关spring配置不扫描service层的原因是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。spring配置不扫描service层原因我将contoller给springm
    2023-06-29

    Spring Bean中Bean的注册是什么

    这篇文章主要介绍“Spring Bean中Bean的注册是什么”,在日常操作中,相信很多人在Spring Bean中Bean的注册是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring Bean中B
    2023-06-29

    Spring中的bean概念是什么

    这篇文章将为大家详细讲解有关Spring中的bean概念是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Bean是Spring框架中最核心的两个概念之一(另一个是面向切面编程AOP)。1 定义Spri
    2023-06-29

    Spring中Bean的作用域是什么

    这篇文章给大家介绍Spring中Bean的作用域是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、Bean的作用域首先我们来讲一下有关于bean的作用域,一般情况下,我们书写在IOC容器中的配置信息,会在我们的I
    2023-06-20

    Spring中的Bean作用域是什么

    本文小编为大家详细介绍“Spring中的Bean作用域是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring中的Bean作用域是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。概述scope用来声明
    2023-06-30

    Spring MVC原理是什么

    这篇文章将为大家详细讲解有关Spring MVC原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。springMVC,是spring的一个子框架,当然拥有spring的特性,如依赖注入。在web模型
    2023-06-27

    Spring bean需要依赖注入的原因是什么

    这篇文章主要介绍“Spring bean需要依赖注入的原因是什么”,在日常操作中,相信很多人在Spring bean需要依赖注入的原因是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring bean
    2023-06-20

    spring中bean的生命周期是什么

    在Spring中,Bean的生命周期包括以下几个阶段:1. 实例化:当Spring容器接收到请求时,根据配置文件或注解等方式,在内存中创建Bean的实例。2. 属性赋值:Spring容器通过依赖注入的方式,将Bean的属性值注入到相应的属性
    2023-09-27

    spring中bean的初始化方法是什么

    在Spring中,bean的初始化可以通过两种方式来完成:使用@Bean注解的initMethod属性或者实现InitializingBean接口。1. 使用@Bean注解的initMethod属性:可以在@Bean注解中通过initMet
    2023-09-21

    spring session的原理是什么

    Spring Session是一种用于管理用户会话的框架,它通过将会话数据存储在外部存储介质中,而不是默认的内存中,来实现会话的持久化和分布式管理。Spring Session的原理如下:1. 在用户请求到达服务器时,Spring Sess
    2023-09-21

    Spring MVC的原理是什么

    今天小编给大家分享一下Spring MVC的原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。SpringMVC是一种
    2023-06-27

    spring scope的原理是什么

    Spring的Bean的作用域(scope)指定了一个Bean的实例是如何被创建和管理的。Spring框架提供了多种作用域,包括singleton(单例)、prototype(原型)、request、session等。单例作用域(singl
    2023-08-31

    Spring底层原理是什么

    这篇文章主要讲解了“Spring底层原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring底层原理是什么”吧!Spring简介ClassPathXmlApplicationCo
    2023-07-05

    tomcat+spring mvc原理是什么

    这篇文章主要讲解了“tomcat+spring mvc原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“tomcat+spring mvc原理是什么”吧!tomat + spring
    2023-06-02

    spring中aop的执行原理是什么

    在Spring中,AOP(面向切面编程)的执行原理主要涉及以下几个方面:1. 切面的定义:通过注解或配置文件等方式,定义切面(Aspect)类,其中包含了需要在目标对象的特定方法执行前、执行后或执行异常时执行的逻辑。2. 切入点的定义:切入
    2023-08-09

    Spring中bean集合注入的方法是什么

    这篇文章主要讲解了“Spring中bean集合注入的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring中bean集合注入的方法是什么”吧!Spring作为项目中不可缺少的底
    2023-07-02

    Python描述符的工作原理是什么

    小编给大家分享一下Python描述符的工作原理是什么,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、前言其实,在开发过程中,虽然我们没有直接使用到描述符,但是它在底层却无时不刻地被使用到,例如以下这些:function、
    2023-06-15

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录