如何使用第二个DispatcherServlet获取资源

时间:2014-11-11 20:06:45

标签: spring spring-mvc tomcat servlets

配置

Tomcat 7.0.47上的Servlet 3.0 春季3.1

问题

我有一个特殊情况,我需要两个DispatcherServlets:一个用于处理资源请求,一个用于处理正常的@RequestMapping类型请求。出于某种原因,我在日志中得到了这个:

  

在DispatcherServlet中找不到带有URI [/my-app/images/someimage.png]的HTTP请求的映射,其名称为' resources'

这是我的web.xml文件的样子:

<servlet>
  <servlet-name>resources</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>resources</servlet-name>
  <url-pattern>/css/*</url-pattern>
  <url-pattern>/images/*</url-pattern>
  <url-pattern>/js/*</url-pattern>
</servlet-mapping>

<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

resources-servlet.xml我有这个:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"
  default-autowire="byName">

  <context:annotation-config />
  <context:property-placeholder />

  <mvc:resources mapping="/images/**" location="/images/"/>
  <mvc:resources mapping="/js/**" location="/js/"/>

  <context:component-scan base-package="lesscss" />
  <mvc:annotation-driven />
</beans>

正如您所看到的,我对CSS文件没有<mvc:resources>因为该映射是使用@Controller构建LESS文件即时完成的。 <context:component-scan base-package="lesscss" />处理这一点,似乎正在发挥作用。

我之前在<mvc:resources>文件中有springmvc-servlet.xml个标记,但我删除了它们。我还能错过什么?

更新

我尝试将web.xml文件更改为使用*.ext网址格式而非/dir/*类型,并且有效。不过,我不必列出所有扩展名,所以我仍然希望能够更好地解决这个问题。

更新

我将<mvc:resources mapping="/images/**" location="/images/"/>更改为<mvc:resources mapping="/**" location="/images/"/>并删除了js个文件,并且图片开始正常工作。在Spring中映射事物时,servlet-mapping之前的url-pattern *似乎被忽略了。有办法解决这个问题吗?

解释为什么我需要&#34;两个Servlet

我想我会添加这个只是为了帮助那些之前没有意识到这一点的人(比如我),并给某人提供一个更好的解决方案,如果有的话。

使用<mvc:resources>标记时,创建的处理程序是常规DispatcherServlet请求处理的一部分。这意味着,如果您没有明确说明<mvc:resources>使用<mvc:mapping>处理的所有资源路径(我仍在使用Spring 3.1但无法使用<mvc:exclude-mapping>),对图片,JavaScript文件和样式表的每个请求都会运行您在HandlerInterceptor中列出的所有<mvc:interceptors>。我的应用程序中有很多拦截器,由于应用程序的结构和性质,列出所有路径都非常容易出错。不仅仅是性能损失,执行所有这些HandlerInterceptor实际上会破坏手写的POST-redirect-GET组件。

替代方法是检查每个HandlerInterceptor方法中使用的处理程序,但这很难干,也容易出错,而且我不能在OpenSessionInViewInterceptor这样的类上进行此调整扩展它们。我提出的避免所有这些混乱的解决方案是使用专门用于静态(ish)资源的单独servlet。

1 个答案:

答案 0 :(得分:2)

简单的解决方案

首先,我觉得我必须写明显(如果不是OP,那么将来可能会读到这个答案的人):

  • 简单的解决方案就是使用单DispatcherServlet。如果没有很强的理由,请不要使用两个servlet。

  • 或者,您可以将资源映射到/resources/*路径。将所有资源置于明确定义的路径下是一种常见的(最好的)做法。

复杂的解决方案

现在<mvc:resources>ResourcesBeanDefinitionParser处理。该组件注册了两个关键组件:SimpleUrlHandlerMappingResourceHttpRequestHandler

第一个组件负责将请求映射到处理程序。如果您不希望此组件strip servlet mapping path,则需要设置alwaysUseFullPath flag。这可以通过自己进行<mvc:resources>配置,也可以通过对已注册的实例进行后处理来完成。

对于使用问题中的示例进行手动配置,请设置映射和处理程序:

<context:component-scan base-package="lesscss" />

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
  <property name="alwaysUseFullPath" value="true" />
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="alwaysUseFullPath" value="true"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="alwaysUseFullPath" value="true"/>
  <property name="mappings">
    <props>
      <prop key="/images/**">imagesResources</prop>
      <prop key="/js/**">jsResources</prop>
    </props>
  </property>
</bean>

<bean id="imagesResources" class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler">
  <property name="locations">
    <list>
      <value>/images/</value>
    </list>
  </property>
</bean>

<bean id="jsResources" class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler">
  <property name="locations">
    <list>
      <value>/js/</value>
    </list>
  </property>
</bean>

对于后处理方法,创建并注册bean:

public class SimpleUrlMappingConfigurer implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof SimpleUrlHandlerMapping) {
            ((SimpleUrlHandlerMapping) bean).setAlwaysUseFullPath(true);
        }
        return bean;
    }

}