赞
踩
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>
上下文监听器在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()); } }
ContextLoaderListener的contextInitialized(ServletContextEvent event)
这个上下文初始化方法是调用了父类ContextLoader
的initWebApplicationContext(event.getServletContext());
方法,很显然,这是对ApplicationContext的初始化方法,也就是到这里正是进入了springIOC的初始化。
initWebApplicationContext(event.getServletContext());
主要干了三件事情
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; } } }
// 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()); } }
// 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(); } } }
AbstractApplicationContext里面的obtainFreshBeanFactory方法,来进行刷新beanFactory方法
核心方法是loadBeanDefinitions方法,核心过程如下:
(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); } }
查看另外一篇博文:DispatchServlet的原理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。