在SpringMVC中,如果将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕 获 WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他 们当成一个普通请求处理,因找不到对应处理器将导致错误。
可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/> 的方式解决静态资源的问题:
<mvc:default-servlet-handler/> 将在 SpringMVC 上下文中注册一个SimpleUrlHandlerMapping,以及注册一个
DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由
WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet 继续处理。
一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name
属性显式指定。
1)、 <mvc:default-servlet-handler/>标签处理
<mvc:default-servlet-handler/>标签是MVC模块自定义标签,当Ioc容器解析BeanDefinition遇到<mvc:default-servlet-handler/>这个标签,将使用自定义标签解析机制处理这些标签。MvcNamespaceHandler类用于加载与Spring
MVC模块相关的自定义标签解析器:
public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); //这里注册<mvc:default-servlet-handlerv>标签解析器,该标签主要用与处理静态资源 registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); //这里注册<mvc:interceptors>标签解析器 registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); } }
在注册的DefaultServletHandlerBeanDefinitionParser类中主要的方法是parse方法:
class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); //获取指定的默认 Servlet 名称 String defaultServletName = element.getAttribute("default-servlet-name"); //这里定义DefaultServletHttpRequestHandler BeanDefinition,之后将注册这个BeanDefinition到应用上下文 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)); //这里构建一个urlMap 以便于SimpleUrlHandlerMapping使用,完成请求到Handler的映射 Map<String, String> urlMap = new ManagedMap<String, String>(); urlMap.put("/**", defaultServletHandlerName); //这里定义SimpleUrlHandlerMapping BeanDefinition,之后将注册这个BeanDefinition到应用上下文,用于将请求匹配"/**"静态资源的路径映射到DefaultServletHttpRequestHandler,使用默认的Servlet进行处理 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实现HttpRequestHandler接口,重写handleRequest()方法实现请求处理:
public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware { /** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish */ private static final String COMMON_DEFAULT_SERVLET_NAME = "default"; /** Default Servlet name used by Google App Engine */ private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default"; /** Default Servlet name used by Resin */ private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file"; /** Default Servlet name used by WebLogic */ private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet"; /** Default Servlet name used by WebSphere */ private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet"; @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //通过servletContext获取默认Servlet的请求转发器 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 +"'"); } //将请求转发给默认的Servlet请求,以便处理静态资源等 rd.forward(request, response); } }