Spring Core Container 源码分析七:注册 Bean Definitions

Spring(八):注册Bean(2) 《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取! 回顾spring解析Bean主要分为三步创建普通的BeanDefinition(解析默认的属性、XML标签)修饰创建出来的BeanDefinition(解析自定义的属性和标签)注册BeanDefinition下面就来看一下如何进行注册的!对应代码就是下面这一行,注册BeanDefinition可以看到,解析和修饰BeanDefinition都是交由delegate去做的,也就是BeanDefinitionP 阅读详情

前言

原本以为,Spring 通过解析 bean 的配置,生成并注册 bean defintions 的过程不太复杂,比较简单,不用单独开辟一篇博文来讲述;但是当在分析前面两个章节有关 @Autowired、@Component、@Service 注解的注入机制的时候,发现,如果没有对有关 bean defintions 的解析和注册机制彻底弄明白,则很难弄清楚 annotation 在 Spring 容器中的底层运行机制;所以,本篇博文作者将试图去弄清楚 Spring 容器内部是如何去解析 bean 配置并生成和注册 bean definitions 的相关主流程;

备注,本文是作者的原创作品,转载请注明出处。

bean definition 是什么?

➥ bean definitions 是什么?

其实很简单,就是 Java 中的 POJO,用来描述 bean 配置中的 element 元素的,比如,我们有如下的一个简单的配置

beans.xml


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

    
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:p= "http://www.springframework.org/schema/p"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.shangyang" />
<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>

可以看到,上面有三个 element

  1. <beans/>, root element
  2. <context:component/>, component-scan element
  3. <bean/>, bean element

在配置文件 beans.xml 被 Spring 解析的过程中,每一个 element 将会被解析为一个 bean definition 对象缓存在 Spring 容器中;

➥ 需要被描述为 bean definitions 的配置对象主要分为如下几大类,

  1. xml-based,解析 xml beans 的情况;
  2. 使用 @Autowired、@Required 注解注入 beans 的解析情况;
    需要特殊处理并解析的元素 <context:annotation-config/>
  3. 使用 @Component、@Service、@Repository,@Beans 注解注入 beans 的解析情况;
    需要特殊处理并解析的元素 <context:annotation-scan/>

➥ 最开始我的确是这么认识 bean definitions 的,但是当我分析完有关 bean definitions 的相关逻辑和源码以后,对其认识有了升华,参考写在最后;

源码分析

最好的分析源码的方式,就是通过高屋建瓴,逐个击破的方式;首先通过流程图获得它的蓝图(顶层设计图),然后再根据蓝图上的点逐个击破;最后才能达到融会贯通,胸有成竹的境界;所以,这里作者用这样的方式带你深入剖析 Spring 容器里面的核心点,以及相关主流程到底是如何运作的。

测试用例

为了一次性把上述源码分析所描述有的情况阐述清楚,我们继续使用 Spring Core Container 源码分析六:@Service 中使用的测试用例;唯一做的修改是,再使用一个特殊的 element xmlns:p 来配置 john,这样可以进一步去调试自定义 Spring 配置标签是如何实现的;

beans.xml


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

    
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:p= "http://www.springframework.org/schema/p"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.shangyang" />
<bean name="john"
class= "org.shangyang.spring.container.Person"
p:name= "John Doe"
p:spouse-ref= "jane"/>
<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>
<bean name="niba" class="org.shangyang.spring.container.Dog">
<property name="name" value="Niba" />
</bean>
</beans>

流程分析

整个流程是从解析 bean definitions 流程开始的,对应的入口是主流程的 step 1.1.1.2 obtainFreshBeanFactory;

入口流程

entrance-of-register-bean-definitions.png entrance-of-register-bean-definitions.png
  1. 首选初始化得到 BeanFactory 实例 DefaultListableBeanFactory,用来注册解析配置后生成的 bean definitions;

  2. 然后通过 XmlBeanDefinitionReader 解析 Spring XML 配置文件
    根据用户指定的 XML 文件路径 location,进行解析并且得到 Resource[] 对象,具体参考 step 1.1.3.3.1.1 getResource(location) 步骤;这里,对其如何通过 location 得到 Resource[] 对象做进一步分析,看源码,

    PathMatchingResourcePatternResolver.java

    
          
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
          
    public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
    // a class path resource (multiple resources for same name possible)
    if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
    // a class path resource pattern
    return findPathMatchingResources(locationPattern);
    }
    else {
    // all class path resources with the given name
    return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
    }
    }
    else {
    // Only look for a pattern after a prefix here
    // (to not get fooled by a pattern symbol in a strange prefix).
    int prefixEnd = locationPattern.indexOf( ":") + 1;
    if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
    // a file pattern
    return findPathMatchingResources(locationPattern);
    }
    else {
    // a single resource with the given name
    return new Resource[] {getResourceLoader().getResource(locationPattern)};
    }
    }
    }

    这里的解析过程主要分为两种情况进行解析,一种是前缀是 classpath: 的情况,一种是普通的情况,正如我们当前所使用的测试用例的情况,既是 new ClassPathXmlApplicationContext(“beans.xml”) 的情况,这里不打算在这里继续深挖;

  3. 以测试用例 beans.xml 为例,从 #2 解析得到的 Resource 实例 resource 对应的是 beans.xml 的配置信息,从 step 1.1.3.3.1.2 loadBeanDefinitions 开始将会对 resource 既是 beans.xml 中的元素依次进行解析;首先生成对应 beans.xml 的 org.w3c.Document 对象实例 document,见 step 1.1.3.3.1.2.2.1,其次得到解析 document 对象的 BeanDefinitionDocumentReader 实例 documentReader,将当前的 Resource 对象封装为 XmlReaderContext 实例 xmlReaderContext,最后通过 documentReader 开始正式解析 document 对象得到 bean definitions 并将其注册到当前的 beanFactory 实例中,该步骤见 step 1.1.3.3.1.2.2.2.3

当完成上述三个步骤以后,将进入 register bean definitions process 流程

register bean definitions process

➥ 首先,重要的两件事情是,

  • 从 document 对象中获得了 Root 实例 root,见 step 1.2
    看一个 root 元素,长什么样的

    
          
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
          
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p= "http://www.springframework.org/schema/p"
    xmlns:context= "http://www.springframework.org/schema/context"
    xsi:schemaLocation= "http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    </beans>

    就是一个 xml 配置文件中的最顶层元素 <beans/>

  • 然后初始化得到 documentReader 实例的解析对象既 this.delegate<BeanDefinitionParserDelegate>,后面针对 element 元素的解析将会使用到它;

➥ 后续,当前面的工作准备好了以后,来看看是如何解析 element 的?

首先,判断 root 元素的 namespace 对应的是不是 default namespace,若不是,将进入 step 1.3.3.3: parse custom element;这里我们关注常规流程,既是当 root 元素的 namespace 是 default namespace 的流程;

遍历 root 元素下的所有 element,

  1. 若 element 的 namespace 是 default namespace,将进入 parse default element 流程;
    比如当前 element 是普通的 <bean/>
  2. 若 element 的 namespace 不是 default namespace,将进入 parse custom element 流程;
    比如当前 element 是 <context:annotation-config/> 或者是 <context:component-scan/>
parse default element process

可以看到,该流程中包含四个子流程,依次处理不同的 element 元素的情况,其它三种都是比较特殊的情况,我们这里,主要关注“解析 <bean/>” 元素的流程”

解析 bean element 流程

这里,为了能够尽量的展示出解析 <bean/> 元素的流程中的逻辑,我将使用一个比较特殊的 <bean/> 来梳理此部分的流程;


    
1
2
3
4

    
<bean name="john"
class= "com.example.Person"
p:name= "John Doe"
p:spouse-ref= "jane"/>

该 <bean/> 元素使用了 namespace xmlns:p=”http://www.springframework.org/schema/p“

process-bean-definition.png process-bean-definition.png

➥ 首先,通过 BeanDefintionParserDelegate 对象解析该 element,得到一个 BeanDefinitionHolder 对象 bdHolder 实例;该解析过程中会依次去解析 bean id, bean name, 以及相关的 scope, init, autowired model 等等属性;见 step 1.1

➥ 其次,对 bean definition 进行相关的修饰操作,见 step 1.2

常规步骤

  1. 遍历当前 element 中的所有 attributes,依次得到 atttribute node
  2. 取得 node 所对应的 namespace URI,并判断该 namespace 是否是 custom namespace,如果是 custom namespace,那么正式进入对该 attribute node 的修饰过程,如下所述;

attribute node 的修饰过程

假设,我们当前的 attribute node 为 p:spouse-ref=”jane”,看看该属性是如何被解析的,

  1. 首先,通过 node namespace 得到对应的 NamespaceHandler 实例 handler
    通过 xmlns:p=”http://www.springframework.org/schema/p“ 得到的 NamespaceHandler 为 SimplePropertyNamespaceHandler 对象;

  2. 其次,调用 SimplePropertyNamespaceHandler 对象对当前的元素进行解析;
    可以看到,前面的解析并没有什么特殊的,从元素 p:spouse-ref=”jane” 中解析得到 propery name: spouse-ref,property value: jane;但是后续解析,比较特殊,需要处理 REF_SUFFIX 的情况了,也就是当 property name 的后缀为 -ref 的情况,表示该 attribute 是一个 ref-bean 属性,其属性值引用的是其它的 bean 实例,所以呢,这里将其 property value 封装为了一个 RuntimeBeanReference 对象实例,表示将来在解析该 property value 为 Java Object 的时候,需要去初始化其引用的 bean 实例 jane,然后注入到当前的 property value 中;

  3. 最后,将解析后得到的 bean definition 封装在 bean definition holder 对象中进行返回;

➥ 最后,注册 bean definition;

见 step 1.3.2 register.registerBeanDefinition(beanName, beanDefinition),register 就是当前的 bean factory 实例,通过将 bean name 和 bean definition 以键值对的方式在当前的 bean factory 中进行注册;这样,我们就可以通过 bean 的名字,得到其对应的 bean definition 对象了;

➥ 写在该小节最后,

我们也可以自定义某个 element 或者 element attribute,并且定义与之相关的 namespace 和 namespace handler,这样,就可以使得 Spring 容器解析自定义的元素;类似于 dubbo 配置中所使用的 <dubbo /> 自定义元素那样;

parse custom element process

此步骤对应 register bean definitions process 步骤中的 step 1.3.3.2

parse-custom-element-process.png parse-custom-element-process.png

该小节我将试图使用一个常用的 custom element: <context:component-scan/> 来梳理整个流程;

  1. 首先得到与 <context:component-scan /> 元素相关的 namespace uri: http://www.springframework.org/schema/context,见 step 1.1
  2. 通过 #1 得到的 namespace uri 解析得到相应的 NamespaceHandler,这里得到的是 ContextNamespaceHandler;见 step 1.2
    从 step 1.2.1 getHandlerMappings() 返回了所有内置的 namespace uri 与 namespace handler 所一一对应的键值对;
  3. 使用 #2 返回的 NamespaceHandler 既 ContextNamespaceHandler 进行 parse 操作,见 step 1.3,参考子流程 parse element by ContextNamespaceHandler,注意,之所以这里单独使用一个子流程来介绍,是因为使用 ContextNamespaceHandler 来解析只是其中的一种解析情况,将来考虑分析更多的子流程情况;
parse element by ContextNamespaceHandler

继续 parse custom element process 章节中所使用到的例子,<context:component-scan/> 来分析该流程,

parse-element-via-ContextNameSpaceHandler.png parse-element-via-ContextNameSpaceHandler.png

➥ 在开始分析之前,看看 component-scan 元素长什么样,

注意,component-scan element 本身包含 annotation-config attribute;

➥ 流程分析

首先,根据 element name: component-scan 找到对应的 BeanDefinitionParser,在 ContextNamespaceHandler 初始化的时候,便初始化设置好 8 对内置的 element name 与 parsers 的键值对;这里,根据名字 component-scan 找到对应的 parser ComponentScanBeanDefinitionParser 对象;

其次,使用 ComponentScanBeanDefinitionParser 对象开始解析工作,

  1. 首先,解析 <context:component-scan base-package=”org.shangyang”/> 得到 basePcakges String[] 对象;

  2. 其次,初始化得到 ClassPathBeanDefinitiionScanner 对象实例 scanner,然后调用 scanner.doScan 方法进入 do scan 流程,该流程中将会遍历 base package 中所包含的所有 .class 文件,解析之,并生成相应的 bean definitions;另外在这个流程中,还要注意的是,最后会将 bean definitions 在当前的 bean factory 对象中进行注册;

  3. 最后,这一步是从 step 1.2.4 开始,主要处理的逻辑为,当 element 含有 annotation-config 属性的时候,将会注册一系列的 post-processors-bean-definitions;

do scan 流程

这里主要介绍上一个小节中 #2 步骤中所提到的 do scan 流程步骤,对应 parse element by ContextNamespaceHandler 流程图中的 step 1.2.3 scanner.doScan;

➥ 先来看看 step 1.2.3.1 findCandidateComponent(basePackage)

ClassPathScanningCandidateComponentProvider.java (已删除大量不相干代码)


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

    
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//1. 从当前用户自定的 classpath 子路径中,通过 regex 查询到所有的所匹配的 resources;要特别注意的是,
// 这里为什么不直接通过 Class Loader 去获取 classes 来进行判断? 因为这样的话就相当于是加载了 Class Type,而 Class Type 的加载过程是通过 Spring 容器严格控制的,是不允许随随便便加载的
// 所以,取而代之,使用一个 File Resource 去读取相关的字节码,从字节码中去解析........
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
//2. 依次遍历用户定义的 bean Class 对象
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace( "Scanning " + resource);
}
if (resource.isReadable()) {
try {
// 将从字节码中获取到的相关 annotation(@Service) 以及 FileSystemResource 对象保存在 metadataReader 当中;
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
...
}
...
}
...
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException( "I/O failure during classpath scanning", ex);
}
return candidates;
}

  1. 代码第 10 行

    
          
    1
    
          
    Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);

    这一步通过递归搜索 base package 目录下的所有 .class 文件,并将其字节码封装成 Resource[] 对象;上面的注释解释得非常清楚了,这里封装的是 .class 文件的字节码,而非 class type;除了注解中所描述的,这里再引申说明下,这里为什么不直接加载其 Class Type 还有一个原因就是当 Spring 在加载 Class Type 的时候,很有可能在该 Class Type 上配置了 AOP,通过 ASM 字节码技术去修改原有的字节码以后,再加入 Class Loader 中;所以,之类不能直接去解析 Class Type,而只能通过字节码的方式去解析;

    这一步同样告诫我们,在使用 Spring 容器来开发应用的时候,开发者不要随随便便的自行加载 Class Type 到容器中,因为有可能在加载 Class Type 之前需要通过 Spring 容器的 ASM AOP 进行字节码的修改以后再加载;

  2. 代码第 23 行

    
          
    1
    
          
    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);

    解析当前的 .class 字节码,解析出对应的 annotation,比如 @Service,并将其协同 FileSystemResource 对象一同保存到 metadataReader 对象中;

  3. 代码第 24 行

    
          
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
          
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
    if (tf.match(metadataReader, this.metadataReaderFactory)) {
    return false;
    }
    }
    for (TypeFilter tf : this.includeFilters) { // includedFilters 包含三类 annotation,1. @Component 2. @ManagedBean 3. @Named
    if (tf.match(metadataReader, this.metadataReaderFactory)) {
    return isConditionMatch(metadataReader);
    }
    }
    return false;
    }

    既是从当前的 metadataReader 中去判断是否存在 1. @Component 2. @ManagedBean 3. @Named 三种注解中的一种,如果是,则进入下面的流程

  4. 代码 25 - 29 行,将符合 #3 标准的 annotation 封装为 ScannedGenericBeanDefinition annotation-bean-definition,并加入 candidates 返回

    
          
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
          
    if (isCandidateComponent(metadataReader)) {
    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    sbd.setResource(resource);
    sbd.setSource(resource);
    if (isCandidateComponent(sbd)) {
    candidates.add(sbd);
    }
    ...
    }

➥ 依次处理并注册返回的 candidates

该步骤从流程图 parse element by ContextNamespaceHandler 中的 step 1.2.3.2 开始,主要做了如下几件事情,

  1. 设置 candiate (既 annotation bean definition) 的 scope
  2. 通过 AnnotationBeanNameGenerator 生成 bean name,因为通过 @Component、@Service 注解的方式注入的 bean 往往没有配置 bean name,所以往往需要通过程序的方式自行生成相应的 bean name,看看内部的源码,如何生成 bean name 的,

    
          
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
          
    /**
    * 因为通过 @Component、 @Serivce 等注解的方式不会像 xml-based 配置那样提供了一个 name 的标签,可以指定 bean name;所以,这里需要去单独为其生成一个;
    */
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    if (definition instanceof AnnotatedBeanDefinition) {
    String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); // 处理诸如 @Service("dogService") 的情况
    if (StringUtils.hasText(beanName)) {
    // Explicit bean name found.
    return beanName;
    }
    }
    // Fallback: generate a unique default bean name. 里面的实现逻辑就是通过将 Class Name 的首字母大写编程小写,然后返回;
    return buildDefaultBeanName(definition, registry);
    }

    通常情况下,是将类名的首字母进行小写并返回;对应 step 1.2.2.3.3

  3. 设置 annotation bean definition 的默认值,参考 step 1.2.4
  4. 设置 scoped proxy 到当前的 annotation bean definition
  5. 最后,将 annotation bean definition 注册到当前的 bean factory
注册 post-processor-bean-definitions

该步骤从流程图 parse element by ContextNamespaceHandler 的 step 1.2.4.2 registerAnnotationConfigProcessors 开始,将会依次注册由如下 post-processor class 对象所对应的 post-processor-bean-definitions,

  • ConfigurationClassPostProcessor.class
  • AutowiredAnnotationBeanPostProcessor.class
  • RequiredAnnotationBeanPostProcessor.class
  • CommonAnnotationBeanPostProcessor.class
  • 通过 PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 发射得到的 class
  • EventListenerMethodProcessor.class
  • DefaultEventListenerFactory.class

注意,这里都是通过 Class 对象注册的,并非注册的实例化对象,下面,我们来简单分析一下注册相关的源码,以注册 AutowiredAnnotationBeanPostProcessor post-processor-bean-definition 为例子,

AnnotationConfigUtils#registerAnnotationConfigProcessors


    
1
2
3
4
5
6

    
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// 将 AutowiredAnnotationBeanPostProcessor.class 封装为 bean definition
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

上面的步骤将 AutowiredAnnotationBeanPostProcessor.class 封装为 bean definition;

AnnotationConfigUtils.registerPostProcessor


    
1
2
3
4
5
6
7

    
private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition); // 注册 bean definition
return new BeanDefinitionHolder(definition, beanName);
}

这一步将 AutowiredAnnotationBeanPostProcessor 所对应的 bean definition 注入了当前的 bean factory 当中;

AutowiredAnnotationBeanPostProcessor 提供了 @Autowired 注解注入机制的实现,详情参考 AutowiredAnnotationBeanPostProcessor 章节;

写在最后

通过上述的分析,可以清晰的看到,bean definition 的作用是什么,就是通过 bean definition 中的描述去限定通过 Class Type 实例化得到 instance 的业务规则,我们看看由 do scan 流程 所生成的 annotation-bean-definition 对象,

debug-scanned-generic-bean-definition.png debug-scanned-generic-bean-definition.png

可以看到,当我们在后续要根据该 annotation-bean-definition 得到一个 DogService 实例的时候,所要遵循的业务规则,如下所示,


    
1
2
3
4
5
6
7
8
9
10
11
12
13

    
Generic bean: class [org.shangyang.spring.container.DogService];
scope=;
abstract= false;
lazyInit= false;
autowireMode=0;
dependencyCheck=0;
autowireCandidate= true;
primary= false;
factoryBeanName=null;
factoryMethodName=null;
initMethodName=null;
destroyMethodName=null;
defined in file [/Users/mac/workspace/spring/framework/sourcecode-analysis/spring-core-container/spring-sourcecode-test/target/classes/org/shangyang/spring/container/DogService.class]

不过,要注意,这里所得到的 ScannedGenericBeanDefinition 实例,同样没有真正去加载 org.shangyang.spring.container.DogService Class Type 到容器中,而只是将 class name 字符串赋值给了 ScannedGenericBeanDefinition.beanClass,言外之意,将来在加载 Class Type 到容器中的时候,或许与实例化 instance 一样也要根据 bean definitions 中的规则来限定其加载行为,目前我所能够想到的与其相关的就是 ASM 字节码技术,可以在 bean definition 中定义 ASM 字节码修改规则,来控制相关 Class Type 的加载行为;

References


本文转载自笔者的私人博客,伤神的博客: http://www.shangyang.me/2017/04/07/spring-core-container-sourcecode-analysis-register-bean-definitions/
华为CNA VRM搭建(使用vmware worfstartion搭建) 华为CNA和VRM在 vmware workstation安装使用 阅读详情

相关推荐

从网格剖分到结果外推:一文拆解HFSS快速扫频(Fast Sweep)的底层逻辑与性能调优

本文深入解析了HFSS快速扫频(Fast Sweep)技术的底层逻辑与性能调优方法。通过ALPS算法实现频带外推,快速扫频相比离散扫频和插值扫频能提升3-5倍计算效率,特别适用于天线阵列、微波滤波器等宽频带分析场景。文章详细介绍了算法架构、参数优化策略及工程实践中的精度保障机制,帮助工程师高效完成电磁仿真。

weixin_28693447的博客 92

Spring源码讲解之 registerBeanDefinitions 方法

registerBeanDefinitions 方法是在接口 org.springframework.beans.factory.xml.BeanDefinitionDocumentReader 中定义的,主要作用是:从指定的 DOM 文档中读取 bean 定义并将其注册到 bean 定义表中,该方法第一个参数是 org.w3c.dom.Document 类型(包含 bean 定义信息的 document),第二个参数是 org.springframework.beans.factory.xml.XmlRe

misterhou的专栏 4603

银河麒麟系统安装redis【亲测可行】

架构:Linux 5.10.0-9-generic x86_64 GNU/Linux。redis版本:redis-6.2.3.tar.gz。然后在执行make install,结束;OS:银河麒麟V10(SP1)未激活。cpu:I5-10代;

lx_nhs的博客 3333

spring5源码篇(2)——BeanDefinition的注册

spring5源码篇之BeanDefinition的注册

笔记罢了 1740

MIKE21桥墩模拟实战:抠网格与构筑物法对比与选型指南

本文详细对比了MIKE21软件中抠网格法与构筑物法在桥墩模拟中的应用,包括操作步骤、适用场景及优缺点分析。通过实际案例数据展示两种方法的精度与效率差异,并提供选型决策树和常见问题解决方案,帮助工程师根据项目需求选择最优模拟方法。

weixin_42656416的博客 322

Tesseract in Java - Intellij IDEA对Tess4J的配置和使用

最近要弄OCR,搜到这个,官网全英文,很烦,看看有没有中文的博客介绍,擦,一个比一个奇葩,还是自己按照官方的来吧,外网慢的很,传到csdn给大家下载。中文训练库下载地址: http://download.youkuaiyun.com/detail/d_dmelon/9916532Tess4J3.4下载地址: http://download.youkuaiyun.com/detail/d_dmelon/9916519安装

Lewis的博客 1万+

Spring源码学习(八)---注册BeanDefinition

这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析 文章目录 1. 查看源码相关的快捷键 快捷键 作用 Ctrl + Shift+i 出现类似于预览的小窗口 Ctrl + Enter (接上步)完全打开源码 Ctrl + 鼠标左键 一步到位打开源码 = Ctrl + Shift+i –>Ctrl + Enter Alt+7 查看类的有什么方法 ctrl+f12 查看继承方法 ctrl+h 查看接口的实.

1171

Python 爬虫实战:医学临床指南爬取(含 PDF 解析与结构化存储)

在当今数字化时代,医学临床指南作为医生进行科学诊疗的重要参考资料,包含大量经过临床验证的诊疗路径和建议。这些指南通常发布在权威医学网站上,数量庞大且更新频繁。如何自动化地抓取并存储这些指南,成为医疗数据分析、医学研究以及医疗 AI 模型训练等工作的关键第一步。本文将系统讲解如何利用 Python 爬虫技术从权威医学网站爬取临床指南内容,并实现 PDF 解析与结构化存储。

u014481728的博客 449

Spring框架探秘:深入理解registerBeanDefinitions方法的奥秘

方法,我们揭开了Spring容器初始化过程的神秘面纱。理解这一过程对于掌握Spring框架的高级应用至关重要。希望本文能够帮助你更深入地理解Spring的内部机制。它负责将Bean的定义注册到Spring容器中,是整个依赖注入(DI)和面向切面编程(AOP)机制的基石。方法的调用顺序对Spring容器中Bean的创建和依赖关系解析至关重要。的调用上下文顺序及其功能,通过源码分析来揭示其运行原理,并探讨其在实际开发中的应用场景。在Spring容器启动时,会经历一系列的初始化过程,其中。

赵KK的博客 883

Android 11.0 app添加校验锁(输入密码才能进入app)

11.0定制化开发中客户要求给app添加锁,就是点击app 图标时,会弹出Dialog,需要输入密码才能进入app中,就是应用校验锁, 最开始想到在Launcher3中实现,但是如果更换了默认Launcher 又不行了 所以最终还是得拦截app启动Activity 从这里入手了 Launcher与APP是在两个不同的进程中,他们之间的通信是通过Binder完成的,点击Launcher上的某个APP,这时会调用Launcher的startActivitySafely方法。 public boolean sta

安卓兼职framework和app工程师的博客 1952

智能合约监控告警:提示工程架构师用AI Prompt实现精准预警

当AI大模型(尤其是代码理解模型如CodeLlama、GPT-4 Code)与区块链数据结合,智能合约监控迎来范式革新。AI模型如何精准理解智能合约的业务特殊性?如何避免"AI幻觉"导致的误报?如何让模型学会区分"正常异常"与"攻击前兆"?答案藏在"提示工程"(Prompt Engineering)中。理解特定智能合约的业务逻辑(如DeFi协议的清算机制、NFT的铸造规则)识别复杂的攻击模式(如重入攻击的调用链特征、闪电贷的套利路径)结合链上链下数据进行多维度推理(如关联价格波动与异常交易)

AI架构全栈开发实战笔记 1011

Spring解析及注册BeanDefinition

上一篇文章介绍了资源文件转为Document文件. 下面接着介绍doLoadBeanDefinitions中最核心的部分.如果说前面两篇文章都是介绍XML解析的准备阶段的话,那么registerBeanDefinitions()里面会进行真正的XML文件的解析. 把代码贴一遍 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreExcepti

qq_40708942的博客 706

软件可靠性测试与评价

本文系统介绍了软件可靠性工程的核心内容,包括可靠性概念、模型分类、测试方法和评价体系。重点解析了可靠性测试的三大步骤:定义运行剖面、设计测试用例和实施测试收集数据,并以智能安防系统为例说明具体应用。可靠性评价需结合业务场景,通过量化指标(MTBF、失效率等)和模型预测持续优化系统。文章强调可靠性需贯穿软件全生命周期,通过闭环反馈实现持续改进,为关键系统提供质量保障。

moton2017的博客 2050

spring frmework源码分析

子路老师2021版spring源码深度分析,由于之前两个版本的spring源码课程是在公司里面录制的,存在很多局限性(时间、讲课方式)也存在很多不合理的地方;这次把spring源码课程进行了一次重制;属于子路老师个人行为,内容更加的丰富,知识更加深入;也纠正了以前版本的一些错误。之前的课程基本是围绕spring bean的生命周期周边进行讲解,这次重制之后除了对bean的生命周期更加入了很多以前没有涉及到的知识;比如ASM的扫描原理,spring扫描器的扩展原理,beanFactory的各种api、以及spring cloud对spring的扩展等等;内容和深度也是之前版本的5倍以上,真正的对spring源码逐行分析;可以让你学完之后对spring源码有更加深入了解,在面试中轻松对答,在工作中轻松实现对spring的扩展;成为真正的spring大师。这里是部分课程的内容截图 以及部分源码的注释   

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

BeanDefinition的注册

上节完成对BeanDefinition的解析分析后,下面继续了解BeanDefinition的注册过程 main方法 public static void main(String[] args) { //1.资源定位 Resource resource = new ClassPathResource("bean.xml"); //2.创建IOC容器,这里使用DefaultListableBeanFactory作为IOC容器 DefaultListableBeanFactory default

保质期已过 592

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

spring源码解析-容器的基本实现

spring源码解析-容器的基本实现,本人精心整理,包含详细的流程图。

qq_22270363的博客 546

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

7、【死磕 Spring】—— IoC 之注册 BeanDefinitions(转译)

获取 XML Document 对象后,会根据该对象和 Resource 资源对象调用XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)方法,开始注册 BeanDefinitions 之旅。代码如下: // AbstractBeanDefinitionReader.java...

a1290123825的博客 347

Spring的爬坑之路(七)解析及注册BeanDefinilions

Spring的爬坑之路(七)解析及注册BeanDefinilionsprofile 属性的使用 文档二中,最后在方法doLoadBeanDefinitions中步骤三为:根据返回的 Document 注册 Bean 信息。上文我们也已经了解把文件转换为 Document,接下来的提取及注册 bean到底是如何完成的呢?我们简单分析一下~

qq_40540178的博客 568

SpringBoot 基础概念:注册BeanDefinition

Spring注册BeanDefinition

java_xiaoba1的博客 947

spring源码学习(三)解析和注册BeanDefinition(上)

spring通过registerBeanDefinitions来解析和注册BeanDefinition。位于XmlDefinitionsReader类中 。

晓晨的博客 2706

bean标签的解析及注册-registerBeanDefinition

1、注册DefaultBeanDefinitionDocumentReader类下的processBeanDefinition方法里面的BeanDefinitionReaderUtils.registerBeanDefinition解析2、BeanDefinitionReaderUtils下面的registerBeanDefinition用于注册bean第一步使用beanName作为标识注册第二步...

JAVA圈的博客 1万+

registerBeanDefinition

public interface BeanDefinitionRegistry extends AliasRegistry { /** * 在此注册表中注册一个新的bean定义。必须支持RootBeanDefinition和ChildBeanDefinition。 * @param beanName 要注册的bean实例的名称 * @param beanDefinition 要注册的bean实例的定义 *指定的bean名称,我们不允许覆盖它 * @see GenericBeanDe.

zzzzzyyyz的博客 2234

解析及注册BeanDefinitions

当程序已经拥有XML文档文件的Document的实例对象时,就会被引入下面这个方法。public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = crea...

panzm_csdn的博客 331

spring 注册Bean的好几种方法

1. @Bean 通过在类级别上用@Configuration注解,方法上用@Bean注解显式注入Bean 。 类似xml配置中通过&amp;amp;amp;amp;amp;amp;lt;beans&amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;lt;bean&amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;lt;bean&amp;amp;am

www.yiye.tech 3022

BeanDefinition注册-上篇

BeanDefinition注册-上篇 前言 上文说到Document已经加载完毕,此时到了Spring注册BeanDefinition的时候。下面就一起来探究下Spring是怎么把Xml文档注册成BeanDefinition的吧。 XmlBeanDefinitionReader.registerBeanDefinitions registerBeanDefinitions方法 publi...

PP_BLOG 242
上一篇: Spring Core Container 源码分析三:Spring Beans 初始化流程分析
下一篇: javascript:this 关键字
伤神v
博客等级 码龄19年 17粉丝 129原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值