1、IoC 和 DI
IoC (Inversion of Control),即控制反转,是 Spring 的一种设计思想。
们直接在对象内部通过 new 来创建对象,是程序主动去创建依赖对象;
而在 Spring 中有专门的一个容器来创建和管理这些对象,并将对象依赖的其他对象注入到该对象中,这个容器我们一般称为 IoC 容器。控制反转是将对象管理的控制权反转
DI(Dependency Injection),即依赖注入,IoC 和 DI 其实是同一个概念的不同角度描述。依赖注入是指组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中
为什么使用spring而不是使用工厂模式,因为spring提供的ioc容器不仅仅是一个工厂,同时内部维护了对象之间的依赖关系。
2、bean
bean 是一个由 Spring IoC 容器实例化,组装和管理的对象
3、BeanDefinition
BeanDefinition 是 bean 的定义,用来存储 bean 的所有属性方法定义。
4、BeanFactory 和 ApplicationContext
BeanFactory:基础类型 IoC 容器,提供完整的 IoC 服务支持。
ApplicationContext:BeanFactory 的子接口,在 BeanFactory 的基础上构建,是相对比较高级的 IoC 容器实现。包含 BeanFactory 的所有功能,还提供了其他高级的特性,比如:事件发布、国际化信息支持、统一资源加载策略等。正常情况下,我们都是使用的 ApplicationContext。
5.FactoryBean
FactoryBean 是一种特殊的 bean,它是个工厂 bean,可以自己创建 bean 实例,如果一个类实现了 FactoryBean 接口,则该类可以自己定义创建实例对象的方法,只需要实现它的 getObject() 方法即可。
在中间件中,会看到 FactoryBean 的身影。
核心流程
一、容器构建启动入口
最终调用的是AbstractApplicationContext#refresh方法。
2.web容器中使用
在web.xml里面
配置了一个监听器
org.springframework.web.context.ContextLoaderListener
该监听器实现了ServletContextListener接口
tomcat启动的时候 会触发ServletContextListener的contextInitialized方法
从而执行 initWebApplicationContext 方法
在该方法里面初始化了WebApplicationContext这个容器
最终调用的还是AbstractApplicationContext#refresh方法。
此外 spring的配置 在contextConfigLocation这个属性里面
3.springboot里面
默认是初始化AnnotationConfigApplicationContext容器
ApplicationContext 刷新前配置
在正式进入容器的刷新前,会进行一些前置操作。
1、确认要使用的容器,通常使用的是:XmlWebApplicationContext,如果是用 Spring Boot,一般是 AnnotationConfigApplicationContext,但其实都差别不大,最终都会继承 AbstractApplicationContext,核心逻辑也都是在 AbstractApplicationContext 中实现。
2、提供一个给开发者初始化 ApplicationContext 的机会,具体的使用如下。
例子:ApplicationContextInitializer 扩展使用
1)创建一个 ApplicationContextInitializer 接口的实现类,例如下面的 SpringApplicationContextInitializer,并在 initialize 方法中进行自己的逻辑操作,例如:添加监听器、添加 BeanFactoryPostProcessor。
public class SpringApplicationContextInitializer implements
ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 自己的逻辑实现
// 例子1:通过硬编码的方式添加监听器
EarlyListener earlyListener = new EarlyListener();
applicationContext.addApplicationListener(earlyListener);
// 例子2:通过硬编码的方式添加BeanFactoryPostProcessor
MyBeanFactoryPostProcessor myBeanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(myBeanFactoryPostProcessor);
}
}
2)在web.xml中,定义 contextInitializerClasses 或 globalInitializerClasses 参数,参数值为 SpringApplicationContextInitializer 的全路径。
三、初始化 BeanFactory、加载 Bean 定义
1、创建一个新的 BeanFactory,默认为 DefaultListableBeanFactory。
2、根据 web.xml 中 contextConfigLocation 配置的路径,读取 Spring 配置文件,并封装成 Resource。
3、根据 Resource 加载 XML 配置文件,并解析成 Document 对象 。
4、从根节点开始,遍历解析 Document 中的节点。
4.1、对于默认命名空间的节点:先将 bean 节点内容解析封装成 BeanDefinition,然后将 beanName、BeanDefinition 放到 BeanFactory 的缓存中,用于后续创建 bean 实例时使用。
默认命名空间:http://www.springframework.org/schema/beans,可能存在的节点如下:
4.2、对于自定义命名空间的节点:会拿到自定义命名空间对应的解析器,对节点进行解析处理。
自定义命名空间对应的解析器 对应自定义的标签
存放在meta-inf目录下的spring.handler里面
任何与spring兼容的框架都提供了自己的namespace解析器
同时
spring.schemas提供了xsd的路径
:
四、触发 BeanFactoryPostProcessor
实例化和调用所有 BeanFactoryPostProcessor,包括其子类 BeanDefinitionRegistryPostProcessor。
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。
BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的 BeanFactoryPostProcessor 激活之前注册一些 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。
注:这边的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。
例子:BeanFactoryPostProcessor 扩展使用
1)创建一个 BeanFactoryPostProcessor 接口的实现类,例如下面的 MyBeanFactoryPostProcessor,并在 postProcessBeanFactory 方法中进行自己的逻辑操作。例如:扫描某个包路径,将该包路径下使用了某个注解的类全部注册到 Spring 中。
2)将该实现类注册到 Spring 容器中,例如使用 @Component 注解
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor#postProcessBeanFactory");
// 自己的逻辑处理
}
}
另外,Mybatis 中的 MapperScannerConfigurer 是一个典型的 BeanDefinitionRegistryPostProcessor 的扩展使用
五、注册 BeanPostProcessor
注册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
BeanPostProcessor 接口是 Spring 初始化 bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。
例子:BeanPostProcessor 扩展使用
1)创建一个 BeanPostProcessor 接口的实现类,例如下面的 MyBeanPostProcessor,并在方法中进行自己的逻辑操作。
2)将该实现类注册到 Spring 容器中,例如使用 @Component 注解。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization");
// 自己的逻辑
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor#postProcessAfterInitialization");
// 自己的逻辑
return bean;
}
}
六、实例化所有剩余的非懒加载单例 bean
1、遍历所有被加载到缓存中的 beanName,触发所有剩余的非懒加载单例 bean 的实例化。
2、首先通过 beanName 尝试从缓存中获取,如果存在则跳过实例化过程;否则,进行 bean 的实例化。
3、根据 BeanDefinition,使用构造函数创建 bean 实例。
4、根据 BeanDefinition,进行 bean 实例属性填充。
5、执行 bean 实例的初始化。
5.1、触发 Aware 方法。
5.2、触发 BeanPostProcessor 的 postProcessBeforeInitialization 方法。
5.3、如果 bean 实现了 InitializingBean 接口,则触发 afterPropertiesSet() 方法。
5.4、如果 bean 设置了 init-method 属性,则触发 init-method 指定的方法。
5.5、触发 BeanPostProcessor 的 postProcessAfterInitialization 方法。
6、将创建好的 bean 实例放到缓存中,用于之后使用。
七、完成上下文的刷新
使用应用事件广播器推送上下文刷新完毕事件(ContextRefreshedEvent )到相应的监听器。
例子:监听器扩展使用
1)创建一个自定义监听器,实现 ApplicationListener 接口,监听 ContextRefreshedEvent(上下文刷新完毕事件)。
2)将该监听器注册到 Spring IoC 容器即可。
就能收到初始化完成的事件通知
@Component
public class MyRefreshedListener implements ApplicationListener {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 自己的逻辑处理
}
}
至此,整个 IoC 的核心流程介绍完毕。