自定义SpringMVC拦截器中HandlerMethod类型转换问题调研

本文详细探讨了在将SpringMVC模块迁移到Spring Boot并使用内嵌Tomcat时,遇到的HandlerMethod类型转换异常问题。通过分析发现,问题源于内嵌Tomcat未正确处理context-path,导致请求被误识别为静态资源。解决方案是在`application.properties`中指定`server.contextPath`,从而避免问题。
摘要由CSDN通过智能技术生成

摘要

在将a模块迁移到spring boot项目下、使用embeded tomcat启动项目后,在调用RESTfule接口时,模块中声明的一个SpringMVC拦截器"cn.xxx.thread.common.web.speedctrlforuser.SpeedctrlForUserInterceptor"中抛出了ClassCastException。但是使用外置Tomcat启动就没有这个问题。在逐行debug后发现是spring boot缺失一项配置导致了这个问题。

问题

在 TECHSTUDY-91 - THREAD模块接入服务注册/订阅服务 进行中 任务中,我为a模块定义了一个启动类(注解了@SpringBootApplication),并配置了对应的application.properties。由于目前只需要注册到eureka上,配置文件中只有如下两行配置:

applictaion.properties
spring.application.name=a
eureka.client.serviceUrl.defaultZone=http://10.255.33.207:8080/eureka,http://10.255.33.208:8080/eureka,http://10.255.33.209:8080/eureka

在其它配置(如maven依赖关系、xml配置文件引入等)都整理好之后,用eclipse将a模块发布到tomcat上(即打成war包后发布),调用auth模块接口(如http://localhost:8080/a/rest/users/admin),一切正常。
但是,在使用启动类将模块发布到内置tomcat上(相当于打成jar包后发布),再调用上述auth模块的接口,会出现以下异常:

17:52:31,864 ERROR [org.apache.juli.logging.DirectJDKLog.log] (http-nio-8080-exec-2) Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.web.servlet.resource.ResourceHttpRequestHandler cannot be cast to org.springframework.web.method.HandlerMethod] with root cause^|TraceId.-http-nio-8080-exec-2
java.lang.ClassCastException: org.springframework.web.servlet.resource.ResourceHttpRequestHandler cannot be cast to org.springframework.web.method.HandlerMethod
at cn.xxx.thread.common.web.speedctrlforuser.SpeedctrlForUserInterceptor.preHandle(SpeedctrlForUserInterceptor.java:66) ~[classes/:?]
at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:133) ~[spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:962) ~[spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.proce***equest(FrameworkServlet.java:970)

分析

从上文的异常信息可知,问题出现在SpeedctrlForUserInterceptor的第66行。这里的代码是这样的:

public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler)
        throws TooManyRequestsException {
    User user = SecurityUtils.getUserFromPrincipal(SecurityContextHolder
        .getContext().getAuthentication());
    if (user == null) {
        return true;
    }
    HandlerMethod method = (HandlerMethod) handler; // 这里是第66行
    // 省略后续代码
}

在第66行,代码中做了一个强制类型转换。根据异常信息,在这里得到的handler是一个ResourceHttpRequestHandler,而不是HandlerMethod。所以会报错。
这里的ResourceHttpRequestHandler和HandlerMethod分别是什么呢?我们可以简单的看一下二者的Javadoc。

org.springframework.web.servlet.resource.ResourceHttpRequestHandler
HttpRequestHandler that serves static resources in an optimized way according to the guidelines of Page Speed, YSlow, etc.
The "locations" property takes a list of Spring Resource locations from which static resources are allowed to be served by this handler. Resources could be served from a classpath location, e.g. "classpath:/META-INF/public-web-resources/", allowing convenient packaging and serving of resources such as .js, .css, and others in jar files.
This request handler may also be configured with a resourcesResolver and resourceTransformer chains to support arbitrary resolution and transformation of resources being served. By default a PathResourceResolver simply finds resources based on the configured "locations". An application can configure additional resolvers and transformers such as the VersionResourceResolver which can resolve and prepare URLs for resources with a version in the URL.
This handler also properly evaluates the Last-Modified header (if present) so that a 304 status code will be

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值