控制反转与大家熟知的依赖注入同理, 这是通过依赖注入对象的过程. 创建 Bean 后, 依赖的对象由控制反转容器通过构造参数 工厂方法参数或者属性注入. 创建过程相对于普通创建对象的过程是反向, 称之为控制反转 (IoC).
Tomcat 也是 IoC 的一个 Bean
ApplicationContext
ApplicationContext
是 Spring IoC 容器实现的代表, 它负责实例化, 配置和组装 Bean.
IoC 使用元数据配置这种形式, 这个配置元数据表示了应用开发人员告诉 Spring 容器以何种方式实例化 配置和组装应用程序中的对象.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="..."> (1) (2)
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
除了 id 和 class 外, <bean></bean>
标签支持指定 factory-bean
factory-method
标识容器以何种方式实例化对象.
元数据
元数据 (BeanDefinition), 一种类似于 java.lang.Class
的定义格式.
- SpringApplication.run
- AbstractApplicationContext.refresh
- AbstractRefreshableApplicationContext.refreshBeanFactory
- BeanDefinitionRegistry.registerBeanDefinition (多为 ConcurrentHashMap)
容器对通过 xml
或是其他方式定义的解析成 BeanDefinition 对象, 并保存在 BeanDefinitionRegistry.beanDefinitionMap(ConcurrentHashMap) 中.
BeanDefinition 包括 factory-bean 和 factory-method 以备创建实例.
注意: 如果没有指定构造器这种方式, 会选择无参构造器去创建. 事实上, 大部分构造方式都是无参构造创建, 再由DI setter 注入. 如: Controller
Service
等
Bean 和 getBean
容器没有直接提供一个方法去 set 对象到容器里, 对外只暴漏了 getBean 这个方法. 容器中如果没有 Bean 实例, 这时才会去创建实例.
为了便于理解, 我会分开说.
- getBean 获取
- AbstractBeanFactory.getBean
- AbstractBeanFactory.doGetBean
- SingletonBeanRegistry.getSingleton (多为 ConcurrentHashMap)
- createBean 创建
- AbstractBeanFactory.getBean
- AutowireCapableBeanFactory.createBean
- AutowireCapableBeanFactory.doCreateBean
- BeanDefinitionRegistry.getBeanDefinition (多为 ConcurrentHashMap)
- instantiate (getDeclaredConstructor)
- SingletonBeanRegistry.addSingleton (多为 ConcurrentHashMap)
容器将创建好的 Bean addSingleton 到 SingletonBeanRegistry.singletonObjects(ConcurrentHashMap) 中. 需要时, getSingleton 可以直接获取.
Bean 整体加载过程如上, 可以看出在 loadClass 没有被重载的情况下, 走的还是 JVM 的双亲委派.
扫描与注解
之前提到过除了 xml
还可以使用其他方式, 比较常见的就是注解的方式, 这种方式一般搭配扫描注解一起使用. 其实本质上还是找到类的 Class
并且 loadBeanDefinitions 供 instantiate 方法来使用.