IOC容器的创建

一、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容器的流程总结如下:

sequenceDiagram
ContextLoaderListener->>ContextLoader:initWebApplicationContext()
ContextLoader->>ContextLoader:createWebApplicationContext()
ContextLoader->>ContextLoader:determineContextClass()
ContextLoader->>ContextLoader:configureAndRefreshWebApplicationContext()
ContextLoader->>AbstractApplicationContext:refresh()

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中的步骤进行说明。

原文地址:https://www.cnblogs.com/liuyi6/p/10317111.html

时间: 2024-11-05 20:11:17

IOC容器的创建的相关文章

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

.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合,并由此引申出IoC.DI以及Ioc容器等概念. 2.意图 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节

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

1.Spring作用:      1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!]      2.将其他组件粘合在一起      比如将SpringMVC和Mybaits连在一起      3.包括:IOC容器和AOP[面向切面编程]           Spring的IOC机制(控制反转和依赖注入)正是用在此处.           Spring的IOC(控制反转和依赖注入)                控制反转[IOC]:就是由

【深入spring】IoC容器的实现

本文乃学习整理参考而来 IoC概述: 在spring中,IoC容器实现了依赖控制反转,它可以再对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入方法调用的依赖,这种依赖注入是可以递归的,对象被逐层注入.对象之间的相互依赖关系由IoC容器管理,并由容器完成注入,如对象的创建.赋值等,这样很大程度简化开发. 结构图如下 1.1 BeanFactory 接口BeanFactory:体现了spring为提供给用户的IoC容器所设定的最基本功能规范. 接口Bean

Spring框架—— IOC容器和Bean的配置

 1 IOC和DI ①IOC(Inversion of Control):反转控制. 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率. 反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向--改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效

spring技术内幕读书笔记之IoC容器的学习

第一篇:概念和设计原理 IoC容器的实现作为Spring的核心内容之一非常有必要拿来研究一下 1.概念 IoC(Inversion of Control,控制反转)必须思考的问题:哪些方面的控制被反转了? 对于这个问题,Martin Flower给出结论是:依赖对象的获得被反转了.基于此,他为控制反转创造了一个更好的名字:依赖注入. SpringIoC模块是这一思想的一种实现,IoC容器把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,降低了组件之间的耦合度,利于功能复用,方便进行

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

所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合,并由此引申出IoC.DI以及Ioc容器等概念. 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象.即使

Spring源码分析之-加载IOC容器

本文接上一篇文章 SpringIOC 源码,控制反转前的处理(https://mp.weixin.qq.com/s/9RbVP2ZQVx9-vKngqndW1w) 继续进行下面的分析 首先贴出 Spring bean容器的刷新的核心 11个步骤进行祭拜(一定要让我学会了...阿门) // 完成IoC容器的创建及初始化工作 @Override public void refresh() throws BeansException, IllegalStateException { synchroni

【Spring源码--IOC容器的实现】(五)Bean对象的创建

前言: 1.第一次直接用CSDN原生的编辑器写博客,格式排版还有点陌生,见谅. 2.前面的文章我们写了IOC容器的启动,也就是把xml里面的配置都解析成Spring的内部结构BeanDefinition,并存储在Spring的容器中.下面我们将分析IOC容器室怎样对bean的依赖关系进行注入的.依赖注入的过程是第一次向IOC容器索要bean时触发的(例外:lazy-init实现bean的预实例化),也就是getBean方法. 3.依赖注入可以分为两个过程,一是bean所包含的Java对象的创建,

Spring框架笔记(十一)——IOC容器创建bean实例的第三种方法——FactoryBean

IOC容器创建bean实例有3类方法: 1 通过bean对应实例的全类名 2 通过工厂方法,包括静态工厂和实例工厂 3 实现 FactoryBean 接口在 Spring IOC 容器中配置 Bean 今天我们介绍这第三种方法--FactoryBean Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean. 工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法