Spring MVC之FrameworkServlet源码分析
FrameworkServlet是HttpServletBean的子类,实现了HttpServletBean 的 initServletBean 方法。
通过initServletBean()进行WebApplicationContext初始化,其源码为:
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 this.webApplicationContext = initWebApplicationContext(); 15 initFrameworkServlet(); 16 } 17 catch (ServletException ex) { 18 this.logger.error("Context initialization failed", ex); 19 throw ex; 20 } 21 catch (RuntimeException ex) { 22 this.logger.error("Context initialization failed", ex); 23 throw ex; 24 } 25 26 if (this.logger.isInfoEnabled()) { 27 long elapsedTime = System.currentTimeMillis() - startTime; 28 this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + 29 elapsedTime + " ms"); 30 } 31 }
其主要的功能就是调用initWebApplicationContext方法初始化一个WebApplicationContext。
以下是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 //1.取得根容器 12 WebApplicationContext rootContext = 13 WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 14 WebApplicationContext wac = null; 15 16 //2.取得 SpringMVC 容器 17 if (this.webApplicationContext != null) { 18 // A context instance was injected at construction time -> use it 19 wac = this.webApplicationContext; 20 if (wac instanceof ConfigurableWebApplicationContext) { 21 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 22 if (!cwac.isActive()) { 23 // The context has not yet been refreshed -> provide services such as 24 // setting the parent context, setting the application context id, etc 25 if (cwac.getParent() == null) { 26 // The context instance was injected without an explicit parent -> set 27 // the root application context (if any; may be null) as the parent 28 cwac.setParent(rootContext); 29 } 30 configureAndRefreshWebApplicationContext(cwac); 31 } 32 } 33 } 34 35 //3.寻找 SpringMVC 容器 36 if (wac == null) { 37 // No context instance was injected at construction time -> see if one 38 // has been registered in the servlet context. If one exists, it is assumed 39 // that the parent context (if any) has already been set and that the 40 // user has performed any initialization such as setting the context id 41 wac = findWebApplicationContext(); 42 } 43 44 // 4.创建 SpringMVC 容器 45 if (wac == null) { 46 // No context instance is defined for this servlet -> create a local one 47 wac = createWebApplicationContext(rootContext); 48 } 49 50 // 5.重刷新 SpringMVC 容器 51 if (!this.refreshEventReceived) { 52 // Either the context is not a ConfigurableApplicationContext with refresh 53 // support or the context injected at construction time had already been 54 // refreshed -> trigger initial onRefresh manually here. 55 onRefresh(wac); 56 } 57 58 // 6.发布 SpringMVC 容器 59 if (this.publishContext) { 60 // Publish the context as a servlet context attribute. 61 String attrName = getServletContextAttributeName(); 62 getServletContext().setAttribute(attrName, wac); 63 if (this.logger.isDebugEnabled()) { 64 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + 65 "' as ServletContext attribute with name [" + attrName + "]"); 66 } 67 } 68 69 return wac; 70 }
若在 Servlet、ServletContext 中均没有找到 SpringMVC 容器,说明它还没被创建。创建过程的源码为:
1 /** 2 * Instantiate the WebApplicationContext for this servlet, either a default 3 * {@link org.springframework.web.context.support.XmlWebApplicationContext} 4 * or a {@link #setContextClass custom context class}, if set. 5 * Delegates to #createWebApplicationContext(ApplicationContext). 6 * @param parent the parent WebApplicationContext to use, or {@code null} if none 7 * @return the WebApplicationContext for this servlet 8 * @see org.springframework.web.context.support.XmlWebApplicationContext 9 * @see #createWebApplicationContext(ApplicationContext) 10 */ 11 protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { 12 return createWebApplicationContext((ApplicationContext) parent); 13 } 14 15 16 17 /** 18 * Instantiate the WebApplicationContext for this servlet, either a default 19 * {@link org.springframework.web.context.support.XmlWebApplicationContext} 20 * or a {@link #setContextClass custom context class}, if set. 21 * <p>This implementation expects custom contexts to implement the 22 * {@link org.springframework.web.context.ConfigurableWebApplicationContext} 23 * interface. Can be overridden in subclasses. 24 * <p>Do not forget to register this servlet instance as application listener on the 25 * created context (for triggering its {@link #onRefresh callback}, and to call 26 * {@link org.springframework.context.ConfigurableApplicationContext#refresh()} 27 * before returning the context instance. 28 * @param parent the parent ApplicationContext to use, or {@code null} if none 29 * @return the WebApplicationContext for this servlet 30 * @see org.springframework.web.context.support.XmlWebApplicationContext 31 */ 32 protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { 33 Class<?> contextClass = getContextClass(); 34 if (this.logger.isDebugEnabled()) { 35 this.logger.debug("Servlet with name '" + getServletName() + 36 "' will try to create custom WebApplicationContext context of class '" + 37 contextClass.getName() + "'" + ", using parent context [" + parent + "]"); 38 } 39 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 40 throw new ApplicationContextException( 41 "Fatal initialization error in servlet with name '" + getServletName() + 42 "': custom WebApplicationContext class [" + contextClass.getName() + 43 "] is not of type ConfigurableWebApplicationContext"); 44 } 45 // 创建容器 46 ConfigurableWebApplicationContext wac = 47 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 48 49 // 初始化容器 50 wac.setEnvironment(getEnvironment()); 51 wac.setParent(parent); 52 wac.setConfigLocation(getContextConfigLocation()); 53 54 configureAndRefreshWebApplicationContext(wac); 55 56 return wac; 57 }
BeanUtils.instantiateClass 方法负责容器的真正创建过程,其源码为:
1 /** 2 * Instantiate a class using its no-arg constructor. 3 * <p>Note that this method tries to set the constructor accessible 4 * if given a non-accessible (that is, non-public) constructor. 5 * @param clazz class to instantiate 6 * @return the new instance 7 * @throws BeanInstantiationException if the bean cannot be instantiated 8 * @see Constructor#newInstance 9 */ 10 public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { 11 Assert.notNull(clazz, "Class must not be null"); 12 if (clazz.isInterface()) { 13 throw new BeanInstantiationException(clazz, "Specified class is an interface"); 14 } 15 try { 16 // 利用反射实例化对象,完成容器创建 17 return instantiateClass(clazz.getDeclaredConstructor()); 18 } 19 catch (NoSuchMethodException ex) { 20 throw new BeanInstantiationException(clazz, "No default constructor found", ex); 21 } 22 } 23 24 25 26 /** 27 * Convenience method to instantiate a class using the given constructor. 28 * <p>Note that this method tries to set the constructor accessible if given a 29 * non-accessible (that is, non-public) constructor. 30 * @param ctor the constructor to instantiate 31 * @param args the constructor arguments to apply 32 * @return the new instance 33 * @throws BeanInstantiationException if the bean cannot be instantiated 34 * @see Constructor#newInstance 35 */ 36 public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { 37 Assert.notNull(ctor, "Constructor must not be null"); 38 try { 39 // 设置访问权限为 public 40 ReflectionUtils.makeAccessible(ctor); 41 // 利用构造函数实例化对象 42 return ctor.newInstance(args); 43 } 44 catch (InstantiationException ex) { 45 throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); 46 } 47 catch (IllegalAccessException ex) { 48 throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex); 49 } 50 catch (IllegalArgumentException ex) { 51 throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex); 52 } 53 catch (InvocationTargetException ex) { 54 throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException()); 55 } 56 }
onRefresh 方法为空方法,由子类 DispatcherServlet 来实现。