面试官:聊一聊Spring实例化Bean都有哪些方式?
Spring功能简介
我们都知道Spring是一个ioc容器,它主要给我们提供ioc,aop以及依赖注入功能,通过spring我们在应用中并不需要了解对象如何创建,这个都交给spring容器去管理。通过简单的声明式配置就可完成对象的装配,可以简单的理解成Spring内部就是一个很大的Map,里面存储了很多实例化好的java对象,我们需要的时候制需要getBean 或者通过属性,setter,构造器,接口 等自动注入方式直接依赖其他的Bean。Spring容器表面看起来非常的简单,但是它内部实际上是一个非常复杂的过程,Spring的源码非常的庞大,分支和接口也非常多,本节不说源码,只是简单的和大家分享Spring的核心流程和Bean创建的几种方式。看完后你一定对Spring创建Bean的过程有更深的理解。
Spring容器bean创建流程
spring容器创建Bean的过程非常复杂,这里我通过一个图简单的把Bean创建的核心流程画出来,有助于大家可用进一步了解Bean创建的过程。
上图可用看到,Spring创建bean主要经过四个流程
- 资源定位 ,可从xml配置文件,java注解,java配置类等各种入口加载bean的来源
- 加载解析,将各种入口资源解析成BeanDefinition
- 注册,将第二步解析出来的各种BeanDefinition注册到BeanDefinition容器中等待实例化
- 实例化,将BeanDefinition容器中的BeanDefinition实例化成最终的Bean对象
其中在BeanDefinition到Bean的过程中需要经过BeanFactoryPostProcessor前置处理器,在Bean实例化前可以对BeanDefinition进行修改,创建Bean后会通过BeanPostProcessor后置处理器对Bean进行修改或者进一步包装。很多优秀的框架都是基于前置和后置处理器对Spring做二次扩展,实现了很多强大的功能,特别是SpringBoot在这方面更是突出的代表。
Bean的创建方式
通过上面对Bean创建流程的分析后,大概就清楚在具体应用中我们到底有那些方式可用创建Bean了,实际应用中主要可用通过以下几个方式创建Bean.
- 通过注解扫描方式
我们常见的 @Component,@Service,@Controller,@Configuration 这几个注解都被Spring扫描到并自动注入到容器,其中后面三个其实就是@Component,只不过从名字上区分不同类别而已。
- 其次用@import导入配置类,这种配置方式就是直接导入一个类进行配置,比如我们非常熟悉的 @EnableAsync,@EnableScheduling 这两个注解内部就是通过@import导入的。
- 直接使用BeanDefinitionRegistry接口,通过编程的方式自定义BeanDefinition后注入到容器。
总结
注解扫描的方式多用于本项目默认扫描的包路径,比如@SpringBootApplication注解所在的包路径下才能被扫描到。
@import用于导入不在默认扫描路径下的配置类,比如一些外部的第三方包
前面两种都是通过声明式方式配置Bean,这两种方式都在容器启动后Bean初始化完成了。通过BeanDefinitionRegistry接口可以动态的创建Bean,比如在运行的过程中创建Bean并交给Spring托管,这种方式最灵活。比如我们可以根据配置文件动态的创建多个数据源,也可以在各种数据源管理应用运行的过程中再创建数据源,其实Spring底层最终都是通过这个接口注册BeanDefinition的。