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