SpringMVC源码分析--容器初始化(四)FrameworkServlet
在上一篇博客SpringMVC源码分析--容器初始化(三)HttpServletBean我们介绍了HttpServletBean的init函数,其主要作用是初始化了一下SpringMVC配置文件的地址contextConfigLocation的配置属性,然后其调用的子类FrameworkServlet的initServletBean方法。
其实FrameworkServlet是springMVC初始化IOC容器的核心,通过读取配置的contextConfigLocation配置的springMVC配置文件的地址来读取各种初始化信息和Bean注入信息,来完成springMVC IOC 容器的初始化。接下来我们通过源码一步一步进行分析。
首先我们看initServletBean函数方法,其主要的操作就是this.webApplicationContext = initWebApplicationContext()完成webApplicationContext的初始化,webApplicationContext 即为springMVC的IOC容器,其实现类是XMLWebApplicationContext,
这样我们发现,初始化工作在initWebApplicationContext函数方法中。
//创建springMVC的IOC容器 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //创建springMVC的IOC容器 this.webApplicationContext = initWebApplicationContext(); //没有任何具体实现 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
initWebApplicationContext函数方法是整个springMVC IOC实现的实现过程,函数代码段如下,
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext())是从ServletContext中获取IOC容器,因为我们知道spring web在初始化时会将初始化的webApplicationContex保存到ServletContext中,这样我们就可以获取到spring web容器了,并且把它作为父容器。
if (this.webApplicationContext != null) 当容器已经存在时,我们就不需要重新创建,只需要刷新一下,将springMVC配置文件中的属性加载到webApplicationContext中就可以了,当不存在时我们就需要一步一步的创建webApplicationContext了。
wac = findWebApplicationContext()在ServletContext中查找WebApplicationContext,说不定它已经被存放到ServletContext中去了。
如果没有我们就需要创建wac = createWebApplicationContext(rootContext),这个步骤我们会接下来分析,创建完之后我们调用onRefresh(wac),这个方法在子类DispatcherServlet中实现,去初始化一些其他操作,现在已经完成了webApplicationContext的初始化工作了接下来就需要把webApplicationContext存放到ServletContext中去了getServletContext().setAttribute(attrName,
wac),最后调用return wac完成springMVC IOC容器的初始化。
/** *初始化和发布web应用上下文,封装了建立Spring容器上下文的整个过程 * */ protected WebApplicationContext initWebApplicationContext() { //获取由ContextLoaderListener初始化并注册在ServletContext中的根上下文,一般是Spring的IOC容器记为rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //如果webApplicationContext已经不为空,表示这个Servlet类是通过编程式注册到容器中的(Servlet 3.0 +中的ServletContext.addServlet()), //上下文也由编程式传入。若这个传入的上下文还没有被初始化,将rootContext上下文设置为它的父上下文,然后将其初始化,否则直接使用。 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } //判断wac是否为空判断是否已经完成上面的上下文的设置 if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id //如果为空,说明该Servlet不是由编程式注册到容器中的,在ServletContext中查找上下文,查找得到 //说明上下文已经以别的方式初始化并注册在contextAttribute下,直接使用 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one //此时还为空,调用以下方法创建一个全新的以rootContext为父上下文的上下文,作为SpringMVC配置元素的上下文 //大多数情况下我们所使用的上下文就是这个新建的上下文 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. //在DispatcherServlet中复写,会初始化一系列属性 onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. //将这个上下文发布到ServletContext中,也就是将上下文以一个和Servlet类在web.xml中注册名字有关的值为键 //设置为ServletContext的一个属性 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
上面我们介绍了springMVC IOC容器的初始化操作,但很多具体操作是在其他函数中完成的,接下来我们一一分析,
findWebApplicationContext函数是从ServletContext中查找webApplicationContext,springMVC初始化完成之后我们会看到它会将webApplicationContext保存到ServletContext中,这样我们可以在ServletContext中获取到它。
//从当前容器中查找SpringMVC的IOC容器 protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); if (attrName == null) { return null; } WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }
在上面介绍中我们并没有真正介绍到webApplicationContext初始化的完整过程,其完整实现是在createWebApplicationContext函数中,其首先会实例化一个IOC容器,这只是一个空容器,并没有相关属性wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
接下来的操作就是对wac设置相关属性
wac.setEnvironment(getEnvironment())设置环境
wac.setParent(parent)设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起
wac.setConfigLocation(getContextConfigLocation())设置web.xml中的springMVC的配置文件,各种bean的注入
configureAndRefreshWebApplicationContext(wac),这个函数是关键,是解析springMVC配置文件完成初始化的过程。
return wac返回已经初始化的webApplicationContext
//创建springMVC的IOC容器, protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { //获取类XMLWebApplicationContext Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } //实例化一个IOC容器 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //设置IOC容器相关的属性,这样springMVC的IOC容器就创建好了 wac.setEnvironment(getEnvironment()); //设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起 wac.setParent(parent); //设置web.xml中的springMVC的配置文件,各种bean的注入 wac.setConfigLocation(getContextConfigLocation()); //初始化springMVC各种的相关配置,各种注入的Controller,配置文件等 configureAndRefreshWebApplicationContext(wac); return wac; }
上面我们介绍到configureAndRefreshWebApplicationContext函数是完成springMVC配置文件初始化过程的函数,其实它也是比较简单的,也是设置springMVC IOC容器的相关属性,
最终调用wac.refresh(),通过AbstractApplicationContext来完成对springMVC xml配置文件的解析初始化操作,这边我们就不再深入探究,在spring IOC容器初始化时我们再详细介绍。
/*配置或者刷新应用上下文*/ protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName()); } } //设置springMVC IOC容器的相关属性 wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); //初始化springMVC各种的相关配置,各种注入的Controller,配置文件等 wac.refresh(); }
通过以上函数的操作就完成了springMVC IOC容器的初始化操作,并且把spring web初始化的IOC容器作为父容器,这样springMVC就可以获得父容器中注入的各种Bean,同时初始化的IOC容器webApplicationContext也会被存放到ServletContex中,这样在整个的web容器中都可以得到并使用IOC容器中的各种属性。
FrameworkServlet完整源码解析:
//FrameworkServlet类的设计目的就是用来建立一个和Servlet关联的Spring容器上下文 //并将其注册到ServletContext中,跳脱开SpringMVC体系,我们也能通过继承FrameworkServlet类 //得到与Spring容器整合的好处,FrameworkServlet和HttpServletBean一样,是一个可以独立使用的类 @SuppressWarnings("serial") public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { //servlet命名空间前缀 public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet"; //FrameworkServlet类的上下文 public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; private static final String INIT_PARAM_DELIMITERS = ",; \t\n"; private static final boolean responseGetStatusAvailable = ClassUtils.hasMethod(HttpServletResponse.class, "getStatus"); private String contextAttribute; private Class<?> contextClass = DEFAULT_CONTEXT_CLASS; private String contextId; private String namespace; // web.xml中配置的contextConfigLocation属性,就是springMVC的配置文件 private String contextConfigLocation; /** Actual ApplicationContextInitializer instances to apply to the context */ private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); /** Comma-delimited ApplicationContextInitializer class names set through init param */ private String contextInitializerClasses; /** Should we publish the context as a ServletContext attribute? */ private boolean publishContext = true; /** Should we publish a ServletRequestHandledEvent at the end of each request? */ private boolean publishEvents = true; /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ private boolean threadContextInheritable = false; /** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */ private boolean dispatchOptionsRequest = false; /** Should we dispatch an HTTP TRACE request to {@link #doService}? */ private boolean dispatchTraceRequest = false; /** WebApplicationContext for this servlet */ private WebApplicationContext webApplicationContext; /** If the WebApplicationContext was injected via {@link #setApplicationContext} */ private boolean webApplicationContextInjected = false; /** Flag used to detect whether onRefresh has already been called */ private boolean refreshEventReceived = false; public FrameworkServlet() { } public FrameworkServlet(WebApplicationContext webApplicationContext) { this.webApplicationContext = webApplicationContext; } public void setContextAttribute(String contextAttribute) { this.contextAttribute = contextAttribute; } public String getContextAttribute() { return this.contextAttribute; } public void setContextClass(Class<?> contextClass) { this.contextClass = contextClass; } public Class<?> getContextClass() { return this.contextClass; } public void setContextId(String contextId) { this.contextId = contextId; } public String getContextId() { return this.contextId; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getNamespace() { return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX); } //web.xml中配置文件的地址 public void setContextConfigLocation(String contextConfigLocation) { this.contextConfigLocation = contextConfigLocation; } public String getContextConfigLocation() { return this.contextConfigLocation; } @SuppressWarnings("unchecked") public void setContextInitializers(ApplicationContextInitializer<?>... initializers) { if (initializers != null) { for (ApplicationContextInitializer<?> initializer : initializers) { this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer); } } } public void setContextInitializerClasses(String contextInitializerClasses) { this.contextInitializerClasses = contextInitializerClasses; } public void setPublishContext(boolean publishContext) { this.publishContext = publishContext; } public void setPublishEvents(boolean publishEvents) { this.publishEvents = publishEvents; } public void setThreadContextInheritable(boolean threadContextInheritable) { this.threadContextInheritable = threadContextInheritable; } public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) { this.dispatchOptionsRequest = dispatchOptionsRequest; } public void setDispatchTraceRequest(boolean dispatchTraceRequest) { this.dispatchTraceRequest = dispatchTraceRequest; } @Override public void setApplicationContext(ApplicationContext applicationContext) { if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) { this.webApplicationContext = (WebApplicationContext) applicationContext; this.webApplicationContextInjected = true; } } //创建springMVC的IOC容器 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //创建springMVC的IOC容器 this.webApplicationContext = initWebApplicationContext(); //没有任何具体实现 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } /** *初始化和发布web应用上下文,封装了建立Spring容器上下文的整个过程 * */ protected WebApplicationContext initWebApplicationContext() { //获取由ContextLoaderListener初始化并注册在ServletContext中的根上下文,一般是Spring的IOC容器记为rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //如果webApplicationContext已经不为空,表示这个Servlet类是通过编程式注册到容器中的(Servlet 3.0 +中的ServletContext.addServlet()), //上下文也由编程式传入。若这个传入的上下文还没有被初始化,将rootContext上下文设置为它的父上下文,然后将其初始化,否则直接使用。 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } //判断wac是否为空判断是否已经完成上面的上下文的设置 if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id //如果为空,说明该Servlet不是由编程式注册到容器中的,在ServletContext中查找上下文,查找得到 //说明上下文已经以别的方式初始化并注册在contextAttribute下,直接使用 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one //此时还为空,调用以下方法创建一个全新的以rootContext为父上下文的上下文,作为SpringMVC配置元素的上下文 //大多数情况下我们所使用的上下文就是这个新建的上下文 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. //在DispatcherServlet中复写,会初始化一系列属性 onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. //将这个上下文发布到ServletContext中,也就是将上下文以一个和Servlet类在web.xml中注册名字有关的值为键 //设置为ServletContext的一个属性 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; } //从当前容器中查找SpringMVC的IOC容器 protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); if (attrName == null) { return null; } WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; } //创建springMVC的IOC容器, protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { //获取类XMLWebApplicationContext Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } //实例化一个IOC容器 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //设置IOC容器相关的属性,这样springMVC的IOC容器就创建好了 wac.setEnvironment(getEnvironment()); //设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起 wac.setParent(parent); //设置web.xml中的springMVC的配置文件,各种bean的注入 wac.setConfigLocation(getContextConfigLocation()); //初始化springMVC各种的相关配置,各种注入的Controller,配置文件等 configureAndRefreshWebApplicationContext(wac); return wac; } /*配置或者刷新应用上下文*/ protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName()); } } //设置springMVC IOC容器的相关属性 wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); //初始化springMVC各种的相关配置,各种注入的Controller,配置文件等 wac.refresh(); } protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) { } protected void applyInitializers(ConfigurableApplicationContext wac) { String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM); if (globalClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { this.contextInitializers.add(loadInitializer(className, wac)); } } if (this.contextInitializerClasses != null) { for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) { this.contextInitializers.add(loadInitializer(className, wac)); } } AnnotationAwareOrderComparator.sort(this.contextInitializers); for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { initializer.initialize(wac); } } @SuppressWarnings("unchecked") private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer( String className, ConfigurableApplicationContext wac) { try { Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader()); Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); if (initializerContextClass != null) { Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( "Could not add context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); } catch (Exception ex) { throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " + "via 'contextInitializerClasses' init-param", className), ex); } } public String getServletContextAttributeName() { return SERVLET_CONTEXT_PREFIX + getServletName(); } /** * Return this servlet's WebApplicationContext. */ public final WebApplicationContext getWebApplicationContext() { return this.webApplicationContext; } protected void initFrameworkServlet() throws ServletException { } public void refresh() { WebApplicationContext wac = getWebApplicationContext(); if (!(wac instanceof ConfigurableApplicationContext)) { throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac); } ((ConfigurableApplicationContext) wac).refresh(); } //WebApplicationContext刷新通知 public void onApplicationEvent(ContextRefreshedEvent event) { this.refreshEventReceived = true; onRefresh(event.getApplicationContext()); } protected void onRefresh(ApplicationContext context) { // For subclasses: do nothing by default. } /** * Close the WebApplicationContext of this servlet. * @see org.springframework.context.ConfigurableApplicationContext#close() */ @Override public void destroy() { getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'"); // Only call close() on WebApplicationContext if locally managed... if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) { ((ConfigurableApplicationContext) this.webApplicationContext).close(); } } /** * Override the parent class implementation in order to intercept PATCH * requests. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method = request.getMethod(); if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) { processRequest(request, response); } else { super.service(request, response); } } //get请求 @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } //post请求 @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } //put请求 @Override protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } //delete请求 @Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { processRequest(request, response); if (response.containsHeader("Allow")) { // Proper OPTIONS response coming from a handler - we're done. return; } } // Use response wrapper for Servlet 2.5 compatibility where // the getHeader() method does not exist super.doOptions(request, new HttpServletResponseWrapper(response) { @Override public void setHeader(String name, String value) { if ("Allow".equals(name)) { value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name(); } super.setHeader(name, value); } }); } @Override protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchTraceRequest) { processRequest(request, response); if ("message/http".equals(response.getContentType())) { // Proper TRACE response coming from a handler - we're done. return; } } super.doTrace(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } } protected LocaleContext buildLocaleContext(HttpServletRequest request) { return new SimpleLocaleContext(request.getLocale()); } protected ServletRequestAttributes buildRequestAttributes( HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) { if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) { return new ServletRequestAttributes(request, response); } else { return null; // preserve the pre-bound RequestAttributes instance } } private void initContextHolders( HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) { if (localeContext != null) { LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); } if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } } private void resetContextHolders(HttpServletRequest request, LocaleContext prevLocaleContext, RequestAttributes previousAttributes) { LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable); RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable); if (logger.isTraceEnabled()) { logger.trace("Cleared thread-bound request context: " + request); } } private void publishRequestHandledEvent( HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) { if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1); this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, statusCode)); } } protected String getUsernameForRequest(HttpServletRequest request) { Principal userPrincipal = request.getUserPrincipal(); return (userPrincipal != null ? userPrincipal.getName() : null); } protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception; //监听WebApplicationContext的刷新情况 private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { FrameworkServlet.this.onApplicationEvent(event); } } private class RequestBindingInterceptor extends CallableProcessingInterceptorAdapter { @Override public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class); initContextHolders(request, buildLocaleContext(request), buildRequestAttributes(request, response, null)); } } @Override public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { resetContextHolders(request, null, null); } } } }
SpringMVC源码分析--容器初始化(四)FrameworkServlet的更多相关文章
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- SpringMVC源码分析--容器初始化(三)HttpServletBean
在上一篇博客springMVC源码分析--容器初始化(二)DispatcherServlet中,我们队SpringMVC整体生命周期有一个简单的说明,并没有进行详细的源码分析,接下来我们会根据博客中提 ...
- springMVC源码分析--容器初始化(二)DispatcherServlet
在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...
- springMVC源码分析--容器初始化(一)ContextLoaderListener
在spring Web中,需要初始化IOC容器,用于存放我们注入的各种对象.当tomcat启动时首先会初始化一个web对应的IOC容器,用于初始化和注入各种我们在web运行过程中需要的对象.当tomc ...
- springMVC源码分析--SimpleUrlHandlerMapping(四)
上一篇博客springMVC源码分析--AbstractUrlHandlerMapping(三)中我们介绍了AbstractUrlHandlerMapping,主要介绍了一个handlerMap的ur ...
- springMVC源码分析--HttpRequestHandlerAdapter(四)
上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...
- springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...
- springMVC源码分析--HandlerAdapter(一)
HandlerAdapter的功能实际就是执行我们的具体的Controller.Servlet或者HttpRequestHandler中的方法. 类结构如下:
- 框架-springmvc源码分析(一)
框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...
随机推荐
- python2.7练习小例子(一)
1)题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去掉不满足条件的 ...
- EtherChannel(PAgP、LACP)基本配置--端口聚合--(转)
转自 http://blog.sina.com.cn/s/blog_635e1a9e01017msv.html EtherChannel EtherChannel(以太通道)也叫端口聚合或链路聚合,特 ...
- window 2008 下 安装域管理并且控制禁用QQ和U盘
场景需求下: 需求一:禁止普通用户使用USB.CD-ROM等驱动器防止病毒和资料外泄 需求二:并USB 键盘鼠标要可以使用 三:限制qq聊天工具的使用.这是公司真实环境需求.因此需要先模拟测试一下, ...
- Dynamic Web Module 3.0 requires Java 1.6 or newer.的解决
在项目的pom.xml增加 <build> <finalName>xxxxxxxx</finalName> <plugins> <plugin&g ...
- git报错:'fatal:remote origin already exists'怎么处理?附上git常用操作以及说明。
git添加远程库的时候有可能出现如下的错误, 怎么解决? 只要两步: 1.先删除 $ git remote rm origin 2.再次执行添加就可以了. ---------------------- ...
- 背包DP入门小笔记01背包
FJUT OJ 2347 http://59.77.139.92/Problem.jsp?pid=2347 采药 TimeLimit:1000MS MemoryLimit:128MB 64-bit ...
- ACM Sudoku
Sudoku是一个非常简单的任务. 具有9行9列的方形表被划分为9个较小的正方形3x3,如图所示. 在一些单元格中写入从1到9的十进制数字.其他单元格为空. 目标是填充空单元格,其中十进制数字从1到9 ...
- ACM Where is the Marble?
Description Raju and Meena love to play with Marbles. They have got a lot of marbles with numbers ...
- Java语言程序设计课程学期总结
2016-2017 第2学期 课程介绍 编程类课程,76学时(44理论+32实验),学期末还有1周的课程设计. 问题与现状 4个班共120人,教师无法逐一检查每个学生的编程实验. 纸质作业质量不高. ...
- Java中获取文件大小的正确方法
本文出处:http://blog.csdn.net/djy1992/article/details/51146837,转载请注明.由于本人不定期会整理相关博文,会对相应内容作出完善.因此强烈建议在原始 ...