SpringMVC-处理404和default-servlet-handler处理静态资源
一、解析mvc:default-servlet-handler/
<mvc:default-servlet-handler/>标签是导入DefaultServletHttpRequestHandler和SimpleUrlHandlerMapping两个bean到spring容器中。DefaultServletHttpRequestHandler通过web容器的default Servlet处理静态资源和处理handle类型是HttpRequestHandler的请求。也可通过配置mvc:resources处理静态资源。此时需要在web.xml里面的DispatcherServlet拦截非静态资源的请求。mvc:resources是通过ResourceHttpRequestHandler来处理的。将请求路径通过mvc:resources映射成文件路径,加载文件成Resource。将Resource返回给客户端。
SpringMVC处理静态资源可参考https://zhuanlan.zhihu.com/p/82961573。
mvc.xml中<mvc:default-servlet-handler/>由DefaultServletHandlerBeanDefinitionParser负责解析。
DefaultServletHandlerBeanDefinitionParser.parse(Element element, ParserContext parserContext)
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
String defaultServletName = element.getAttribute("default-servlet-name");
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
defaultServletHandlerDef.setSource(source);
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (StringUtils.hasText(defaultServletName)) {
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
}
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
Map<String, String> urlMap = new ManagedMap<>();
urlMap.put("/**", defaultServletHandlerName);
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
增加DefaultServletHttpRequestHandler的RootBeanDefinition,DefaultServletHttpRequestHandler处理静态文件。增加SimpleUrlHandlerMapping的RootBeanDefinition。SimpleUrlHandlerMapping的urlMap属性增加了/**为key,DefaultServletHttpRequestHandler为value的元素。
二、获取handle
当发起一个没有handle处理方法的请求时,DispatcherServlet.getHandler(HttpServletRequest request)获取对应的handle方法。
DispatcherServlet.getHandler(HttpServletRequest request)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
handlerMappings中由SimpleUrlHandlerMapping处理Controller类中没有找到处理请求的handle的请求。
AbstractHandlerMapping.getHandler(HttpServletRequest request)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
1、getHandlerInternal获取handle,若不能获取getDefaultHandler获取默认的handle,还是不能获取handle返回null。
2、如果获取到了handle,调用getHandlerExecutionChain获取handle执行链
3、处理CORS
AbstractUrlHandlerMapping.getHandlerInternal(HttpServletRequest request)
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
Object handler;
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
handler = lookupHandler(lookupPath, request);
}
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
1、initLookupPath获取请求路径
2、lookupHandler获取handle
3、获取不到handle则获取默认handle
AbstractUrlHandlerMapping.lookupHandler(String lookupPath, HttpServletRequest request)
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
Object handler = getDirectMatch(lookupPath, request);
if (handler != null) {
return handler;
}
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, lookupPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
1、getDirectMatch通过请求路径完全匹配获取handle
2、如果获取不到handle,遍历handlerMap,将handlerMap中的所有registeredPattern和请求路径进行模式匹配。如匹配则加入matchingPatterns。
3、如果存在模式匹配则从handlerMap获取handle。
4、extractPathWithinPattern从路径模式和请求路径中解析出模式映射部分。如模式为myroot/*.html,请求路径是myroot/myfile.html,返回myfile.html
5、extractUriTemplateVariables解析uri模板变量
6、buildPathExposingHandler构建暴露的handle
AbstractUrlHandlerMapping.getDirectMatch(String urlPath, HttpServletRequest request)
private Object getDirectMatch(String urlPath, HttpServletRequest request) throws Exception {
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
return null;
}
从handlerMap中通过请求路径完全匹配获取handle,如果获取到了则调用getBean获取对应的bean。handlerMap中的key为/**,value为DefaultServletHttpRequestHandler。
AbstractUrlHandlerMapping.buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables)
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
1、创建HandlerExecutionChain
2、HandlerExecutionChain中加入PathExposingHandlerInterceptor
3、如果存在uri模板变量则往HandlerExecutionChain加入UriTemplateVariablesHandlerInterceptor
AbstractHandlerMapping.getHandlerExecutionChain(Object handler, HttpServletRequest request)
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
获取HandlerExecutionChain。HandlerExecutionChain中保存了所有的请求拦截器。
三、处理请求
DispatcherServlet.getHandlerAdapter(Object handler)
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
通过handle获取HandlerAdapter。handle是DefaultServletHttpRequestHandler类型。DefaultServletHttpRequestHandler类型的handle是通过HttpRequestHandlerAdapter来处理。
HttpRequestHandlerAdapter.handle(HttpServletRequest request, HttpServletResponse response, Object handler)
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
DefaultServletHttpRequestHandler.handleRequest(HttpServletRequest request, HttpServletResponse response)
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Assert.state(this.servletContext != null, "No ServletContext set");
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
if (rd == null) {
throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
this.defaultServletName + "'");
}
rd.forward(request, response);
}
获取RequestDispatcher,forward转发请求。
没有handle处理的请求的状态码是404,如果没有配置对应的处理页面。则由web容器处理。tomcat处理页面时
如果在web.xml中配置
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
对应状态码的处理请求。则上面的DefaultServletHttpRequestHandler.handleRequest会在当前请求转发一个请求。请求路径是上面配置的location。
例如:
@RequestMapping("/404")
public String handleNoFound() {
return "404";
}
由这个handle处理404状态码的请求。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通