Spring5源码 - 01 BeanDefination源码分析

文章目录

Spring5源码 - 01 BeanDefination源码分析

引入

Spring 是如何生成一个Bean的?

我们先看个例子

Spring5源码 - 01 BeanDefination源码分析

我们有个Configuration类AppConfig ,通过ComponentScan定义了扫描com.artisan目录下所有表了标注了注解的Bean

package com.artisan;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.artisan")
public class AppConfig {
}

同时com.artisan目录下还有A.class 和 B.class , 其中B类上标注了@Component注解,A上仅仅一个普通的Java类

package com.artisan.test;

import org.springframework.stereotype.Component;

@Component
public class B { 
}

package com.artisan.test;

public class A {
}

我们启动下Spring容器,然后尝试去重bean容器中获取A和B的单例对象,看看会发生什么?

package com.artisan.test;

import com.artisan.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BeanLoadTest {
	public static void main(String[] args) {
		// spring容器
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

		System.out.println(ac.getBean(B.class));
		System.out.println(ac.getBean(A.class));
	}
}

结果显而易见, 被标注了@Component注解的B类,成功的从bean容器中获取到了,而A类是无法从bean容器中获取到的 ( No qualifying bean of type ‘com.artisan.test.A’ available)。

com.artisan.test.B@28864e92
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.artisan.test.A' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1177)
	at com.artisan.test.BeanLoadTest.main(BeanLoadTest.java:12)

这是为什么呢?Spring的源码是如何实现的呢? 接下来的文章,我们逐层揭开Sping Bean的神秘面纱 …


Bean实例化的原理

我们知道普通的Java类的实例化的过程,被JVM编译成字节码文件以后,通过new 关键字实例化。

Spring bean的实例化区别于普通Java类的实例化过程,是一个相对复杂的过程。

原理如下

1. 扫描到可以受到Spring管理的Bean,将其转化成BeanDefinition

2. BeanDefinition是个接口,其中有很多属性可以设置,比如 scope 、lazyInit、dependson 、autoType 、关联的是哪个类setBeanClass 等等

3. 将BeanDefinition放入到一个Map中

4. 遍历Map ,取出BeanDefinition,根据你上一步设置的各种属性,去做不同的操作,比如autoType 、是否懒加载等等等等,实例化Bean

5. 实例化完成以后,将实例化好的Bean放到map中 (就是spring容器),即Spring的单例池 singletonObjectsMap

这里只会创建单例模式的Bean,prototype等类型的bean不会添加到单例池中,因为prototype每次都会new一个,没有必要去缓存。

当然了,Spring的实现是很复杂的,我们这里先对其大致的过程和原理有个初步的了解,方便后续源码的展开

Spring5源码 - 01 BeanDefination源码分析


singleton vs prototype

spring容器在启动的时候就已经将singleton的bean 缓存到 单例池中,而 prototype类型的bean ,在spring容器启动的时候并不会被实例化,仅在调用的时候生成,且每次都一个新的对象 。

看个演示
Spring5源码 - 01 BeanDefination源码分析

加个断点,
可以发现 还没执行到ac.getBean(B.class),仅在容器启动阶段就已经实例化完成了singleton作用域的bean。

Spring5源码 - 01 BeanDefination源码分析


接下来我们看下prototype类型的bean

Spring5源码 - 01 BeanDefination源码分析

可以看到 在spring容器启动的时候,并没有实例化prototype类型的bean 。

Spring5源码 - 01 BeanDefination源码分析

Spring5源码 - 01 BeanDefination源码分析


Singleton VS Prototype 小结

我们看到了 Singleton VS Prototype的区别

  1. 创建时机不同: singleton的bean是在容器初始化的时候创建的,而原型bean是在调用的时候创建的
  2. 作用域不同: 单例singleton的bean 在容器中仅会创建一次,并且只有一个,而原型Prototype类型的bean每次调用都会初始化一个新的对象。

下文

下文我们探讨对象和bean的关系

上一篇:Redis进阶-List底层数据结构精讲


下一篇:为`php artisan serve`设置默认的’host’值