前言
SpringBoot的核心之一是通过IOC容器管理各个bean对象,前几天看到一个大厂面试题问向Spring容器中注入bean有哪几种方式,今天整理了下跟大家分析一波。
这个题看似简单,实则暗藏玄机,可能大部分掘友能回答出一部分,要回答全面在众多面试者中脱颖而出似乎不是那么简单。
首先面试官问出这个问题,思路应该要立刻转到SpringBoot的启动流程来,按SpringBoot是怎么扫描出要注入的类入手。
启动流程模糊的掘友可以回顾下之前的文章:【SpringBoot启动流程】https://developer.aliyun.com/article/1038860?spm=a2c6h.13148508.setting.15.229f4f0ew1zijc
@Component + @ComponentScan
SpringBoot启动类的@SpringBootApplication注解中就标注了 @ComponentScan注解,这个注解默认扫描当前包及其子包下的标注有@Component、@Controller、@Service、@Repository等的类加载到容器中。
这一条应该都能答出来,属于基础八股了。
@Import注解
了解过SpringBoot源码的掘友应该对这个注解不陌生,SpringBoot之所以拥有自动装配能力,全依仗于启动类@SpringBootApplication注解中的另一个核心注解@EnableAutoConfiguration。
点进去这个注解会发现 @Import(AutoConfigurationImportSelector.class),也就是通过将AutoConfigurationImportSelector类加载到容器中。
并通过此类的getAutoConfigurationEntry()方法,查找并筛选出位于META-INF/spring.factories文件中的所有需要注入的自动配置类并加载。
这个注解也可以直接注入class,SpringBoot默认是注入ImportSelector接口,重写selectImports规则实现,本质上都是将外部类加载到当前classpath中并注入成bean。
@Configuration + @Bean
这个组合相信大家都有用过,比如想利用ioc容器管理一个map容器,只需要在配置类上标注上@Configuration声明配置类,在某个方法上标注@Bean并返回一个new HashMap即可。
publicclassTestConfig { publicMapTestAutoMap() { returnnewHashMap(); } }
BeanDefinitionRegistryPostProcessor
万变不离其宗,掌握SpringBoot启动流程和SpringBean的生命周期,很多问题就能连点成线的串起来。
我们知道SpringBean的生命周期中有很多前后置方法,整体上可以概括为普通类对象转化为beanDefinition再转化为spring中的bean这么三个阶段。
而Spring会在启动的AbstratApplicationContxt类中的refresh方法中执行
invokeBeanFactoryPostProcessors,这个方法中会回调所有实现
BeanDefinitionRegistryPostProcessor接口的钩子方法。
可以简单理解成beanDefinition加载完毕之后,会对beanDefinition进行后置处理。所以理论上实现BeanDefinitionRegistryPostProcessor接口就可以手动将bean注入到容器中。
publicclassTestBeanProcessor { publicstaticvoidmain(String[] args) { AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(); UptownBeanProcessorbeanDefinitionRegistryPostProcessor=newUptownBeanProcessor(); applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor); applicationContext.refresh(); Objectbean=applicationContext.getBean("test_map"); System.out.println(bean); } } classUptownBeanProcessorimplementsBeanDefinitionRegistryPostProcessor { publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry) throwsBeansException { AbstractBeanDefinitionbeanDefinition=BeanDefinitionBuilder.rootBeanDefinition(HashMap.class).getBeanDefinition(); registry.registerBeanDefinition("test_map", beanDefinition); } publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory) throwsBeansException { } }
总结
- 按照SpringBoot启动原理和SpringBean的生命周期为思路
- @Component + @ComponentScan
- @Bean + @Configuration
- @Import
- BeanDefinitionRegistryPostProcessor接口实现postProcessBeanDefinitionRegistry后置函数