Spring原理之Ioc启动流程

基于XML 文件的 Spring Ioc 的启动流程

IOC时序图

在这里插入图片描述

spring的启动是建筑在servlet容器之上的,所有web工程的初始位置就是web.xml,它配置了servlet的上下文(context)和监听器(Listener)

web.xml中一般需要配置前端控制器与上下文监听器,如下:

<!--dispatcherServlet的配置,这个servlet主要用于前端控制,这是springMVC的基础-->
    <servlet>
        <servlet-name>service_dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/services/service_dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

<!--spring资源上下文定义,在指定地址找到spring的xml配置文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/application_context.xml</param-value>
    </context-param>
<!--spring的上下文监听器-->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

1.1 spring的上下文监听器

上下文监听器在web.xml中的配置:

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/application_context.xml</param-value>
</context-param>

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

spring的启动其实就是IOC容器的启动过程,通过上述的第一段配置<context-param>是初始化上下文,然后通过后一段的的来加载配置文件,其中调用的spring包中的ContextLoaderListener这个上下文监听器,ContextLoaderListener是一个实现了ServletContextListener接口的监听器,他的父类是 ContextLoader,在启动项目时会触发contextInitialized上下文初始化方法。下面我们来看看这个方法:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    private ContextLoader contextLoader;

    public ContextLoaderListener() {}

    public ContextLoaderListener(WebApplicationContext context) super(context);
	// 上下文初始化,启动项目的时候触发
    public void contextInitialized(ServletContextEvent event) {
        this.contextLoader = this.createContextLoader();
        if (this.contextLoader == null) {
            this.contextLoader = this;
        }
		// 核心的初始化方法
        this.contextLoader.initWebApplicationContext(event.getServletContext());
    }
	//上下文销毁方法,关闭项目时触发
    public void contextDestroyed(ServletContextEvent event) {
        if (this.contextLoader != null) {
            this.contextLoader.closeWebApplicationContext(event.getServletContext());
        }
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}
1.1.1 初始化上下文方法解析

ContextLoaderListener的contextInitialized(ServletContextEvent event) 这个上下文初始化方法是调用了父类ContextLoaderinitWebApplicationContext(event.getServletContext());方法,很显然,这是对ApplicationContext的初始化方法,也就是到这里正是进入了springIOC的初始化。

initWebApplicationContext(event.getServletContext()); 主要干了三件事情

  • 创建WebApplicationContext。(核心方法是createWebApplicationContext)
  • 加载Spring配置文件,Bean相关操作。(核心方法是refresh)
  • 将WebApplicationContext放入ServletContext(Java Web的全局变量)中。(servletContext.setAttribute() )
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
    } else {
        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) {
                // 1、创建applicationContext
                this.context = this.createWebApplicationContext(servletContext);
            }
            // 2、如果这个类是ConfigurableWebApplicationContext或者其子类等相关类
            //如果是则configureAndRefreshWebApplicationContext方法就是加载对应的Spring配置文件中的Bean。
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        ApplicationContext parent = this.loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
		   //3、将WebApplicationContext放入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;
        } catch (RuntimeException var8) {
            logger.error("Context initialization failed", var8);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
            throw var8;
        } catch (Error var9) {
            logger.error("Context initialization failed", var9);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
            throw var9;
        }
    }
}
创建WebApplicationContext
  • 首先判断servletContext的具体类型contextClass
  • 然后判断该具体类型contextClass是否是ConfigurableWebApplicationContext同类型或者子类或者子接口
  • 根据实例化该contextClass并转型为ConfigurableWebApplicationContext返回
// 1、创建applicationContext
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    // determineContextClass主要是根据类加载器判断sc的具体类型
    Class<?> contextClass = this.determineContextClass(sc);
    // 这里isAssignableFrom主要是判断contextClass是否可以向上转型为ConfigurableWebApplicationContext
    // 也就是说ConfigurableWebApplicationContext是否是contextClass的同类型、或者超类、或者超接口
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    } else {
        return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
    }
}

// determineContextClass主要是根据类加载器判断sc的具体类型
protected Class<?> determineContextClass(ServletContext servletContext) {
    	// 判断是否指定contextClass的名称,则根据加载器去找该类
        String contextClassName = servletContext.getInitParameter("contextClass");
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {
            // 否则,该类名称为WebApplicationContext
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
}

//实例化该class
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
        Assert.notNull(clazz, "Class must not be null");
        if (clazz.isInterface()) {
            throw new BeanInstantiationException(clazz, "Specified class is an interface");
        } else {
            try {
                return instantiateClass(clazz.getDeclaredConstructor());
            } catch (NoSuchMethodException var2) {
                throw new BeanInstantiationException(clazz, "No default constructor found", var2);
            }
        }
}
  public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            ReflectionUtils.makeAccessible(ctor);
            return ctor.newInstance(args);
        } catch (InstantiationException var3) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", var3);
        } catch (IllegalAccessException var4) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", var4);
        } catch (IllegalArgumentException var5) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", var5);
        } catch (InvocationTargetException var6) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", var6.getTargetException());
        }
    }

加载Spring配置,Bean相关操作
  • 设置id值
  • 设置配置文件地址
  • 若有配置ApplicationContextInitializer相关的类,则调用ApplicationContextInitializer的initialize方法进行一些初始化的操作
  • 读取SpringMVC配置文件,解析bean、组装bean。wac是ConfigurableApplicationContext的类型,refresh方法由其子抽象类AbstractApplicationContext进行实现
// 2、加载对应的Spring配置文件中的Bean
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    String initParameter;
    // 判断是否自定义了该wac的id,如果自定义了,就设置
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        initParameter = sc.getInitParameter("contextId");
        if (initParameter != null) {
            wac.setId(initParameter);
        } else if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName()));
        } else {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }
    wac.setServletContext(sc);
    //contextConfigLocation是在web.xml文件中指定的配置文件的地址
    initParameter = sc.getInitParameter("contextConfigLocation");
    if (initParameter != null) {
        wac.setConfigLocation(initParameter);
    }
    // 如果有配置ApplicationContextInitializer相关的类,则调用ApplicationContextInitializer的initialize方法进行一些初始化的操作。
    this.customizeContext(sc, wac);
    
    // 这个方法就是读取SpringMVC配置文件,解析bean、组装bean等等一系列操作了
    wac.refresh();
}

//如果有配置ApplicationContextInitializer相关的类,则调用ApplicationContextInitializer的initialize方法进行一些初始化的操作。
 protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = this.determineContextInitializerClasses(servletContext);
        if (initializerClasses.size() != 0) {
            Class<?> contextClass = applicationContext.getClass();
            ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList();
            Iterator i$ = initializerClasses.iterator();
            while(i$.hasNext()) {
                Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass = (Class)i$.next();
                Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
                Assert.isAssignable(initializerContextClass, contextClass, String.format("Could not add context initializer [%s] as its generic parameter [%s] is not assignable from the type of application context used by this context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), contextClass.getName()));
                initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
            }

            ConfigurableEnvironment env = applicationContext.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment)env).initPropertySources(servletContext, (ServletConfig)null);
            }

            Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
            Iterator i$ = initializerInstances.iterator();

            while(i$.hasNext()) {
                ApplicationContextInitializer<ConfigurableApplicationContext> initializer = (ApplicationContextInitializer)i$.next();
                initializer.initialize(applicationContext);
            }

        }
}
//读取SpringMVC配置文件,解析bean、组装bean
public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        // 1.调用容器准备刷新的方法,获取当前时间和同步标识
        this.prepareRefresh();
        // 2.调用子类的refreshBeanFactory()方法,Bean定义资源文件的载入从这里启动,解析Bean
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        // 3.为beanFactory配置事件处理器、类加载器等
        this.prepareBeanFactory(beanFactory);

        try {
            // 4.为容器的某些子类指定特殊的Post事件处理器
            this.postProcessBeanFactory(beanFactory);
            // 5.调用所有注册的BeanFactoryPostProcesser的Bean
            this.invokeBeanFactoryPostProcessors(beanFactory);
            // 6.为BeanFactory注册Post事件处理器
            this.registerBeanPostProcessors(beanFactory);
            // 7.国际化初始化
            this.initMessageSource();
            // 8.初始化事件传播器
            this.initApplicationEventMulticaster();
            // 9.调用子类某些特殊的Bean初始化方法
            this.onRefresh();
            // 10.为上面的事件传播器注册事件监听器
            this.registerListeners();
            // 11.初始化所有单例Bean
            this.finishBeanFactoryInitialization(beanFactory);
            // 12.完成初始化
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }
            // 销毁已创建的Bean
            this.destroyBeans();
            // 取消刷新并重置标识
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }
    }
}
1.1.2 Spring 容器对Bean配置资源的载入是从refresh方法开始的

AbstractApplicationContext里面的obtainFreshBeanFactory方法,来进行刷新beanFactory方法

核心方法是loadBeanDefinitions方法,核心过程如下:

  • 通过ResourceLoader 来完成资源文件的定位,也就是说Bean定义文件时通过抽象成Resource来被Ioc容器处理
  • 容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,Bean的定义信息由BeanDefinition来表示
  • 注册过程就是维护一个HashMap保存得到的这个BeanDefinition

(1)obtainFreshBeanFactory:加载bean的资源

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    this.refreshBeanFactory();
    return this.getBeanFactory();
}

// 刷新beanFactory
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

这个方法是由其子类AbstractRefreshableApplicationContext进行实现,核心的方法是loadBeanDefinitions() 方法

protected final void refreshBeanFactory() throws BeansException {
    if (this.hasBeanFactory()) {
        this.destroyBeans();
        this.closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = this.createBeanFactory();
        beanFactory.setSerializationId(this.getId());
        this.customizeBeanFactory(beanFactory);
        // 核心方法
        this.loadBeanDefinitions(beanFactory);
        synchronized(this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    } catch (IOException var5) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
    }
}

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory var1) throws BeansException, IOException;

loadBeanDefinitions由AbstractRefreshableApplicationContext的子类AbstractXmlApplicationContext进行实现

// 实现父类抽象的载入Bean定义方法
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 创建XmlBeanDefinitionReader,创建Bean读取器
    // 并通过回调设置到容器中,容器使用该读取器读取Bean 的配置资源
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// 为Bean读取器设置Spring资源加载器
    // AbstractXmlApplicationContext的祖先父类AbstractApplicationContext继承DefaultResourceLoader
    // 因此容器本身也是一个资源加载器
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    // 为Bean读取器设置SAX xml解析器
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    // 当Bean读取器读取Bean定义的xml资源文件时,启用xml的校验机制
    this.initBeanDefinitionReader(beanDefinitionReader);
    // Bean读取器真正实现加载的方法
    this.loadBeanDefinitions(beanDefinitionReader);
}

protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
}
// xml Bean读取器加载Bean配置资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    	// 获取Bean配置资源的定位
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            // xml Bean 读取器调用其父类 XmlBeanDefinitionReader 读取定位的Bean配置资源
            reader.loadBeanDefinitions(configResources);
        }
		// 如果子类中获取的Bean资源定位为空
        // 则获取ClassPathXmlApplicationContext 构造方法中setConfigLocations方法设置的资源
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            // xml Bean 读取器调用其父类 XmlBeanDefinitionReader 读取定位的Bean配置资源
            reader.loadBeanDefinitions(configLocations);
        }

}

1.2 DispatchServlet的加载

查看另外一篇博文:DispatchServlet的原理

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值