| Method from org.springframework.web.servlet.DispatcherServlet Detail: |
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
return new LocaleContext() {
public Locale getLocale() {
return localeResolver.resolveLocale(request);
}
public String toString() {
return getLocale().toString();
}
};
}
Build a LocaleContext for the given request, exposing the request's
primary locale as current locale.
The default implementation uses the dispatcher's LocaleResolver
to obtain the current locale, which might change during a request. |
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (request instanceof MultipartHttpServletRequest) {
logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml");
}
else {
return this.multipartResolver.resolveMultipart(request);
}
}
// If not returned before: return original request.
return request;
}
Convert the request into a multipart request, and make multipart resolver available.
If no multipart resolver is set, simply use the existing request. |
protected void cleanupMultipart(HttpServletRequest request) {
if (request instanceof MultipartHttpServletRequest) {
this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
}
}
Clean up any resources used by the given multipart request (if any). |
protected Object createDefaultStrategy(ApplicationContext context,
Class clazz) throws BeansException {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
|
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
// Expose current LocaleResolver and request as LocaleContext.
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
// Expose current RequestAttributes to current thread.
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
try {
ModelAndView mv = null;
boolean errorView = false;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
getServletName() + "': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
// Reset thread-bound context.
RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
// Clear request attributes.
requestAttributes.requestCompleted();
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
}
}
Process the actual dispatching to the handler.
The handler will be obtained by applying the servlet's HandlerMappings in order.
The HandlerAdapter will be obtained by querying the servlet's installed
HandlerAdapters to find the first that supports the handler class.
All HTTP methods are handled by this method. It's up to HandlerAdapters or
handlers themselves to decide which methods are acceptable. |
protected void doService(HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = new UrlPathHelper().getRequestUri(request);
logger.debug("DispatcherServlet with name '" + getServletName() +
"' processing request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
Exposes the DispatcherServlet-specific request attributes and
delegates to #doDispatch for the actual dispatching. |
protected List getDefaultStrategies(ApplicationContext context,
Class strategyInterface) throws BeansException {
String key = strategyInterface.getName();
List strategies = null;
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
strategies = new ArrayList(classNames.length);
for (int i = 0; i < classNames.length; i++) {
String className = classNames[i];
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_15 && className.indexOf("Annotation") != -1) {
// Skip Java 5 specific strategies when running on JDK 1.4...
continue;
}
try {
Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add(strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
}
else {
strategies = Collections.EMPTY_LIST;
}
return strategies;
}
Create a List of default strategy objects for the given strategy interface.
The default implementation uses the "DispatcherServlet.properties" file
(in the same package as the DispatcherServlet class) to determine the class names.
It instantiates the strategy objects through the context's BeanFactory. |
protected Object getDefaultStrategy(ApplicationContext context,
Class strategyInterface) throws BeansException {
List strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
Return the default strategy object for the given strategy interface.
The default implementation delegates to #getDefaultStrategies ,
expecting a single object in the list. |
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return this.viewNameTranslator.getViewName(request);
}
Translate the supplied request into a default view name. |
protected HandlerExecutionChain getHandler(HttpServletRequest request,
boolean cache) throws Exception {
HandlerExecutionChain handler =
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
return handler;
}
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
if (logger.isTraceEnabled()) {
logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" +
getServletName() + "'");
}
handler = hm.getHandler(request);
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
return handler;
}
}
return null;
}
Return the HandlerExecutionChain for this request.
Try all handler mappings in order. |
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) it.next();
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: Does your handler implement a supported interface like Controller?");
}
Return the HandlerAdapter for this handler object. |
protected long getLastModified(HttpServletRequest request) {
if (logger.isDebugEnabled()) {
String requestUri = new UrlPathHelper().getRequestUri(request);
logger.debug("DispatcherServlet with name '" + getServletName() +
"' determining Last-Modified value for [" + requestUri + "]");
}
try {
HandlerExecutionChain mappedHandler = getHandler(request, true);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// Ignore - > will reappear on doService.
logger.debug("No handler found in getLastModified");
return -1;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = new UrlPathHelper().getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
return lastModified;
}
catch (Exception ex) {
// Ignore - > will reappear on doService.
logger.debug("Exception thrown in getLastModified", ex);
return -1;
}
}
Override HttpServlet's getLastModified method to evaluate
the Last-Modified value of the mapped handler. |
public final MultipartResolver getMultipartResolver() {
return this.multipartResolver;
}
Obtain this servlet's MultipartResolver, if any. |
public final ThemeSource getThemeSource() {
if (getWebApplicationContext() instanceof ThemeSource) {
return (ThemeSource) getWebApplicationContext();
}
else {
return null;
}
}
Return this servlet's ThemeSource, if any; else return null.
Default is to return the WebApplicationContext as ThemeSource,
provided that it implements the ThemeSource interface. |
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
}
|
protected void noHandlerFound(HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
String requestUri = new UrlPathHelper().getRequestUri(request);
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" +
requestUri + "] in DispatcherServlet with name '" + getServletName() + "'");
}
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
No handler found -> set appropriate HTTP response status. |
protected void onRefresh(ApplicationContext context) throws BeansException {
initStrategies(context);
}
|
protected ModelAndView processHandlerException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// Check registerer HandlerExceptionResolvers...
ModelAndView exMv = null;
for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) {
HandlerExceptionResolver resolver = (HandlerExceptionResolver) it.next();
exMv = resolver.resolveException(request, response, handler, ex);
}
if (exMv != null) {
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
// Send default responses for well-known exceptions, if possible.
if (ex instanceof HttpRequestMethodNotSupportedException && !response.isCommitted()) {
String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
return null;
}
throw ex;
}
Determine an error ModelAndView via the registered HandlerExceptionResolvers. |
protected void render(ModelAndView mv,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view = null;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
view.render(mv.getModelInternal(), request, response);
}
Render the given ModelAndView. This is the last stage in handling a request.
It may involve resolving the view by name. |
protected View resolveViewName(String viewName,
Map model,
Locale locale,
HttpServletRequest request) throws Exception {
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
Resolve the given view name into a View object (to be rendered).
Default implementations asks all ViewResolvers of this dispatcher.
Can be overridden for custom resolution strategies, potentially based
on specific model attributes or request parameters. |
public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
this.cleanupAfterInclude = cleanupAfterInclude;
}
Set whether to perform cleanup of request attributes after an include request,
that is, whether to reset the original state of all request attributes after
the DispatcherServlet has processed within an include request. Else, just the
DispatcherServlet's own request attributes will be reset, but not model
attributes for JSPs or special attributes set by views (for example, JSTL's).
Default is "true", which is strongly recommended. Views should not rely on
request attributes having been set by (dynamic) includes. This allows JSP views
rendered by an included controller to use any model attributes, even with the
same names as in the main JSP, without causing side effects. Only turn this
off for special needs, for example to deliberately allow main JSPs to access
attributes from JSP views rendered by an included controller. |
public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
this.detectAllHandlerAdapters = detectAllHandlerAdapters;
}
Set whether to detect all HandlerAdapter beans in this servlet's context.
Else, just a single bean with name "handlerAdapter" will be expected.
Default is "true". Turn this off if you want this servlet to use a
single HandlerAdapter, despite multiple HandlerAdapter beans being
defined in the context. |
public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
}
Set whether to detect all HandlerExceptionResolver beans in this servlet's context.
Else, just a single bean with name "handlerExceptionResolver" will be expected.
Default is "true". Turn this off if you want this servlet to use a
single HandlerExceptionResolver, despite multiple HandlerExceptionResolver
beans being defined in the context. |
public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
this.detectAllHandlerMappings = detectAllHandlerMappings;
}
Set whether to detect all HandlerMapping beans in this servlet's context.
Else, just a single bean with name "handlerMapping" will be expected.
Default is "true". Turn this off if you want this servlet to use a
single HandlerMapping, despite multiple HandlerMapping beans being
defined in the context. |
public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
this.detectAllViewResolvers = detectAllViewResolvers;
}
Set whether to detect all ViewResolver beans in this servlet's context.
Else, just a single bean with name "viewResolver" will be expected.
Default is "true". Turn this off if you want this servlet to use a
single ViewResolver, despite multiple ViewResolver beans being
defined in the context. |
public void setThreadContextInheritable(boolean threadContextInheritable) {
this.threadContextInheritable = threadContextInheritable;
}
Set whether to expose the LocaleContext and RequestAttributes as inheritable
for child threads (using an java.lang.InheritableThreadLocal ).
Default is "false", to avoid side effects on spawned background threads.
Switch this to "true" to enable inheritance for custom child threads which
are spawned during request processing and only used for this request
(that is, ending after their initial task, without reuse of the thread).
WARNING: Do not use inheritance for child threads if you are
accessing a thread pool which is configured to potentially add new threads
on demand (e.g. a JDK java.util.concurrent.ThreadPoolExecutor ),
since this will expose the inherited context to such a pooled thread. |