一、IOC容器创建方式

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



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

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

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

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

二、IOC容器创建入口

1、Java中IOC容器创建

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

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

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

  1. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
  2. super(parent);
  3. setConfigLocations(configLocations);
  4. if (refresh) {
  5. //容器的创建与初始化
  6. refresh();
  7. }
  8. }

2、Web中IOC容器创建

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

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

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

  1. //初始化
  2. @Override
  3. public void contextInitialized(ServletContextEvent event) {
  4. initWebApplicationContext(event.getServletContext());
  5. }
  6. //销毁
  7. @Override
  8. public void contextDestroyed(ServletContextEvent event) {
  9. closeWebApplicationContext(event.getServletContext());
  10. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  11. }

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

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  2. //判断ServletContext是否已经存在WebApplication,如果存在则抛出异常
  3. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  4. //抛出异常...
  5. }
  6. Log logger = LogFactory.getLog(ContextLoader.class);
  7. servletContext.log("Initializing Spring root WebApplicationContext");
  8. if (logger.isInfoEnabled()) {
  9. logger.info("Root WebApplicationContext: initialization started");
  10. }
  11. long startTime = System.currentTimeMillis();
  12. try {
  13. if (this.context == null) {
  14. //创建WebApplicationContext
  15. this.context = createWebApplicationContext(servletContext);
  16. }
  17. if (this.context instanceof ConfigurableWebApplicationContext) {
  18. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  19. if (!cwac.isActive()) {
  20. if (cwac.getParent() == null) {
  21. //得到根上下文的父上下文,然后设置到根上下文,一般的web项目parent为空
  22. ApplicationContext parent = loadParentContext(servletContext);
  23. cwac.setParent(parent);
  24. }
  25. //从web.xml加载参数,初始化根上下文WebApplicationContext,创建bean工厂和bean对象
  26. configureAndRefreshWebApplicationContext(cwac, servletContext);
  27. }
  28. }
  29. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  30. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  31. if (ccl == ContextLoader.class.getClassLoader()) {
  32. currentContext = this.context;
  33. }
  34. else if (ccl != null) {
  35. currentContextPerThread.put(ccl, this.context);
  36. }
  37. if (logger.isDebugEnabled()) {
  38. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  39. }
  40. if (logger.isInfoEnabled()) {
  41. long elapsedTime = System.currentTimeMillis() - startTime;
  42. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  43. }
  44. return this.context;
  45. }
  46. //异常处理...
  47. }

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

2.1、创建上下文

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

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

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

  1. protected Class<?> determineContextClass(ServletContext servletContext) {
  2. //从web.xml获得参数contextClass,在一般的web项目中,此参数为null
  3. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
  4. if (contextClassName != null) {
  5. try {
  6. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
  7. }
  8. catch (ClassNotFoundException ex) {
  9. throw new ApplicationContextException(
  10. "Failed to load custom context class [" + contextClassName + "]", ex);
  11. }
  12. }
  13. else {
  14. //获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,在CotnextLoader类开头static语句块中初始化
  15. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
  16. try {
  17. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  18. }
  19. catch (ClassNotFoundException ex) {
  20. throw new ApplicationContextException(
  21. "Failed to load default context class [" + contextClassName + "]", ex);
  22. }
  23. }
  24. }

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

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

2.2、初始化根上下文

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

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  2. //设置应用程序上下文Id
  3. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  4. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
  5. if (idParam != null) {
  6. wac.setId(idParam);
  7. }
  8. else {
  9. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  10. ObjectUtils.getDisplayString(sc.getContextPath()));
  11. }
  12. }
  13. wac.setServletContext(sc);
  14. String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
  15. if (configLocationParam != null) {
  16. wac.setConfigLocation(configLocationParam);
  17. }
  18. //获取环境中配置的属性
  19. ConfigurableEnvironment env = wac.getEnvironment();
  20. if (env instanceof ConfigurableWebEnvironment) {
  21. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
  22. }
  23. customizeContext(sc, wac);
  24. //容器的创建与初始化
  25. wac.refresh();
  26. }

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

3、refresh方法

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

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

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. 【论文学习】YOLO9000: Better,Faster,Stronger(YOLO9000:更好,更快,更强)

    原文下载:https://arxiv.org/pdf/1612.08242v1.pdf 工程代码:http://pjreddie.com/darknet/yolo/ 目录 目录 摘要 简介 BETTE ...

  2. 详解Android中的四大组件之一:Activity详解

    activity的生命周期 activity的四种状态 running:正在运行,处于活动状态,用户可以点击屏幕,是将activity处于栈顶的状态. paused:暂停,处于失去焦点的时候,处于pa ...

  3. 第六节:详细讲解Java中的装箱与拆箱及其字符串

    前言 大家好,给大家带来详细讲解Java中的装箱与拆箱及其字符串的概述,希望你们喜欢 装箱与拆箱 封装类有:Byte , short , Integer , Character , long , Fl ...

  4. 夜神模拟已开启,adb命令检测不了设备解决方法

    日常APP测试中,很难拥有多种机型和各种安卓版本的手机,此时可以借助模拟器. 命令返回结果只有 “List of devices attached”,即代表检测不了模拟器 最近在使用夜神模拟器的时候, ...

  5. pythonic(fork)

    转载  https://wuzhiwei.net/be_pythonic/

  6. Java并发编程笔记之StampedLock锁源码探究

    StampedLock是JUC并发包里面JDK1.8版本新增的一个锁,该锁提供了三种模式的读写控制,当调用获取锁的系列函数的时候,会返回一个long 型的变量,该变量被称为戳记(stamp),这个戳记 ...

  7. Android 监听 ScrollView 滑动到最底部。

    做产品时,有一个需求,需要监听ScrollView滑动到最底部.在网上找了些方法,都有这样或那样的问题,要不就是监听不精确, 要不就是重复监听,那些代码没有产品化,很不可靠. 经过自己试验,终于找到了 ...

  8. docker学习系列(二):使用Dockerfile创建自己的镜像

    dockerfile可以允许我们自己创建镜像,通过编写里面的下载软件命令,执行docker build 即可生成镜像文件. 初尝dockerfile 新建一个目录test,然后进入这个目录,创建一个名 ...

  9. C语言第十一讲,预处理命令.

    C语言第十一讲,预处理命令. 一丶预处理简介 什么是预处理,预处理就是预先处理. 我们知道,程序会经过编译,连接形成可执行文件 这些在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理.提前 ...

  10. php,vue,vue-ssr 做出来的页面有什么区别?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由shirishiyue发表于云+社区专栏 目前我这边的web页面,都是采用php+smarty模板生成的,是一种比较早期的开发模式.好 ...