引入
Spring 是如何生成一个Bean的?
我们先看个例子
我们有个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的实现是很复杂的,我们这里先对其大致的过程和原理有个初步的了解,方便后续源码的展开
singleton vs prototype
spring容器在启动的时候就已经将singleton的bean 缓存到 单例池中,而 prototype类型的bean ,在spring容器启动的时候并不会被实例化,仅在调用的时候生成,且每次都一个新的对象 。
看个演示
加个断点,
可以发现 还没执行到ac.getBean(B.class)
,仅在容器启动阶段就已经实例化完成了singleton作用域的bean。
接下来我们看下prototype类型的bean
可以看到 在spring容器启动的时候,并没有实例化prototype类型的bean 。
Singleton VS Prototype 小结
我们看到了 Singleton VS Prototype的区别
- 创建时机不同: singleton的bean是在容器初始化的时候创建的,而原型bean是在调用的时候创建的
- 作用域不同: 单例singleton的bean 在容器中仅会创建一次,并且只有一个,而原型Prototype类型的bean每次调用都会初始化一个新的对象。
下文
下文我们探讨对象和bean的关系