Spring源代码分析之Spring MVC与Web环境
2014-07-15 17:22
429 查看
1 IOC是一个独立的模块,它是与Web容器启动过程中集成在一起的。在Web容器的启动过程中,一方面处理Web容器的启动,另一方面通过设计特定的Web容器拦截器,将IOC环境加载到Web环境中,并将其初始化。
dispatcher起着分发请求的作用。
<context-param>用来指定Spring容器读取Bean定义的Xml路径
ContextLoaderListener在web容器启动过程中负责完成IOC容器的初始化。
Dispatcher和ContextLoaderListener提供了在Web容器中对Spring的接口,也就是说,这些接口与Web容器耦合式通过ServletContext来实现的。这个ServletContext为SpringIOC容器提供了一个宿主环境。
2 IOC容器启动的基本过程
ServletContextListener提供了与servlet生命周期结合的回调;
ContextLoader负责加载IOC容器;
WebApplicationContext接口:
XmlWebApplicationContext:
ContexLoaderListener加载IOC容器的过程:
首先从Servlet的事件中获得ServletContext,然后加载web.xml的各个相关属性,接着ContextLoader会加载WebApplicationContext,完成初始化和加载过程。这个根上下文会被绑定到ServletContext中去。可以通过WebApplicationContextUtils的静态方法来获得跟上下文。
WebApplicationContext getWebApplicationContext(ServletContext sc) ;
下面是具体的根上下文的载入过程。在ContextLoaderListener实现了ServletContextListener接口,这个的函数会结合Web容器的生命周期被调用。ContextLoaderListener是ServletContext的监听者。服务器启动的时候,服务器关闭的时候,ServletContext被创建的时候,ServletContext被销毁的时候。
由上面的源代码可以看出具体的初始化工作是由ContextLoader来完成的。
创建根上下文:
在web.xml中,我们除了要配置ContextLoaderListener,还要配置Dispatcher,它是一个前端控制器,负责对请求进行转发,匹配。
Dispatcher的初始化和加载过程
initServletBean方法在FrameworkServlet中完成
初始化上下文
MVC的初始化
initHanderMapping初始化过程:
HanderMapping:
在初始化完成的时候,上下文环境已经加载了所有的HanderMapping,存放着HTTP请求对应的数据。这些数据被存放在一个List,然后被排序
以SimpleUrlHanderMapping来分析HanderMapping的设计和实现。在SimpleUrlHanderMapping中定义了一个map来持有一系列的映射关系。
在HanderMappping接口总定义了一个geHander方法,通过这个方法来获取与HTTP请求对应的HanderExecutionChain,
这个HanderExceutionChain实现看起来比较简洁,它有一个Interceptor和一个hander对象,这个hander对象实际上是HTTP请求的Controller,它还有一个拦截器链。
SimpleUrlHandlerMapping:
<pre class="html" name="code"><pre class="html" name="code"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
dispatcher起着分发请求的作用。
<context-param>用来指定Spring容器读取Bean定义的Xml路径
ContextLoaderListener在web容器启动过程中负责完成IOC容器的初始化。
Dispatcher和ContextLoaderListener提供了在Web容器中对Spring的接口,也就是说,这些接口与Web容器耦合式通过ServletContext来实现的。这个ServletContext为SpringIOC容器提供了一个宿主环境。
2 IOC容器启动的基本过程
ServletContextListener提供了与servlet生命周期结合的回调;
ContextLoader负责加载IOC容器;
WebApplicationContext接口:
public interface WebApplicationContext extends ApplicationContext { //用于保存ServletContext的根上下文
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; /** * Scope identifier for request scope: "request". * Supported in addition to the standard scopes "singleton" and "prototype". */ String SCOPE_REQUEST = "request"; /** * Scope identifier for session scope: "session". * Supported in addition to the standard scopes "singleton" and "prototype". */ String SCOPE_SESSION = "session"; /** * Scope identifier for global session scope: "globalSession". * Supported in addition to the standard scopes "singleton" and "prototype". */ String SCOPE_GLOBAL_SESSION = "globalSession"; /** * Scope identifier for the global web application scope: "application". * Supported in addition to the standard scopes "singleton" and "prototype". */ String SCOPE_APPLICATION = "application"; /** * Name of the ServletContext environment bean in the factory. * @see javax.servlet.ServletContext */ String SERVLET_CONTEXT_BEAN_NAME = "servletContext"; /** * Name of the ServletContext/PortletContext init-params environment bean in the factory. * <p>Note: Possibly merged with ServletConfig/PortletConfig parameters. * ServletConfig parameters override ServletContext parameters of the same name. * @see javax.servlet.ServletContext#getInitParameterNames() * @see javax.servlet.ServletContext#getInitParameter(String) * @see javax.servlet.ServletConfig#getInitParameterNames() * @see javax.servlet.ServletConfig#getInitParameter(String) */ String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; /** * Name of the ServletContext/PortletContext attributes environment bean in the factory. * @see javax.servlet.ServletContext#getAttributeNames() * @see javax.servlet.ServletContext#getAttribute(String) */ String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; //获得Web容器的ServletContext上下文
ServletContext getServletContext(); }
XmlWebApplicationContext:
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { //默认加载BeanDefinition的路径 public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; //默认的配置文件所在的目录 public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; //默认的配置文件的后缀 public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; //load the BeanDefition visa XmlBeanDefinitionReader protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //允许子类为reader配置好自定义的初始化过程,之后加载BeanDefition initBeanDefinitionReader(beanDefinitionReader); //这里使用定义好的XmlBeanDefition来加载BeanDefition loadBeanDefinitions(beanDefinitionReader); } /** * Initialize the bean definition reader used for loading the bean * definitions of this context. Default implementation is empty. */ protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } /** * Load the bean definitions with the given XmlBeanDefinitionReader. * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method; * therefore this method is just supposed to load and/or register bean definitions. * <p>Delegates to a ResourcePatternResolver for resolving location patterns * into Resource instances. * @throws java.io.IOException if the required XML document isn't found * @see #refreshBeanFactory * @see #getConfigLocations * @see #getResources * @see #getResourcePatternResolver */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } } /** * The default location for the root context is "/WEB-INF/applicationContext.xml", * and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet" * (like for a DispatcherServlet instance with the servlet-name "test"). */ @Override protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } }
ContexLoaderListener加载IOC容器的过程:
首先从Servlet的事件中获得ServletContext,然后加载web.xml的各个相关属性,接着ContextLoader会加载WebApplicationContext,完成初始化和加载过程。这个根上下文会被绑定到ServletContext中去。可以通过WebApplicationContextUtils的静态方法来获得跟上下文。
WebApplicationContext getWebApplicationContext(ServletContext sc) ;
下面是具体的根上下文的载入过程。在ContextLoaderListener实现了ServletContextListener接口,这个的函数会结合Web容器的生命周期被调用。ContextLoaderListener是ServletContext的监听者。服务器启动的时候,服务器关闭的时候,ServletContext被创建的时候,ServletContext被销毁的时候。
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); }
由上面的源代码可以看出具体的初始化工作是由ContextLoader来完成的。
/** * Initialize Spring's web application context for the given servlet context, * using the application context provided at construction time, or creating a new one * @param servletContext current servlet context * @return the new WebApplicationContext * @see #ContextLoader(WebApplicationContext) * @see #CONTEXT_CLASS_PARAM * @see #CONFIG_LOCATION_PARAM */ public WebApplicationContext initWebApplicationContext(ServletContext 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!"); } 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 { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // The context has not yet been 125ca 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 -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, 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 ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
创建根上下文:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
/** * Return the WebApplicationContext implementation class to use, either the * default XmlWebApplicationContext or a custom context class if specified. * @param servletContext current servlet context * @return the WebApplicationContext implementation class to use * @see #CONTEXT_CLASS_PARAM * @see org.springframework.web.context.support.XmlWebApplicationContext */ protected Class<?> determineContextClass(ServletContext servletContext) { //这里读取servletContext的CONTEXT_CLASS_PARAM参数 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); //如果servletContext配置了CONTEXT_CLASS,则用这个class if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else {//如果没有配置,那么使用默认配置的class contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
在web.xml中,我们除了要配置ContextLoaderListener,还要配置Dispatcher,它是一个前端控制器,负责对请求进行转发,匹配。
Dispatcher的初始化和加载过程
/** * Map config parameters onto bean properties of this servlet, and * invoke subclass initialization. * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. */ public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. //获取servlet的初始化参数,对Bean属性进行配置 try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. //调用子类进行初始化 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
initServletBean方法在FrameworkServlet中完成
/** * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. */ @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 { //这里初始化上下文 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"); } }
初始化上下文
/** * Initialize and publish the WebApplicationContext for this servlet. * <p>Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * @return the WebApplicationContext instance * @see #FrameworkServlet(WebApplicationContext) * @see #setContextClass * @see #setContextConfigLocation */ protected WebApplicationContext initWebApplicationContext() { //从ServletContext中获取根上下文,作为MVC的双亲上下文 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; 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); } } } 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 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one 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. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. //把当前建立的上下文放到ServletContext中,属性名是与当前的Servlet相关的 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; }
MVC的初始化
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
initHanderMapping初始化过程:
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. //导入所有的HanderMapping Bean,可能在Dispatcher的IOC容器内,也有可能在在其双亲上下文中 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } } else { //可以根据名称从当前的IOC容器内获取handerMapping try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. //如果没有则设置一个默认的HanderMapping if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
HanderMapping:
在初始化完成的时候,上下文环境已经加载了所有的HanderMapping,存放着HTTP请求对应的数据。这些数据被存放在一个List,然后被排序
以SimpleUrlHanderMapping来分析HanderMapping的设计和实现。在SimpleUrlHanderMapping中定义了一个map来持有一系列的映射关系。
在HanderMappping接口总定义了一个geHander方法,通过这个方法来获取与HTTP请求对应的HanderExecutionChain,
这个HanderExceutionChain实现看起来比较简洁,它有一个Interceptor和一个hander对象,这个hander对象实际上是HTTP请求的Controller,它还有一个拦截器链。
public interface HandlerMapping { /** * Name of the {@link HttpServletRequest} attribute that contains the path * within the handler mapping, in case of a pattern match, or the full * relevant URI (typically within the DispatcherServlet's mapping) else. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations. URL-based HandlerMappings will * typically support it, but handlers should not necessarily expect * this request attribute to be present in all scenarios. */ String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; /** * Name of the {@link HttpServletRequest} attribute that contains the * best matching pattern within the handler mapping. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations. URL-based HandlerMappings will * typically support it, but handlers should not necessarily expect * this request attribute to be present in all scenarios. */ String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; /** * Name of the boolean {@link HttpServletRequest} attribute that indicates * whether type-level mappings should be inspected. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations. */ String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; /** * Name of the {@link HttpServletRequest} attribute that contains the URI * templates map, mapping variable names to values. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations. URL-based HandlerMappings will * typically support it, but handlers should not necessarily expect * this request attribute to be present in all scenarios. */ String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; /** * Name of the {@link HttpServletRequest} attribute that contains a map with * URI matrix variables. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations and may also not be present depending on * whether the HandlerMapping is configured to keep matrix variable content * in the request URI. */ String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; /** * Name of the {@link HttpServletRequest} attribute that contains the set of * producible MediaTypes applicable to the mapped handler. * <p>Note: This attribute is not required to be supported by all * HandlerMapping implementations. Handlers should not necessarily expect * this request attribute to be present in all scenarios. */ String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; /** * Return a handler and any interceptors for this request. The choice may be made * on request URL, session state, or any factor the implementing class chooses. * <p>The returned HandlerExecutionChain contains a handler Object, rather than * even a tag interface, so that handlers are not constrained in any way. * For example, a HandlerAdapter could be written to allow another framework's * handler objects to be used. * <p>Returns {@code null} if no match was found. This is not an error. * The DispatcherServlet will query all registered HandlerMapping beans to find * a match, and only decide there is an error if none can find a handler. * @param request current HTTP request * @return a HandlerExecutionChain instance containing handler object and * any interceptors, or {@code null} if no mapping found * @throws Exception if there is an internal error */ HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute */ public HandlerExecutionChain(Object handler) { this(handler, null); } /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute * @param interceptors the array of interceptors to apply * (in the given order) before the handler itself executes */ public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } /** * Return the handler object to execute. * @return the handler object */ public Object getHandler() { return this.handler; } public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this.interceptorList.add(interceptor); } public void addInterceptors(HandlerInterceptor[] interceptors) { if (interceptors != null) { initInterceptorList(); this.interceptorList.addAll(Arrays.asList(interceptors)); } } private void initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } } /** * Return the array of interceptors to apply (in the given order). * @return the array of HandlerInterceptors instances (may be {@code null}) */ public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } /** * Apply preHandle methods of registered interceptors. * @return {@code true} if the execution chain should proceed with the * next interceptor or the handler itself. Else, DispatcherServlet assumes * that this interceptor has already dealt with the response itself. */ boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { if (getInterceptors() != null) { for (int i = 0; i < getInterceptors().length; i++) { HandlerInterceptor interceptor = getInterceptors()[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; } /** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { if (getInterceptors() == null) { return; } for (int i = getInterceptors().length - 1; i >= 0; i--) { HandlerInterceptor interceptor = getInterceptors()[i]; interceptor.postHandle(request, response, this.handler, mv); } } /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle invocation * has successfully completed and returned true. */ void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { if (getInterceptors() == null) { return; } for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = getInterceptors()[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } /** * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors. */ void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) { if (getInterceptors() == null) { return; } for (int i = getInterceptors().length - 1; i >= 0; i--) { if (interceptors[i] instanceof AsyncHandlerInterceptor) { try { AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) this.interceptors[i]; asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler); } catch (Throwable ex) { logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex); } } } } /** * Delegates to the handler's {@code toString()}. */ @Override public String toString() { if (this.handler == null) { return "HandlerExecutionChain with no handler"; } StringBuilder sb = new StringBuilder(); sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]"); if (!CollectionUtils.isEmpty(this.interceptorList)) { sb.append(" and ").append(this.interceptorList.size()).append(" interceptor"); if (this.interceptorList.size() > 1) { sb.append("s"); } } return sb.toString(); } }
public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute */ public HandlerExecutionChain(Object handler) { this(handler, null); } /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute * @param interceptors the array of interceptors to apply * (in the given order) before the handler itself executes */ public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } /** * Return the handler object to execute. * @return the handler object */ public Object getHandler() { return this.handler; } public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this.interceptorList.add(interceptor); } public void addInterceptors(HandlerInterceptor[] interceptors) { if (interceptors != null) { initInterceptorList(); this.interceptorList.addAll(Arrays.asList(interceptors)); } } private void initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } } /** * Return the array of interceptors to apply (in the given order). * @return the array of HandlerInterceptors instances (may be {@code null}) */ public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } /** * Apply preHandle methods of registered interceptors. * @return {@code true} if the execution chain should proceed with the * next interceptor or the handler itself. Else, DispatcherServlet assumes * that this interceptor has already dealt with the response itself. */ boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { if (getInterceptors() != null) { for (int i = 0; i < getInterceptors().length; i++) { HandlerInterceptor interceptor = getInterceptors()[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; } /** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { if (getInterceptors() == null) { return; } for (int i = getInterceptors().length - 1; i >= 0; i--) { HandlerInterceptor interceptor = getInterceptors()[i]; interceptor.postHandle(request, response, this.handler, mv); } } /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle invocation * has successfully completed and returned true. */ void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { if (getInterceptors() == null) { return; } for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = getInterceptors()[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } /** * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors. */ void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) { if (getInterceptors() == null) { return; } for (int i = getInterceptors().length - 1; i >= 0; i--) { if (interceptors[i] instanceof AsyncHandlerInterceptor) { try { AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) this.interceptors[i]; asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler); } catch (Throwable ex) { logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex); } } } } /** * Delegates to the handler's {@code toString()}. */ @Override public String toString() { if (this.handler == null) { return "HandlerExecutionChain with no handler"; } StringBuilder sb = new StringBuilder(); sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]"); if (!CollectionUtils.isEmpty(this.interceptorList)) { sb.append(" and ").append(this.interceptorList.size()).append(" interceptor"); if (this.interceptorList.size() > 1) { sb.append("s"); } } return sb.toString(); } }
SimpleUrlHandlerMapping:
/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }
/** * Register all handlers specified in the URL map for the corresponding paths. * @param urlMap Map with URL paths as keys and handler beans or bean names as values * @throws BeansException if a handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } }
/** * Register the specified handler for the given URL path. * @param urlPath the URL the bean should be mapped to * @param handler the handler instance or handler bean name String * (a bean name will automatically be resolved into the corresponding handler bean) * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. //如果直接用bean Name来映射,那么直接从容器内获取 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else {//如果URL是'/',把这个'/'设置的controller放到rootHander中 if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); }//如果URL是'/*',把这个'/'设置的controller放到defaultHander中 else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else {//处理正常的URL this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
相关文章推荐
- Spring技术内幕之Spring MVC与Web环境(02)- Spring Web MVC核心架构
- spring源代码分析之org.springframework.web.method.support.HandlerMethodReturnValueHandler
- 看透Spring MVC:源代码分析与实践 (Web开发技术丛书)
- Spring源代码分析之(二):IOC容器在web容器中的启动
- 【JAVA Web】spring mvc4.1.6 + spring4.1.6 + hibernate4.3.11 + mysql5.5.25 开发环境搭建及相关说明
- Spring boot源码分析-AnnotationConfigApplicationContext非web环境下的启动容器(2)
- Spring技术内幕——Spring MVC与Web环境(未完)
- Spring技术内幕之Spring MVC与Web环境(01)-MVC模式
- Spring源码分析3----Web环境中的SpringMVC(Web容器中的上下文的设计)
- spring mvc 源代码笔记1--org.springframework.web.servlet.mvc.method
- 深入剖析Spring Web源码(七) - DispatcherServlet的实现 - 根共享环境的加载/其他Servlet
- DE2开发板:NiosII+LWIP环境下DM9000A的驱动程序分析(web_server.c)
- spring mvc框架 web.xml配置文件 Could not open ServletContext resource [/WEB-INF/classes/spring-servlet.xml] 错误
- Apache Geronimo 和 Spring 框架,第 6 部分: Spring MVC:使用 Web 视图技术
- web 环境,非web 环境中,freemarker 结合 spring使用。
- web 环境,非web 环境中,freemarker 结合 spring使用。
- 在 Spring Web MVC 环境下使用 DWR
- Spring3.0上传和下载完整分析和源代码
- Spring3.0上传下载完整源代码和分析
- Spring源代码分析(14)---Spring事务(兵来将挡,水来土淹)