为嘛访问页面老是 404?——url-pattern

1.访问的资源存在,也够权限访问,路径也对,但是就是返回404.

2.在看一些 web项目demo的时候,发现url-pattern的写法虽然能看懂,但是不同写法的它们的处理顺序是一样?

格式

映射到url可以使用通配符‘*’
    格式1:*.扩展名
    格式2:/*结尾

一、servlet容器对url的匹配过程:

    首先,url的匹配是这样的,当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,
    比如我访问的是 http://localhost/test/a.html,我的应用上下文是test,容器会将/a.html部分拿来做servlet的映射匹配。
    url匹配上某个Servlet的时候,就交由这个Servlet来负责处理,就不会去理会剩下的servlet了(需和filter区别开来)。
    这个映射匹配过程是有顺序的。
    其匹配规则和顺序如下:

  1. 精确路径匹配                                                   :/abc,/a/b/c/d.xxx
  2. 最长路径匹配(以 "/" 开头 并以 "/*" 结尾) :/*,/abc/*,/a/b/c/*
  3. 扩展匹配(以前缀 "*." 开头)                        :*.do,*.html,*.xxx
  4. 缺省匹配                                                            :/

    如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。
    如果应用定义了一个default servlet,则容器会将请求丢给default servlet。    
    例:
            servletA映射到/abc/*
            servletB映射到/*
            servletC映射到/abc
            servletD映射到*.do
            servletE映射到/
            
            当请求的url为"/abc/a.html"的时候,首先进行精确路径匹配,发现并不匹配,接着最长路径匹配,
            匹配上了,Servlet引擎将调用servletA,来进行处理,就不会找其他的Servlet。
            
            当请求的url为"/abc"时,首先进行精确路径匹配,Servlet引擎将调用ServletC
            当请求的url为"/abc/a.do"时,Servlet引擎将调用ServletA
            当请求的url为"/a.do"时,Servlet引擎将调用ServletB
            当请求的url为"/xxx/yyy/a.do"时,Servlet引擎将调用ServletD
            当请求的url为"/xxx/yyy/zzz.action"时,Servlet引擎将调用ServletE

    通常 filter url-pattern 会配置为 "/*","/*" 的优先级较高,对于大部分 url 请求都能进行过滤,甚至我们访问 index.jsp 这样的文件都需要走这个映射。对于 servlet 而言,不是希望看到的。对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一个filter。Filter的处理顺序和filter-mapping在web.xml中定义的顺序相同。

二、"/" 和 "/*"

!!!"/" 和 "/*"是两种不同的映射匹配规则(废话,又不瞎)

!!!注意“/*”和“/”均会匹配所有request,拦截静态资源的加载。

  • "/*":最长路径匹配,由于路径匹配的优先级仅次于精确匹配,所以“/*”会覆盖所有的扩展名匹配,

    很多404错误均由此引起,
    比如说,使用SpringMVC的时候,当你映射模式配置为”/*“,访问jsp页面的时候404,原因是路径匹配会优先于扩展匹配,所以这个请求会交给DispatcherServlet,而它找不到一个能处理jsp的类,那能怎么办,索性返回个呵呵吧(404)。
    所以这是一种特别恶劣的匹配模式,一般只用于filter的url-pattern

  • "/":缺省匹配,servlet中特殊的匹配模式,切该模式有且仅有一个实例(只能配置一个),
    优先级最低,不会覆盖其他任何url-pattern,
    只是会替换servlet容器的内建default servlet 。配置“/”后,一种可能的现象是你配置的Servlet会拦截诸如以下格式的请求:
        http://localhost:8080/appDemo/user/addUser.action、
        http://localhost:8080/appDemo/user/updateUser
    但是并不会拦截.jsp结尾的,因为servlet容器有默认的 "*.jsp" Servlet,
    且扩展名匹配的优先级高于缺省匹配,所以才会有上述现象。

    砍下tomcat对web应用默认的部分配置(%TOMCAT_HOME%/conf/web.xml):        


    default
    org.apache.catalina.servlets.DefaultServlet
    
        debug
        0
    
    
        listings
        false
    
    1



    jsp
    org.apache.jasper.servlet.JspServlet
    
        fork
        false
    
    
        xpoweredBy
        false
    
    3




    default
    /




    jsp
    *.jsp
    *.jspx

        上面的就是tomcat默认配置的两个Servlet,
        配置的缺省servlet就是org.apache.catalina.servlets.DefaultServlet这个类,其配置url-pattern就是"/"
        而org.apache.jasper.servlet.JspServlet 来处理以.jsp为后缀的请求。

三、其他

  1. 同一个servlet可以映射多个url上。
  2. 如果在元素中配置了6元素的话,
    那么这个servlet将在服务器启动的时候创建对象,并调用init()方法,数字越小启动的优先级越高。
  3. servlet的线程安全问题
        当多个客户端并发访问同一个servlet的时候,web服务器会为每一个客户端的访问请求创建一个线程,
        在这个线程上调用servlet的service方法,
        如果service方法访问了同一个资源的话就有可能引发线程的安全问题。
        
        那如果你用同步锁或者synchronized同步函数的话,固然安全了,
        但是这个应用就成了单线程访问的了,你得等别人操作完了你才能访问,人数一多,那得等到猴年马月去
        
        在java中有这么些接口它里边什么都没有,只是用来标记的。称之为标记接口。
        SingleThreadModel接口就这么一个用来标记单线的接口。
        
        实现这个接口,多个线程并发访问时,
        服务器找映射的servlet处理当前的请求,当没有完成时又有一个线程需要处理,
        服务器就创建一个新的servlet对象去处理这个请求,也就是说其处理的资源并没有唯一。
        这个方法是非标准的解决方案,也已经过时了,还是采用synchronized。
        
        子类重写父类的方法时,是不允许抛出比父类更多的异常。

有不对之处欢迎大家指正,补充。

四、补充

“/*”“/**” 都是最长路径匹配,区别在于 ”/*“ 匹配一级,“/**” 匹配多级。

五、参考资料

http://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html

https://www.cnblogs.com/solverpeng/p/5729763.html

 

你可能感兴趣的:(为嘛访问页面老是 404?——url-pattern)