小白新手web开发简单总结(十七)-DispatcherServlet中的url-pattern的一点反思

目录

 

前言

一 匹配路径

1.在web.xml注册

2.源码分析

3.解决模糊匹配页面404

二 后缀名匹配

三 总结


前言

在小白新手web开发简单总结(十六)-Web层优化(DispatcherServlet源码分析)中也提到需要在web.xml配置DispatcherServlet的url-pattern用来过滤哪些浏览器发送过来的请求交给该DispatcherServlet来处理。在这个url-pattern配置过程中自己遇到一些问题,特意总结一下。

前提基于@RequestMapping注解的方式来将url和业务方法进行匹配,也就是使用的HandlerMapping为RequestMappingHandlerMapping。我们前面在小白新手web开发简单总结(十六)-Web层优化(DispatcherServlet源码分析)也提到RequestMappingHandlerMapping就是维护着url和业务方法进行匹配的一个映射关系。

一 匹配路径

1.在web.xml注册

在使用路径匹配的方式时,形式一:精准匹配:即将请求的路径完全放到url-pattern,这样就仅仅会只拦截一个请求,如下:

    <servlet-mapping>
        <servlet-name>sql-web</servlet-name>
        <url-pattern>/mvc/getbook</url-pattern>
    </servlet-mapping>

形式二:模糊匹配:用来拦截与之相关的所有请求,如下:

    <servlet-mapping>
        <servlet-name>sql-web</servlet-name>
        <url-pattern>/mvc/*</url-pattern>
    </servlet-mapping>

2.源码分析

上面两种方式的执行过程,前面的过程是相同的。我们启动项目之后通过访问http://localhost:8080/mvc/getbook来看下返回

  • (1)DispatcherServlet接收该请求

当DispatcherServlet接收到该请求的时候,会调用到doDispatch()方法,其中有下面的逻辑,就是根据传入的request去查找对应的Handler,而这个Handler就是我们定义与url匹配的处理业务的方法。

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
  • (2)执行具体的HandlerMapping(本例为RequestMappingHandlerMapping)#getHandlerInternal()

根据getHandler()里面的具体逻辑,通过debug发现

最终会调用到使用的RequestMappingHandlerMapping的继承的父类 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping中的getHandlerInternal()根据对应的request找到对应的HandlerMethod,该HandlerMethod就是对应的具体的url匹配的处理业务的方法

    /**
	 * Look up a handler method for the given request.
	 */
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
  • (3)执行 org.springframework.web.util.UrlPathHelper#getLookupPathForRequest(request),获取request中的url

该方法的代码如下,主要就是根据alwaysUseFullPath来决定调用哪个方法,默认情况下该值为false,那么就需要进入到getPathWithinServletMapping()去查看后面的逻辑。

    public String getLookupPathForRequest(HttpServletRequest request) {
        if (this.alwaysUseFullPath) {
            return this.getPathWithinApplication(request);
        } else {
            String rest = this.getPathWithinServletMapping(request);
            return !"".equals(rest) ? rest : this.getPathWithinApplication(request);
        }
    }

进入到org.springframework.web.util.UrlPathHelper#getPathWithinServletMapping()中,刚才两种<url-pattern>的处理方式会有所不同:

形式一:当设置为 <url-pattern>/mvc/getbook</url-pattern>

在执行UrlPathHelper#getPathWithinServletMapping()的时候,servletPath的值为"/mvc/getbook",恰好就是我们配置<url-pattern>的内容。此时sanitizedPathWithinApp值也是"/mvc/getbook",通过调用getRemainingPath()之后,最后path的值为""。

 那么执行完getPathWithinServletMapping()返回给UrlPathHelper#getLookupPathForRequest()的时候,此时rest为""。

那么会执行UrlPathHelper#getPathWithinApplication(),此时与alwaysUseFullPath为false的时候,调用的方法一样。当getPathWithinApplication()执行完,最终getLookupPathForRequest()返回的url为"/mvc/getbook"。

返回给AbstractHandlerMethodMapping#getHandlerInternal()的时候,此时lookupPath为"/mvc/getbook"。

形式二:当设置为 <url-pattern>/mvc/*</url-pattern>

同样执行getPathWithinServletMapping()的时候,由于servletPath返回的"/mvc",恰好就是我们配置的"*"之前的内容(大胆猜想:经过上面两个过程发现<url-pattern>中进行配置路径的时候,在那么将浏览器发送过来的request封装成HttpServletRequest的时候,会默认的将"*"之前的内容认为是servletPath,其实也就是配置的servletPath的内容)

那么当执行完getPathWithinServletMapping()在返回给getLookupPathForRequest()的时候,只会返回"/getbook"。

  • (4)返回AbstractHandlerMethodMapping#getHandlerInternal(),继续向下执行lookupHandlerMethod(),匹配对应的HandlerMethod

在执行AbstractHandlerMethodMapping#lookupHandlerMethod()的时候,由于形式一传入的lookupPath为"/mvc/getbook",而在mappingRegistry中对应的mapping是"/mvc/getbook",在mappingRegistry中对应的mapping是"/mvc/getbook",所以就可以匹配到对应的业务逻辑方法,完成一次请求。

但是形式二由于传入的lookupPath为"/getbook",而在mappingRegistry中对应的mapping是"/mvc/getbook",所以此时肯定是访问不成功,页面404。

3.解决模糊匹配页面404

在实际项目中,很少会去设置情况一精准匹配,那么在使用情况二模糊匹配的时候,会报出页面404。其实有一种方式可以规避这个问题。

  • (1)增加com.wj.mysql.utils.SetAlwaysUseFullPathForDispatcher来设置HandlerMapping的alwaysUseFullPath为true
public class SetAlwaysUseFullPathForDispatcher {
    @Resource
    public void setHandlerMapping(RequestMappingHandlerMapping mapping){
        mapping.setAlwaysUseFullPath(true);
    }
}
  • (2)在Spring中注册该Bean
    <!--解决路径模糊匹配,页面404-->
    <bean class="com.wj.mysql.utils.SetAlwaysUseFullPathForDispatcher"/>

那么现在 我们在使用情况二模糊匹配来获取执行刚才的(3)执行 org.springframework.web.util.UrlPathHelper#getLookupPathForRequest(request),获取request中的url的时候,此时的alwaysUseFullPath为true,那么会自行执行UrlPathHelper#getPathWithinApplication()的返回完整路径,那么在(4)匹配对应的HandlerMethod的时候,就不会出现匹配不到的情况了。

二 后缀名匹配

 

其实解决上面的情况二模糊匹配除去在3小节提到的一种解决方案之外,其实可以还有另外一种解决方案:

    <servlet-mapping>
        <servlet-name>sql-web</servlet-name>
        <!--第二种解决方式:直接通过添加后缀名的形式来拦截请求。该后缀名任意-->
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

那就是通过匹配后缀名的方式,那么我们相应的请求的链接也要增加相应的后缀名即http://localhost:8080/mvc/getbook.html大胆猜想:这个地方的后缀名是任意字符串都可以,已经验证) 

这个方法的调用过程同形式一精准匹配的形式,因为在UrlPathHelper#getLookupPathForRequest(request)获取的url为完整路径"/mvc/getbook.html",所以传入到AbstractHandlerMethodMapping#lookupHandlerMethod()的时候lookupPath也就是完整路径"/mvc/getbook.html",并且可以根据设置的<url-pattern>找到对应的HandlerMethod。


其实经过上面三个debug过程,发现差别就在于三种不同的方式被封装成HttpServletRequest的时候,获取 getServletPath()的时候返回的内容不同引起的。而这个 getServletPath()返回的就是<url-pattern>除去"*"部分的内容。

三 总结

  • 1.设置<url-pattern>的内容为HttpServletRequest中的 getServletPath()内容,但是除去"*"部分;
  • 2.可以通过设置<url-pattern>为后缀名的方式来匹配对应的请求;
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值