通过上篇文章(Spring IOC容器初体验(一)),我们可以对 Spring 框架体系有了一个基本的宏观了解。这篇文章开始体验IOC。
我们从大家最熟悉的DespatcherServlet开始,DespatcherServlet的父类HttpServletBean中有一个init()方法,如下:
1 /** 2 * Map config parameters onto bean properties of this servlet, and 3 * invoke subclass initialization. 4 * @throws ServletException if bean properties are invalid (or required 5 * properties are missing), or if subclass initialization fails. 6 */ 7 @Override 8 public final void init() throws ServletException { 9 if (logger.isDebugEnabled()) { 10 logger.debug("Initializing servlet ‘" + getServletName() + "‘"); 11 } 12 13 // Set bean properties from init parameters. 14 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 15 if (!pvs.isEmpty()) { 16 try { 17 //定位资源 18 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); 19 //加载配置信息 20 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); 21 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); 22 initBeanWrapper(bw); 23 bw.setPropertyValues(pvs, true); 24 } 25 catch (BeansException ex) { 26 if (logger.isErrorEnabled()) { 27 logger.error("Failed to set bean properties on servlet ‘" + getServletName() + "‘", ex); 28 } 29 throw ex; 30 } 31 } 32 33 // Let subclasses do whatever initialization they like. 34 initServletBean(); 35 36 if (logger.isDebugEnabled()) { 37 logger.debug("Servlet ‘" + getServletName() + "‘ configured successfully"); 38 } 39 }
在init方法中,真正完成初始化容器动作的逻辑其实是在initServletBean()方法中,我们继续跟进initServletBean()中的代码在FrameworkServlet类中:
1 /** 2 * Overridden method of {@link HttpServletBean}, invoked after any bean properties 3 * have been set. Creates this servlet‘s WebApplicationContext. 4 */ 5 @Override 6 protected final void initServletBean() throws ServletException { 7 getServletContext().log("Initializing Spring FrameworkServlet ‘" + getServletName() + "‘"); 8 if (this.logger.isInfoEnabled()) { 9 this.logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization started"); 10 } 11 long startTime = System.currentTimeMillis(); 12 13 try { 14 15 this.webApplicationContext = initWebApplicationContext(); 16 initFrameworkServlet(); 17 } 18 catch (ServletException ex) { 19 this.logger.error("Context initialization failed", ex); 20 throw ex; 21 } 22 catch (RuntimeException ex) { 23 this.logger.error("Context initialization failed", ex); 24 throw ex; 25 } 26 27 if (this.logger.isInfoEnabled()) { 28 long elapsedTime = System.currentTimeMillis() - startTime; 29 this.logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization completed in " + 30 elapsedTime + " ms"); 31 } 32 }
在上面的代码中看到了initWebApplicationContext(),继续跟进:
1 /** 2 * Initialize and publish the WebApplicationContext for this servlet. 3 * <p>Delegates to {@link #createWebApplicationContext} for actual creation 4 * of the context. Can be overridden in subclasses. 5 * @return the WebApplicationContext instance 6 * @see #FrameworkServlet(WebApplicationContext) 7 * @see #setContextClass 8 * @see #setContextConfigLocation 9 */ 10 protected WebApplicationContext initWebApplicationContext() { 11 12 //先从ServletContext中获得父容器WebAppliationContext 13 WebApplicationContext rootContext = 14 WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 15 //声明子容器 16 WebApplicationContext wac = null; 17 18 //建立父、子容器之间的关联关系 19 if (this.webApplicationContext != null) { 20 // A context instance was injected at construction time -> use it 21 wac = this.webApplicationContext; 22 if (wac instanceof ConfigurableWebApplicationContext) { 23 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 24 if (!cwac.isActive()) { 25 // The context has not yet been refreshed -> provide services such as 26 // setting the parent context, setting the application context id, etc 27 if (cwac.getParent() == null) { 28 // The context instance was injected without an explicit parent -> set 29 // the root application context (if any; may be null) as the parent 30 cwac.setParent(rootContext); 31 } 32 //这个方法里面调用了AbatractApplication的refresh()方法 33 //模板方法,规定IOC初始化基本流程 34 configureAndRefreshWebApplicationContext(cwac); 35 } 36 } 37 } 38 //先去ServletContext中查找Web容器的引用是否存在,并创建好默认的空IOC容器 39 if (wac == null) { 40 // No context instance was injected at construction time -> see if one 41 // has been registered in the servlet context. If one exists, it is assumed 42 // that the parent context (if any) has already been set and that the 43 // user has performed any initialization such as setting the context id 44 wac = findWebApplicationContext(); 45 } 46 //给上一步创建好的IOC容器赋值 47 if (wac == null) { 48 // No context instance is defined for this servlet -> create a local one 49 wac = createWebApplicationContext(rootContext); 50 } 51 52 //触发onRefresh方法 53 if (!this.refreshEventReceived) { 54 // Either the context is not a ConfigurableApplicationContext with refresh 55 // support or the context injected at construction time had already been 56 // refreshed -> trigger initial onRefresh manually here. 57 onRefresh(wac); 58 } 59 60 if (this.publishContext) { 61 // Publish the context as a servlet context attribute. 62 String attrName = getServletContextAttributeName(); 63 getServletContext().setAttribute(attrName, wac); 64 if (this.logger.isDebugEnabled()) { 65 this.logger.debug("Published WebApplicationContext of servlet ‘" + getServletName() + 66 "‘ as ServletContext attribute with name [" + attrName + "]"); 67 } 68 } 69 70 return wac; 71 } 72 73 /** 74 * Retrieve a {@code WebApplicationContext} from the {@code ServletContext} 75 * attribute with the {@link #setContextAttribute configured name}. The 76 * {@code WebApplicationContext} must have already been loaded and stored in the 77 * {@code ServletContext} before this servlet gets initialized (or invoked). 78 * <p>Subclasses may override this method to provide a different 79 * {@code WebApplicationContext} retrieval strategy. 80 * @return the WebApplicationContext for this servlet, or {@code null} if not found 81 * @see #getContextAttribute() 82 */ 83 @Nullable 84 protected WebApplicationContext findWebApplicationContext() { 85 String attrName = getContextAttribute(); 86 if (attrName == null) { 87 return null; 88 } 89 WebApplicationContext wac = 90 WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); 91 if (wac == null) { 92 throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); 93 } 94 return wac; 95 } 96 97 /** 98 * Instantiate the WebApplicationContext for this servlet, either a default 99 * {@link org.springframework.web.context.support.XmlWebApplicationContext} 100 * or a {@link #setContextClass custom context class}, if set. 101 * <p>This implementation expects custom contexts to implement the 102 * {@link org.springframework.web.context.ConfigurableWebApplicationContext} 103 * interface. Can be overridden in subclasses. 104 * <p>Do not forget to register this servlet instance as application listener on the 105 * created context (for triggering its {@link #onRefresh callback}, and to call 106 * {@link org.springframework.context.ConfigurableApplicationContext#refresh()} 107 * before returning the context instance. 108 * @param parent the parent ApplicationContext to use, or {@code null} if none 109 * @return the WebApplicationContext for this servlet 110 * @see org.springframework.web.context.support.XmlWebApplicationContext 111 */ 112 protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { 113 Class<?> contextClass = getContextClass(); 114 if (this.logger.isDebugEnabled()) { 115 this.logger.debug("Servlet with name ‘" + getServletName() + 116 "‘ will try to create custom WebApplicationContext context of class ‘" + 117 contextClass.getName() + "‘" + ", using parent context [" + parent + "]"); 118 } 119 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 120 throw new ApplicationContextException( 121 "Fatal initialization error in servlet with name ‘" + getServletName() + 122 "‘: custom WebApplicationContext class [" + contextClass.getName() + 123 "] is not of type ConfigurableWebApplicationContext"); 124 } 125 ConfigurableWebApplicationContext wac = 126 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 127 128 wac.setEnvironment(getEnvironment()); 129 wac.setParent(parent); 130 String configLocation = getContextConfigLocation(); 131 if (configLocation != null) { 132 wac.setConfigLocation(configLocation); 133 } 134 configureAndRefreshWebApplicationContext(wac); 135 136 return wac; 137 } 138 139 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 140 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { 141 // The application context id is still set to its original default value 142 // -> assign a more useful id based on available information 143 if (this.contextId != null) { 144 wac.setId(this.contextId); 145 } 146 else { 147 // Generate default id... 148 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 149 ObjectUtils.getDisplayString(getServletContext().getContextPath()) + ‘/‘ + getServletName()); 150 } 151 } 152 153 wac.setServletContext(getServletContext()); 154 wac.setServletConfig(getServletConfig()); 155 wac.setNamespace(getNamespace()); 156 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); 157 158 // The wac environment‘s #initPropertySources will be called in any case when the context 159 // is refreshed; do it eagerly here to ensure servlet property sources are in place for 160 // use in any post-processing or initialization that occurs below prior to #refresh 161 ConfigurableEnvironment env = wac.getEnvironment(); 162 if (env instanceof ConfigurableWebEnvironment) { 163 ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); 164 } 165 166 postProcessWebApplicationContext(wac); 167 applyInitializers(wac); 168 wac.refresh(); 169 }
从上面的代码可以看出,在configAndRefreshWebApplicationContext()方法中,调用了refresh()方法,这个是真正启动IOC容器的入口,后面会详细介绍。
IOC容器初始化以后,最后调用了DispatcherServlet的onRefresh()方法,在onRefresh()方法中,又直接调用了initStrategies()方法初始化SpringMVC的九大组件:
1 /** 2 * This implementation calls {@link #initStrategies}. 3 */ 4 @Override 5 protected void onRefresh(ApplicationContext context) { 6 initStrategies(context); 7 } 8 9 /** 10 * Initialize the strategy objects that this servlet uses. 11 * <p>May be overridden in subclasses in order to initialize further strategy objects. 12 */ 13 //初始化策略 14 protected void initStrategies(ApplicationContext context) { 15 //多文件上传的组件 16 initMultipartResolver(context); 17 //初始化本地语言环境 18 initLocaleResolver(context); 19 //初始化模板处理器 20 initThemeResolver(context); 21 //handlerMapping 22 initHandlerMappings(context); 23 //初始化参数适配器 24 initHandlerAdapters(context); 25 //初始化异常拦截器 26 initHandlerExceptionResolvers(context); 27 //初始化视图预处理器 28 initRequestToViewNameTranslator(context); 29 //初始化视图转换器 30 initViewResolvers(context); 31 // 32 initFlashMapManager(context); 33 }
原文:https://www.cnblogs.com/yybinger/p/11218802.html