Spring的IoC和Bean
Spring的IoC和Bean
Spring框架的特性之一就是IoC(Invocation of Control)容器。IoC中文称作控制反转,为什么这么命名呢?以前都是由开发者来控制对象实例的创建、依赖等,而控制反转,顾名思义,就是Spring会帮我们完成对象的创建和依赖等操作,开发者最多只需要做一些配置或注解的声明即可。所以,IoC容器的出现为基于Spring框架开发的程序员来说大大地提高了开发效率。
Bean其实就是一个对象实例,只不过和一般的对象实例不同的是,它存储来IoC容器中,由IoC容器维护创建、依赖等操作。并且,Spring会为Bean创建BeanDefinition配置信息,用于描述这个Bean创建、依赖需要的元数据,这也是Bean和普通的对象实例很大的区别之一。
BeanFactory和ApplicationContext
BeanFactory是Spring框架内置的Bean工厂类,可用于获取创建的Bean实例或者判断Bean的类型,一般在框架逻辑中使用,不用于业务场景;而ApplicationContext,顾名思义,就是整个应用的上下文,可用于获取应用管理的Bean信息,常用于业务环境下获取IoC容器中维护的Bean。
Bean元数据配置
Bean的元数据配置一般有三种方式:
1、XML
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
2、注解
@Component("dataManager")
public class DataManager {
public String sayHello(String name) {
return "hello, " + name;
}
}
3、Java Config
@Configuration
public class WebConfiguration {
@Bean(name = "dataManager1", initMethod = "init", destroyMethod = "destroy")
public DataManager getDataManager() {
return new DataManager();
}
}
Bean的生命周期
1. BeanDefinition
扫描通过XML、注解、javaConfig定义的bean生成对应的元数据信息BeanDefinition,然后存储BeanDefinitionMap中,key为beanName,value为BeanDefinition,用于后续创建bean使用。
2. BeanFactoryPostProcessor
BeanFactoryPostProcessor是BeanFactory后置处理器,用于对收集完的BeanDefinition进行修改。它会遍历BeanDefinitionMap中的BeanDefinition,执行所有实现类逻辑。
比如PropertyPlaceholderConfigurer,用于将Bean配置中的占位符替换成目标值,下面举个简单的例子说明:
1)声明PropertyPlaceholderConfigurer实例,指定配置文件路径,允许未找到占位符目标值;
2)声明一个存在占位符的bean实例,并在jdbc.properties中定义占位符目标值;
3)创建一个自定义BeanFactoryPostProcessor实现类,监控替换前的name和value;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd ">
<!--ignore-unresolvable:默认false,如果没有找到占位符的替换值,直接报错-->
<context:property-placeholder location="classpath:xml/jdbc.properties" ignore-unresolvable="true"/>
<bean class="org.xiangzhu.test.common.EchoBeanFactoryPostProcessor"/>
<bean id="dataSource"
class="org.xiangzhu.test.common.DataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
public class EchoBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition dataSourceBd = beanFactory.getBeanDefinition("dataSource");
MutablePropertyValues propertyValues = dataSourceBd.getPropertyValues();
log.info("postProcessBeanFactory pv size = {}", propertyValues.size());
//打印占位符的name和value
propertyValues.forEach(pv -> log.info("postProcessBeanFactory name={}, value={}", pv.getName(), pv.getValue()));
}
//最高执行优先级
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
执行结果:
3. Create&PopulateBean
Bean三级缓存:
-
bean对象实例;
-
早期bean对象实例(未依赖注入);
-
beanFactory对象(实现了createBean方法的闭包对象);
为了理解前面Bean三级缓存的执行时机,我们举个例子说明下,现在有两个对象A和B,它们相互依赖对方,来看下它们的实例化过程:
-
假设A先实例化
-
由于A不存在缓存,调用doCreateBean方法创建A,并写入三级缓存
-
-
A依赖注入
-
A依赖B,B不存在缓存,B实例化,写入三级缓存
-
B依赖注入
-
A存在缓存,将三级缓存写入二级,并移除三级缓存

-
B依赖注入完成
-
-
B初始化
-
B写入一级缓存
doGetBean -》getSingleton -》addSingleton(写入一级缓存)
-
A依赖注入完成
-
-
A初始化
-
A写入一级缓存
上面介绍的AB实例加载顺序同样也解答了Spring是如何处理循环依赖问题的,对于循环依赖,它本身是一个设计问题,不应该由框架层面来解决,虽然Spring提供了循环依赖的解决方案,但是它同样带来了负面问题,比如应用启动不稳定、系统复杂性上升、测试样例设计困难等问题。所以,Spring框架在2.6版本后默认不启用循环依赖支持,因为这个问题根本上应该由业务开发者优化依赖解决。
4.InitializeBean

如上图,初始化方法执行顺序为:
- Aware方法
- BeanPostProcessor Before
- ApplicationContextAwareProcessor(Aware方法)
- AbstractAutoProxyCreator(代理对象生成)
- InitDestroyAnnotationBeanPostProcessor(@PostConstruct方法)
- ...
- InitMethods
- InitializingBean(afterPropertiesSet)
- init-method
- BeanPostProcessor After
1) Aware
在Spring中Aware是一个接口,aware英文有感知的含义,所以实现Aware接口的对象具备感知框架运行状态和上下文的能力。常见的Aware细化接口有:
- BeanNameAware
- BeanClassloaderAware
- BeanFactoryAware
- ApplicationContextAware(最常用,感知应用上下文)
以下是对应Aware接口的回调位置,第一个是在invokeAwareMethods中:

第二个是在ApplicationContextAwareProcessor这个后置处理器的invokeAwreInterfaces方法中:


2) BeanPostProcessor
BeanPostProcessor是一个后置处理器接口,在Bean的初始化前后做一些事情,包含before和after方法。这是Spring框架的重要拓展点之一,该接口实现了AbstractAutoProxyCreator这个后置处理器实例,使得框架可以在其after方法中完成代理对象的生成,从而实现Spring重要特性之一:AOP代理。
其他常见的BeanPostProcessor还有AutowiredAnnotationBeanPostProcessor,它也用于占位符替换目标值,不过应用的是@Value("${aaa.bbb}")的场景。
3) InitMethods
InitMethod用于在Bean实例化完成之后,执行一些开发者自定义的初始化方法,InitMethod和执行顺序如下:
-
@PostConstruct
-
InitializingBean的afterPropertiesSet
-
init-method
下面是InitMethods的具体执行时机:
@PostConstruct注解的方法在InitDestroyAnnotationBeanPostProcessor的before方法中回调。

而afterPropertiesSet和init-method方法在invokeInitMethods方法中回调。

对于InitMethods和BeanFactory的执行顺序,下面举例说明下:
1)创建UserService对象,并实现拓展点
public class UserService implements InitializingBean {
//step2 di
@Resource
private DataManager dataManager;
@Value("${mysql.namespace}")
private int namespace;
//step1 construct
public UserService() {
log.info("UserService construct, namespace = {} dataManager = {}", namespace, dataManager);
}
//step3 postConstruct
@PostConstruct
private void postConstruct() {
log.info("UserService postConstruct, namespace = {} dataManager = {}", namespace, dataManager);
}
//step4 afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
log.info("UserService afterPropertiesSet, namespace = {} dataManager = {}", namespace, dataManager);
}
public int getNamespace() {
return namespace;
}
public void setNamespace(int namespace) {
this.namespace = namespace;
}
}
2)实现BeanPostProcessor自定义类,监控UserService对象,并修改属性值
public class EchoBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean.getClass().getName().startsWith("org.xiangzhu.test")) {
log.info("postProcessBeforeInitialization beanName = {}, bean = {}", beanName, bean);
if (bean instanceof UserService) {
UserService userService = (UserService) bean;
userService.setNamespace(666);
}
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean.getClass().getName().startsWith("org.xiangzhu.test")) {
log.info("postProcessAfterInitialization beanName = {}, bean = {}", beanName, bean);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
3)应用启动完成后,打印UserService属性值,观察是否修改成功
public class SpringLearnApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringLearnApplication.class, args);
UserService userService = context.getBean(UserService.class);
log.info("applicationContext bootstrap finish, userService.namespace = {}", userService.getNamespace());
}
}
4)执行结果

5.使用中
使用实例执行业务逻辑。
6.销毁
调用destroy-method,实例gc。
其他
autoconfig
SpringBoot之AutoConfiguration自动配置原理
EventListener
EventListenerMethodProcessor是一个BeanFactoryPostProcessor,后置处理中通过遍历所有bean,找到其中添加了@EventListener注解的,并将其包装成ApplicationListenerMethodAdapter,添加到上下文,从而使得@EventListener注解的类和实现了ApplicationListener的类目功能一样,可以监听Spring环境中发布的事件Event。

参考
怎么理解spring bean的生命周期,实际应用场景? - 知乎 (zhihu.com)
Spring源码解读『占位符@Value(“${…}”)替换』_卓立~的博客-CSDN博客
[Spring源码解读『占位符${…}替换』_spring 占位符字符串替换_卓立的博客-CSDN博客](https://blog.csdn.net/weixin_41835612/article/details/107242055#::text=Spring处理以上两种占位符的替换采用不同的方式, xml注入的占位符Spring采用bean工厂后置处理器处理,注解方式的占位符Spring采用bean后置处理器处理,,本篇文章我们先来看一下xml注入的占位符的替换过程。 1. xml占位符注入示例)

浙公网安备 33010602011771号