Spring IOC
前言
Spring框架为什么如此流行?
原来Spring框架解决了一个很关键的问题,它可以把对象之间的依赖关系转为用配置文件来管理,也就是它的依赖注入机制。IOC容器用来管理这些Bean,管理Bean的关系以及生命周期,然而这与之前将应用程序主动new对象不同,Spring实现使用IOC容器创建对象,对象的获取方式反转了,所以IOC容器也称为控制反转。面对繁琐的依赖关系,我们不用一个一个去new对象,直接使用IOC创建好的对象,这也正是IOC的方便之处。
Spring 核心组件:Bean Context,Core
Bean
Bean组件在org.springframework.beans包下,这个包下所有的类解决了Bean的定义,创建以及解析。
1.Bean的定义:主要有BeanDefinition描述,也可以说Spring中的Bean就是BeanDefinition的实例。Spring成功解析一个<bean/>节点后,在Spring的内部它就被转化为BeanDefinition对象。
2.Bean的创建:.Spring Bean的创建是典型的工厂设计模式,*接口是BeanFactory
3.Bean的解析:Bean的解析主要是对配置文件的解析
Context
context组件在org.framework.context包下,主要就是Bean关系的集合。给Spring提供了一个运行时环境用于存储对象的状态。ApplicationContext是*的父类
ApplicationContext继承了BeanFactory说明了Spring容器运行的主要对象是Bean
ApplicationContext实现了ResourceLoader接口说明ApplicationContext可以访问到外部资源。
Core
core组件就是发现,建立和维护每个Bean之间关系所需要的一系列工具。core组件其中一个重要的组成部分就是定义了资源的访问方式。
IOC的工作原理
下面我们看看IOC是怎样工作的呢?
先来一个小demo
MessageService.java
public interface MessageService { String getMessage(); }
MessageServiceImpl.java
public class MessageServiceImpl implements MessageService { public String getMessage() { return "hello world"; } }
application.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName"> <bean id="messageService" class="com.kristin.spring.ioc.MessageServiceImpl"/> </beans>
TestMessage.java
public class TestMessage { public static void main(String[] args) { // 用我们的配置文件来启动一个 ApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml"); System.out.println("context 启动成功"); // 从 context 中取出我们的 Bean,而不是用 new MessageServiceImpl() 这种方式 MessageService messageService = context.getBean(MessageService.class); // 这句将输出: hello world System.out.println(messageService.getMessage()); } }
运行结果:
IOC运行分析
从上面代码可以发现,IOC的入口就是ClassPathXmlApplicationContext的构造方法,下面我们看看源码是怎样写的
ClassPathXmlApplicationContext.java
public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); //根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割) if (refresh) { refresh(); //这里是核心代码 } }
下面我们看一下refresh()
ClassPathXmlApplicationContext.java
@Override public void refresh() throws BeansException, IllegalStateException { //这里加锁,防止多个线程同时refresh()时出现问题 synchronized (this.startupShutdownMonitor) { // 为刷新准备新的context,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符 prepareRefresh(); // 刷新所有BeanFactory的子容器 // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中, // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了, // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 准备BeanFactory,设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean prepareBeanFactory(beanFactory); try { // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化 // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事 postProcessBeanFactory(beanFactory); // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法 invokeBeanFactoryPostProcessors(beanFactory); // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别 // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化 registerBeanPostProcessors(beanFactory); // 初始化message source initMessageSource(); // 初始化 event multicaster initApplicationEventMulticaster(); // 刷新由子类实现的方法 onRefresh(); // 注册事件监听器,监听器需要实现 ApplicationListener 接口。 registerListeners(); // 初始化所有的 singleton beans finishBeanFactoryInitialization(beanFactory); // 最后,广播事件,ApplicationContext 初始化完成 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
refresh函数主要包含了以下几个步骤:
1.构建BeanFactory,以便于产生所需的"演员"
2.注册可能感兴趣的事件
3.创建Bean实例对象
4.触发被监听的事件
创建BeanFactory的时序图:
继续看一看refresh()函数中调用的函数吧
AbstractApplicationContext.java
protected void prepareRefresh() { // 记录启动时间,将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean 类型 this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment initPropertySources(); // 校验 xml 配置文件 getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等 refreshBeanFactory(); // 返回刚刚创建的 BeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
AbstractRefreshableApplicationContext.java
@Override protected final void refreshBeanFactory() throws BeansException { // 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory // 注意,应用中BeanFactory本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前ApplicationContext是否有BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 初始化一个 DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); // 用于 BeanFactory 的序列化 beanFactory.setSerializationId(getId()); // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用 customizeBeanFactory(beanFactory); // 加载 Bean 到 BeanFactory 中,这个方法很重要 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
此时已经获得了BeanFactory
后面会陆续更新,暂时先整理到这里...
对了,以上文章整理自
https://javadoop.com/post/spring-ioc
《深入分析Java Web技术内幕》
感谢两位大神的文章及书籍
下一篇: 【Java学习】javac和java命令
推荐阅读
-
详解Spring Hibernate连接oracle数据库的配置
-
JSP开发之Spring方法注入之替换方法实现
-
JSP Spring配置文件中传值的实例详解
-
spring为java.util.Properties类型的属性进行赋值过程解析
-
Spring AOP定义AfterReturning增加实例分析
-
spring级联属性赋值的两种方式解析
-
spring如何使用命名空间p简化bean的配置
-
JSP Spring防止用户重复登录的实现方法
-
JSP 开发之Spring Boot 动态创建Bean
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理