一、IOC容器创建方式

Ioc容器的创建时通过ApplicationContext接口的相关实现类进行的。



如上图所示:有三种创建IOC容器的方式。

ClassPathXmlApplicationContext:从项目的根目录下加载配置文件

FileSystemXmlApplicationContext:从磁盘中的加载配置文件

AnnotationConfigApplicationContext:当使用注解配置容器对象时使用此类进行注解读取,创建容器。

二、IOC容器创建入口

1、Java中IOC容器创建

Java中通过如下代码进行配置文件读取:

ApplicationContext context = new ClassPathXmlApplicationContext("xml路径");

创建ClassPathXmlApplicationContext对象的过程中,调用refresh方法完成容器的创建与初始化。

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//容器的创建与初始化
refresh();
}
}

2、Web中IOC容器创建

web项目中,Spring启动是在web.xml配置监听器,如下所示:

 <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

ContextLoaderListener类实现了Tomcat容器的ServletContextListener接口,与其他Servlet监听一样,重写了两个方法:contextInitialized()方法进行web容器初始化,contextDestroyed()方法进行容器销毁。

//初始化
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
//销毁
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}

初始化的过程中调用ContextLoader中的initWebApplicationContext()方法,代码如下:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//判断ServletContext是否已经存在WebApplication,如果存在则抛出异常
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
//抛出异常...
} Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis(); try {
if (this.context == null) {
//创建WebApplicationContext
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//得到根上下文的父上下文,然后设置到根上下文,一般的web项目parent为空
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//从web.xml加载参数,初始化根上下文WebApplicationContext,创建bean工厂和bean对象
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
} if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
} return this.context;
}
//异常处理...
}

这个方法中ServletContext是由web容器监听器(ContextLoaderListener)提供。首先判断servlectContext中是否已经存在根上下文,如果存在,则抛出异常;否则通过createWebApplicationContext方法创建新的根上下文。然后通过loadParentContext()方法为其设置父上下文。再通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。最后把上下文存入servletContext,并且存入currentContextPerThread。至此初始化过程完毕,接下来可以获取WebApplicationContext,进而用getBean("bean name")得到bean。

2.1、创建上下文

下面首先针对createWebApplicationContext进行分析,createWebApplicationContext方法用于创建跟上下文,其代码如下:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//从web.xml配置的contextClass参数中获取上下文类名,如果contextClass为空,则使用默认的。
Class<?> contextClass = determineContextClass(sc);
//根上下文必须是ConfigurableWebApplicationContext的子类,否则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
//根据类名创建类
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

determineContextClass方法用于返回根上下文的类名,代码如下:

protected Class<?> determineContextClass(ServletContext servletContext) {
//从web.xml获得参数contextClass,在一般的web项目中,此参数为null
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,在CotnextLoader类开头static语句块中初始化
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

Properties类型的初始化静态代码块:

static {
try {
//获取当前包下面的ContextLoader.properties文件
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}

2.2、初始化根上下文

初始化上下文的方法configureAndRefreshWebApplicationContext代码如下:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//设置应用程序上下文Id
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
} wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
//获取环境中配置的属性
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
} customizeContext(sc, wac);
//容器的创建与初始化
wac.refresh();
}

综上,对Web中创建IOC容器的流程总结的序列图如下:

3、refresh方法

从上面的分析看到,无论是Java或Web,最后的容器创建于初始化都会进入refresh方法中,下面对refresh进行分析,refresh方法在AbstractApplicationContext类中实现,其代码如下:

// 完成IoC容器的创建及初始化工作
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor)
// 1: 刷新预处理
prepareRefresh();
// 2:
// a) 创建IoC容器(DefaultListableBeanFactory)
// b) 加载解析XML文件(最终存储到Document对象中)
// c) 读取Document对象,并完成BeanDefinition的加载和注册工作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3: 对IoC容器进行一些预处理(设置一些公共属性)
prepareBeanFactory(beanFactory); try {
// 4:post-processing对BeanDefinition处理
postProcessBeanFactory(beanFactory); // 5: 调用BeanFactoryPostProcessor后置处理器对BeanDefinition处理
invokeBeanFactoryPostProcessors(beanFactory); // 6: 注册BeanPostProcessor后置处理器
registerBeanPostProcessors(beanFactory); // 7: 初始化一些消息源(比如处理国际化的i18n等消息源)
initMessageSource(); // 8: 初始化应用事件广播器
initApplicationEventMulticaster(); // 9: 初始化一些特殊的bean
onRefresh(); // 10: 注册一些监听器
registerListeners(); // 11: 实例化剩余的单例bean(非懒加载方式)
// 1)、bean的实例化(创建)
// 2)、bean的属性填充
// 3)、bean的初始化(实现InitializingBean接口的类,在bean标签中的init-method属性)
// 注意事项:Bean的IoC、DI和AOP都是发生在此步骤
finishBeanFactoryInitialization(beanFactory); // STEP 12: 完成刷新时,需要发布对应的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - "
+ "cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}

refresh方法是初始化Spring容器的核心代码,共分为12个步骤,具体功能如代码所示,将在后续文章中将对refresh中的步骤进行说明。

IOC容器的创建的更多相关文章

  1. Spring框架——IOC 容器的创建与使用

    企业级开发框架 Spring Framework 是整个 Spring 生态的基础,各个模块都是基于 Spring Framework 衍生出来的. Spring 的两大核心机制 IOC 控制翻转.A ...

  2. 02.Spring Ioc 容器 - 创建

    基本概念 Spring IoC 容器负责 Bean 创建.以及其生命周期的管理等.想要使用 IoC容器的前提是创建该容器. 创建 Spring IoC 容器大致有两种: 在应用程序中创建. 在 WEB ...

  3. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

    1.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模 ...

  4. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解(转)

    所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合 ...

  5. SPRING源码分析:IOC容器

    在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求: ...

  6. spring框架--IOC容器,依赖注入

    思考: 1. 对象创建创建能否写死? 2. 对象创建细节 对象数量 action  多个   [维护成员变量] service 一个   [不需要维护公共变量] dao     一个   [不需要维护 ...

  7. Spring(一)--作用、IOC容器细节、搭配环境、Spring实验

    1.Spring作用:      1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!]      2.将其他组件粘合在一起      比如将S ...

  8. 【深入spring】IoC容器的实现

    本文乃学习整理参考而来 IoC概述: 在spring中,IoC容器实现了依赖控制反转,它可以再对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入方法调用的依赖 ...

  9. Spring IOC容器交给application域对象管理

    在项目开发中,我们不能在每次使用IOC容器时,都创建一个ApplicationContext对象, 因此我们将IOC容器交给application域对象管理,application对象在服务器启动时创 ...

随机推荐

  1. Spring Boot Actuator 使用

    转载于:https://www.jianshu.com/p/af9738634a21 Spring Boot 的 Actuator 提供了很多生产级的特性,比如监控和度量Spring Boot 应用程 ...

  2. Python学习第一章

    1.Python保留字即是关键字,我们不可以把他们当作任何标识符名称. python的标准库提供了一个keyword模板,可以输出当前版本的关键字: >>>import keywor ...

  3. samba服务配置(一)

    samba是一个实现不同操作系统之间文件共享和打印机共享的一种SMB协议的免费软件. samba软件结构: /etc/samba/smb.conf    #samba服务的主要配置文件 /etc/sa ...

  4. phpMyAdmin 4.7.x CSRF 漏洞利用

    作者:Ambulong phpMyAdmin是个知名MySQL/MariaDB在线管理工具,phpMyAdmin团队在4.7.7版本中修复了一个危害严重的CSRF漏洞(PMASA-2017-9),攻击 ...

  5. nginx: [emerg] getpwnam("nginx") failed

    搭建LNMP环境的时候,在安装完Nginx后启动测试Nginx服务时发现报如下错误: nginx: [emerg] getpwnam("nginx") failed 这是由于没有创 ...

  6. DOMContentLoaded事件中使用异步

    概述 我在之前的博文(Performance面板看js加载)中提到过,如果利用监听DOMContentLoaded事件的方式来加载js是不能优化加载的,不能够替代jquery中的ready方法,原因是 ...

  7. 分布式控制系统Git学习

    git : n. 饭桶,无用的人 github : n. 社交编程及代码托管网站 hub: n. 中心:毂:木片 Git是一个分布式版本控制软件,最初由林纳斯·托瓦兹(Linus Torvalds)创 ...

  8. sql 语句的limit的用法

    SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset   mysql> SELECT * FROM table LIMIT ...

  9. Python函数——装饰器

    前言 给下面的函数加上运行时间 def fib(n): a, b = 0, 1 for i in range(n): print(b) a, b = b, a+b return b a = fib(5 ...

  10. 【sping揭秘】16、@After(finally) 但是这个实在afterturning之前执行

    package cn.cutter.start.bean; import org.apache.commons.logging.Log; import org.apache.commons.loggi ...