Servlet 3.0 之 注解和可插拔性

本章聊一聊ServletContext 3.0規(guī)范中定義的注解以及在web應(yīng)用中使用的框架和庫(kù)的可插拔性的提升。

一、 注解和可插拔性

在一個(gè)web應(yīng)用中,如果使用注解的類(lèi)位于WEB-INF/classes目錄下,或者如果它們被打成jar包并被放到WEB-INF/lib目錄中,使用注解的類(lèi)將自己處理它們的注解。

web應(yīng)用部署描述符在web-app元素上有一個(gè)新的屬性metadata-complete。屬性metadata-complete定義了web描述符是否完整,或者在部署的時(shí)候jar文件中類(lèi)文件是否應(yīng)該被掃描注解和web段。如果屬性metadata-complete被設(shè)置為true,部署工具必須忽略出現(xiàn)在應(yīng)用的類(lèi)文件中和web分段中的任何servlet注解。如果屬性metadata-complete沒(méi)有明確設(shè)置或者被設(shè)置為false,部署工具必須檢查應(yīng)用中類(lèi)文件的注解和掃描web片段。

下列是必須被兼容Servlet 3.0的容器支持的注解。

  1. @WebServlet
    這個(gè)注解用來(lái)定義web應(yīng)用中的一個(gè)Servlet注解。這個(gè)注解應(yīng)用在類(lèi)上,且包含聲明的Servlet的元數(shù)據(jù)。注解上的屬性urlPatternsvalue必須配置。其它屬性可以不用配置,直接使用默認(rèn)值就好。推薦做法是,當(dāng)注解上的屬性?xún)H僅是url pattern時(shí)使用value,當(dāng)注解上還有其它屬性使用時(shí)使用urlPattern。不能在一個(gè)注解上同時(shí)使用valueurlPatterns。如果沒(méi)有指定Servlet的名字,默認(rèn)的名字是全路徑類(lèi)名。被注解的servlet必須指明至少一個(gè)url Pattern。如果相同的servlet類(lèi)在部署描述符中聲明了兩份,但是名字不一樣,那么servlet的新實(shí)例必須被實(shí)例化。如果相同的servlet類(lèi)通過(guò)編程API被添加到ServletContext,那么通過(guò)@WebServlet注解聲明的值必須被忽略,并且要為有具體名字的servlet必須要?jiǎng)?chuàng)建一個(gè)新實(shí)例。

@WebServlet注解的類(lèi)必須繼承javax.servlet.http.HttpServlet。
以下是這個(gè)注解如何被使用的例子。
CODE EXAMPLE 1-1 @WebServlet 注解例子
@WebServlet("/foo")
public class CalculatorServlet extends HttpServlet {
// ......
}
以下是注解如何與更多的屬性一起工作的例子。
CODE EXAMPLE 1-2 @WebServlet 使用其它屬性的例子
@WebServlet(name="MyServlet", urlPattern={"/foo", "/bar"})
public class SampleUsingAnnotationAttributes extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) {
//....
}
}

  1. @WebFilter
    這個(gè)注解用來(lái)定義web應(yīng)用中的Filter。這個(gè)注解應(yīng)用在類(lèi)上,并且包含有關(guān)filter的元數(shù)據(jù)。如果沒(méi)有指明Filter的名字,那么默認(rèn)的名字將是全路徑類(lèi)名。注解的屬性URLPatternservltName,或者value必須被配置。所有其它屬性可選,并且有默認(rèn)值可用。推薦的做法是,當(dāng)注解上僅有url pattern屬性的時(shí)候使用value,當(dāng)還有其它屬性被使用時(shí)使用urlPatterns。不能再同一個(gè)注解上同時(shí)使用valueurlPatterns
    @WebFilter注解的類(lèi)必須繼承javax.servlet.Filter。
    以下是這個(gè)注解如何使用的例子。
    CODE EXAMPLE 1-3 @WebFilter注解例子
    @WebFilter("/foo")
    public class MyFilter implements Filter {
    public void doFilter(HttpServletRequest req, HttpServletResponse res) {
    //......
    }
    }
  2. @WebInitParam
    這個(gè)注解用來(lái)指明任何必須傳遞給Servlet或者Filter的參數(shù)。它是WebServletWebFilter注解的一個(gè)屬性。
  3. @WebListener
    注解WebListener用來(lái)注解一個(gè)監(jiān)聽(tīng)者,以便獲取web應(yīng)用上下文中各種操作的事件。用@WebListener注解的類(lèi)必須實(shí)現(xiàn)下列接口的一個(gè):
  • javax.servlet.ServletContextListener
  • javax.servlet.ServletContextAttributeListener
  • javax.servlet.ServletRequestListener
  • javax.servlet.ServletRequestAttributeListener
  • javax.servlet.http.HttpSessionListener
  • javax.servlet.http.HttpSessionAttributeListener
    一個(gè)例子:
    @WebListener
    public class MyListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();
    sc.addServlet("myServlet", "Sample servlet", "foo.bar.MyServlet", null, -1);
    sc.addServletMapping("myServlet", new String[] { "/urlpattern/*" });
    }
  1. @MultipartConfig
    當(dāng)應(yīng)用在一個(gè)Servlet上,這個(gè)注解表明它期望的請(qǐng)求是mime/multipart類(lèi)型。與servlet對(duì)應(yīng)的HttpServletRequest對(duì)象必須要能夠通過(guò)方法getPartsgetPart來(lái)遍歷不同的mime附件。javax.serlvet.annotation.MultipartConfiglocation屬性和<multipart-config><location>元素會(huì)被解析為絕對(duì)路徑,并且默認(rèn)值是javax.servlet.context.tempdir。如果指定了一個(gè)相對(duì)地址,它會(huì)相對(duì)于tempdir這個(gè)位置。絕對(duì)路徑和相對(duì)路徑的測(cè)試必須通過(guò)java.io.File.isAbsolute 來(lái)完成。
  2. 其它注解和約定
    除了以上注解,章節(jié)注解和資源注入中定義的注解將會(huì)繼續(xù)在這些新注解的上下文中正常工作。
    通常情況下,所有應(yīng)用在welcome-file-list中有index.htm(1)index.jsp。這個(gè)描述符可以用來(lái)重寫(xiě)這些默認(rèn)設(shè)置。
    當(dāng)使用注解時(shí),Listeners和Servlets從各種框架jars文件、WEB-INF/classes或者WEB-INF/lib中的class文件中被加載的順序沒(méi)有明確指明。如果順序很重要,那么看看web.xml的模塊和web.xml及web-fragment.xml的順序。順序僅能在部署描述符中被指定。

二、可插拔性

  1. web.xml的模塊化
    使用上述定義的注解可以不再依賴(lài)web.xml文件。但是為了重寫(xiě)默認(rèn)值或者注解設(shè)置的值,就需要使用部署描述符。就像之前的例子,如果metadata-complete元素在web.xml描述符中設(shè)置為true,那么類(lèi)文件和jars里面web-fragments文件中的注解將不會(huì)被處理。這個(gè)暗示了應(yīng)用中所有的元數(shù)據(jù)都通過(guò)web.xml描述符來(lái)指明。

為了給開(kāi)發(fā)者提供更好的可插拔性和更少的配置,servlet 3.0規(guī)范引入了web模塊化部署描述符片段(web fragment),一個(gè)web片段可以是一個(gè)web.xml的一部分或者全部,它可以被包含在一個(gè)庫(kù)中或者框架jar文件里面META-INF目錄中。WEB-INF/lib目錄里面沒(méi)有web-fragment.xml的普通舊jar也被認(rèn)為是一個(gè)片段。在它里面的所有注解將根據(jù)下面第三小節(jié)定義的規(guī)則被處理。容器將選擇和使用像如下定義的規(guī)則來(lái)做為配置。

一個(gè)web片段以這樣一種方式來(lái)作為web應(yīng)用的一個(gè)邏輯部分--正在web應(yīng)用里面被使用的框架能夠定義所有構(gòu)件,而不需要讓開(kāi)發(fā)者來(lái)編輯或者添加信息到web.xml文件中。它幾乎能夠包含所有web.xml文件中使用的元素。然而描述符的頂級(jí)元素必須是web-fragment, 并且對(duì)應(yīng)的描述符文件必須被命名為web-fragment.xml。相關(guān)元素的順序在web-fragment.xmlweb.xml之間也不一樣。

如果一個(gè)框架被打包為一個(gè)jar文件,且元數(shù)據(jù)是部署描述符的形式,那么web-fragment.xml描述符必須要在jar文件的META-INF/目錄里。

如果一個(gè)框架想要像填充web應(yīng)用的web.xml文件的方式來(lái)填充META-INF/web-fragment.xml,框架必須被綁定在web應(yīng)用的WEB-INF/lib目錄里。為了web應(yīng)用能使用框架其它類(lèi)型的資源(如類(lèi)文件),使用web應(yīng)用的類(lèi)加載器委托鏈就能夠?qū)崿F(xiàn)。換句話(huà)說(shuō),僅有位于web應(yīng)用的WEB-INF/lib目錄下,而不是在類(lèi)加載委托鏈上層目錄的JAR文件需要為web-fragment.xml而被掃描。

容器在部署期間需要負(fù)責(zé)掃描上述指定的位置,并且發(fā)現(xiàn)web-fragment.xml,然后處理它們。單個(gè)web.xml中名字唯一性的要求同樣適用于一個(gè)web.xml和所有web-fragment.xml文件的組合。

一個(gè)庫(kù)或者框架能包含內(nèi)容如下:
<web-fragment>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>WelcomeServlet</servlet-class>
</servlet>
<listener>
<listener-class>RequestListener</listener-class>
</listener>
</web-fragment>
上述web-fragment.xml 文件將會(huì)位于框架的jar文件的META-INF/ 目錄下。來(lái)自web-fragment.xml的配置和注解的順序并未定義。如果對(duì)于web應(yīng)用這個(gè)順序很重要,請(qǐng)參考下面定義的如何獲取特定順序的規(guī)則。

  1. web.xml和web-fragment.xml的順序
    由于規(guī)范允許應(yīng)用配置資源由多個(gè)配置文件(web.xml和web-fragment.xml)組成,它們會(huì)從一個(gè)應(yīng)用中幾個(gè)不同的位置被發(fā)現(xiàn)和加載,那么順序的問(wèn)題必須被重視。本章詳述了怎樣聲明構(gòu)件的順序。
    一個(gè)web-fragment.xml可以有一個(gè)javaee:java-identifierType類(lèi)型的頂級(jí)元素<name>。web-fragment.xml 中僅能有一個(gè)<name>元素。如果一個(gè)<name>元素出現(xiàn),必須考慮到構(gòu)件順序。
    要讓?xiě)?yīng)用配置資源表達(dá)他們的順序,必須考慮兩個(gè)場(chǎng)景:
    1. 絕對(duì)順序:web.xml 中的一個(gè)<absolute-ordering>元素。一個(gè)web.xml中僅能有一個(gè)<absolute-ordering>元素。
    • 需要被下面第二條case處理的有序引用必須被忽略。
    • web.xml文件必須在absolute-ordering 元素列表中的任意一個(gè)web-fragment之前被處理。
    • <absolute-ordering>的任意直接子元素<name>必須被解析,以便明確那些有名字的web-fragments按照絕對(duì)順序被處理。
    • <absolute-ordering>元素可以包含0個(gè)或者1一個(gè)<others />元素。這個(gè)元素對(duì)應(yīng)的行為如下。如果<absolute-ordering> 元素不包含一個(gè)<others />元素,任何沒(méi)有在<name />元素中具體提到的web-fragment必須被忽略。外部的jars不會(huì)被掃描帶注解的servlets,filters,或者listeners。然而,如果來(lái)自外部jar的servlet,filter,或者listener被列在web.xml或者一個(gè)非外部的web-fragment中,那么除非被metadata-complete 排除在外,它的注解將會(huì)被掃描。
      在外部jars的TLD文件中發(fā)現(xiàn)的ServletContextListeners不能使用編程方式配置filters和servlets。任何試圖如此的行為都會(huì)拋出IllegalStateException。如果發(fā)現(xiàn)的ServletContainerInitializer 是從外部jar中加載的,它將會(huì)被忽略。外部的jars不會(huì)為任何ServletContainerInitializer 處理的類(lèi)而被掃描。
    • 重復(fù)名字異常:當(dāng)轉(zhuǎn)換<absolute-ordering>的子元素時(shí),如果多個(gè)子元素有相同的<name>元素,僅有遇到第一個(gè)出現(xiàn)的會(huì)被處理。
  2. 相對(duì)順序:web-fragment.xml里面的<ordering>元素。一個(gè)web-fragment.xml 文件中僅有一個(gè)<ordering>
* 一個(gè)*web-fragment.xml*可以有一個(gè)*<ordering>*元素。如果這樣,這個(gè)元素必須包含0個(gè)或者一個(gè)*<before>*元素以及0個(gè)或者一個(gè)*after*元素。這些元素的意義在下面解釋。
* 重復(fù)名字異常:當(dāng)轉(zhuǎn)換web-fragments時(shí),如果有相同*<name>*元素的多個(gè)成員,應(yīng)用必須記錄一個(gè)錯(cuò)誤信息來(lái)幫助修復(fù)這個(gè)問(wèn)題,而且應(yīng)用必須報(bào)錯(cuò)并停止部署。比如,對(duì)于使用者,修復(fù)這個(gè)問(wèn)題的一種方式是使用絕對(duì)順序,這種情況下相對(duì)路徑將會(huì)失效。
* 考慮如下簡(jiǎn)潔但是有說(shuō)明性的例子。3個(gè)web-fragments-MyFragment1,MyFragment2和MyFragment3是包含*web.xml*應(yīng)用的一部分。
 web-fragment.xml
        <web-fragment>
          <name>MyFragment1</name>
          <ordering><after><name>MyFragments</name></after></ordering>
            ...
         </web-fragment>

web-fragment.xml
<web-fragment>
<name>MyFragment2</name>
..
</web-fragment>
web-fragment.xml
<web-fragment>
<name>MyFragment3</name>
<ordering><before><others/></before></ordering>
..
</web-fragment>
web.xml
<web-app>
...
</web-app>
上面這個(gè)例子中,處理的順序如下:
1. web.xml
1. MyFragment3
1. MyFragment2
1. MyFragment1

    前面的例子說(shuō)明了下列原則中的一些而不是全部。
    * <before>意味著文檔在匹配嵌套<name>元素里名字的文件前必須有序。
    * <after>意味著文檔在匹配嵌套<name>元素里名字的文件后必須有序。
    * 有一個(gè)特殊的元素<other/>,它可以被包含在<before>或者<after>里面0次或者1次;或直接被包含<absolute-ordering>元素中0次或者1次。<others/>元素必須按如下處理:
      * 如果<before>元素包含一個(gè)嵌套的<others/>,這個(gè)文檔將會(huì)被移動(dòng)到有序文檔列表的開(kāi)頭。如果有以<before><others/>開(kāi)頭的多個(gè)文檔,它們將會(huì)放在有序文檔列表的開(kāi)頭,但是文檔組內(nèi)的順序并未指定。
      * 如果<after>元素包含一個(gè)嵌套<others/>,文檔將被移動(dòng)到有序文檔列表的末尾。如果有多個(gè)需要<after><others/>的文檔它們將會(huì)被移動(dòng)到有序文檔的末尾,但是這些文檔組內(nèi)部的順序并未指定。
      * 在第一個(gè)<before>或者<after>元素中,如果出現(xiàn)了<others/>元素,但是在它的父級(jí)中并不是只有<name>,父級(jí)元素中的其它元素必須認(rèn)為是按順序處理。
      * 如果<others/>元素直接出現(xiàn)在<absolute-ordering>元素中,運(yùn)行時(shí)必須確保<absolute-ordering>塊中任何未明確命名的web-fragments在那個(gè)點(diǎn)按照處理順序被包含進(jìn)來(lái)。
    * 如果一個(gè)web-fragment.xml文件沒(méi)有一個(gè)<ordering>或者web.xml沒(méi)有<absolute-ordering>元素,那么構(gòu)建就被認(rèn)為沒(méi)有任何順序依賴(lài)。
    * 如果運(yùn)行時(shí)發(fā)現(xiàn)循環(huán)引用,必須記錄一條消息,并且應(yīng)用必須立即報(bào)錯(cuò),停止部署。用戶(hù)可以采用一個(gè)方式是在web.xml中使用絕對(duì)順序。
    * 當(dāng)web.xml包含一個(gè)有序部分,之前的例子能夠被擴(kuò)展來(lái)說(shuō)明這個(gè)case:

web.xml
<web-app>
<absolute-ordering>
<name>MyFragment3</name>
<name>MyFragment2</name>
</absolute-ordering>
...
</web-app>
這個(gè)例子中,不同元素的順序?qū)?huì)是
* web.xml
* MyFragment3
* MyFragment2

      一些額外的示例場(chǎng)景描述如下。所有這些例子都應(yīng)用于相對(duì)順序和非絕對(duì)順序:

Document A
<after>
<others/>
<name>C</name>
</after>

      Document B
          <before>
            <others/>
          </before>

      Document C
          <after>
            <others/>
          </after>

      Document D: no ordering
      Document E: no ordering
      Document F:
          <before>
            <others/>
            <name>name</name>
          </before>

結(jié)果解析順序:
web.xml, F, B, D, E, C, A.
Document<no id>:
<after>
<others/>
</after>
<before>
<name>C</name>
</before>
Document B:
<before>
<others/>
</before>
Document D:
<after>
<others/>
</after>
Document E:
<before>
<others/>
</before>
Document D: no ordering
結(jié)果解析可能是下列中的一種:
* B, E, F, <no id>, C, D
* B, E, F, <no id>, D, C
* E, B, F, <no id>, C, D
* E, B, F, <no id>, D, C
* E, B, F, D, <no id>, C
* E, B, F, D, <no id>, D

      Document A:
          <after>
              <name>B</name>
          </after>

Document B: no ordering
Document C:
<before>
<others/>
</before>
Document D: no ordering: no ordering
結(jié)果解析:C, B, D, A. 解析順序也可能是:C, D, B, A或者C, B, A, D.

  1. 組裝來(lái)自web.xml,web-fragment.xml和annotations的描述符
    如果對(duì)于一個(gè)應(yīng)用來(lái)說(shuō),listener,servlets和filters被調(diào)用的順序很重要,那么必須使用部署描述符。而且必要的時(shí)候,上述有序元素可以被使用。如上述,當(dāng)使用注解來(lái)定義listeners,servlets和filters,它們被調(diào)用的順序就未知了。下面是對(duì)于一個(gè)應(yīng)用,組裝最后部署描述符的規(guī)則集合:
  2. 如果listeners,servlets,filters相關(guān)聯(lián),那么它們之間的順序必須在web.xml或者web-fragment.xml中指明。
  3. 順序?qū)⒒谒鼈冊(cè)诿枋龇斜欢x,web.xml中的absolute-ordering元素或者web-fragment.xml中的ordering元素的順序。
* 匹配請(qǐng)求的filters按照它們?cè)趙eb.xml中聲明的順序組織成鏈。
* servlets會(huì)在請(qǐng)求處理時(shí)通過(guò)懶加載初始化或者在部署階段直接初始化。在后面一個(gè)case中,它們按照*load-on-startup*元素中指明的順序初始化。
* 在這個(gè)規(guī)范發(fā)行之前,上下文listeners按隨機(jī)順序被調(diào)用。在Servlet 3.0中,listeners按照它們?cè)趙eb.xml中聲明的順序被調(diào)用:
    * *javax.servlet.ServletContextListener*的實(shí)現(xiàn)在它們的*contextInitialized*按照它們被聲明的順序被調(diào)用,而在*contextDestroyed* 方法里按照相反的順序被調(diào)用。
    * *javax.servlet.ServletRequestListener*的實(shí)現(xiàn)在它們的*requestInitialized*按照它們被聲明的順序被調(diào)用,而在*requestDestroyed* 方法里按照相反的順序被調(diào)用。
    * *javax.servlet.http.HttpSessionListener*的實(shí)現(xiàn)在它們的*sessionCreated*按照它們被聲明的順序被調(diào)用,而在*sessionDestroyed*  方法里按照相反的順序被調(diào)用。
    * 任何其它listener接口的調(diào)用順序都不能確定。
  1. 如果一個(gè)servlet在web.xml中使用enabled元素來(lái)disabled掉,那么這個(gè)servlet在為servlet指定的url-pattern上不會(huì)生效。
  2. 當(dāng)解決web.xml, web-fragment.xml和注解之間的沖突時(shí),web應(yīng)用的web.xml有最高優(yōu)先級(jí)。
  3. 如果metadata-complete在描述符中未指定,或者在部署描述符中被設(shè)置為false,應(yīng)用的有效元數(shù)據(jù)來(lái)自注解和描述符的并集。合并的規(guī)則如下:
* web fragments中的配置設(shè)置用來(lái)增強(qiáng)主web.xml中設(shè)置,就好像它們?cè)谕粋€(gè)web.xml中配置的一樣。
* web fragments中的配置設(shè)置被添加到主web.xml的順序如上述章節(jié)描述一樣-*web.xml和web-fragment.xml的順序*。
* 在web.xml中當(dāng)*metadata-complete*屬性被設(shè)置為true時(shí),它就被認(rèn)為是完整的,并且注解和fragment的掃描將不會(huì)在部署時(shí)期出現(xiàn)。如果*absolute-ordering*和*ordering*元素出現(xiàn),那么它們將會(huì)被忽略。如果在fragment中設(shè)置為true,*metadata-complete*屬性?xún)H應(yīng)用在那個(gè)特殊jar的注解掃描上。
* 除非*metadata-complete*被設(shè)置為true,那么web fragment被合并進(jìn)主web.xml。這個(gè)合并發(fā)生在對(duì)應(yīng)fragment上注解處理之后。
* 當(dāng)用web fragments增強(qiáng)web.xml時(shí),下列將被認(rèn)為是配置沖突:
    * 多個(gè)*init-param*元素,有相同的*param-name*,但是有不同的*param-value*。
    * 多個(gè)*mime-mapping*元素,有相同的*extension*,但是不同的*mime-type*。
* 上述配置沖突這樣來(lái)解決:
    * web.xml和web fragment之間有配置沖突,那么web.xml中的配置生效。
    * 在兩個(gè)web fragments之間的沖突,而且沖突元素并不出現(xiàn)在web.xml中,將會(huì)導(dǎo)致一個(gè)錯(cuò)誤。這時(shí)應(yīng)該記錄錯(cuò)誤信息,而且應(yīng)用必須失敗,然后停止部署。
* 在上述沖突解決之后,這些額外的規(guī)則會(huì)被應(yīng)用:
    * 那些可以被聲明任意多次的元素最后會(huì)從各個(gè)web-fragment匯總累加到web.xml。比如,*<context-param>*元素有不同的*<param-name>*累加。
    * 那些可以被聲明多次的元素,如果它們?cè)趙eb.xml有,那么web.xml中的值會(huì)覆蓋web-fragments中的的同名元素。
    * 如果一個(gè)最多會(huì)出現(xiàn)一次的元素出現(xiàn)在一個(gè)web fragment中,而沒(méi)有出現(xiàn)在主web.xml中,那么主web.xml會(huì)繼承web fragment中的設(shè)置。如果元素在主web.xml和一個(gè)web fragment中同時(shí)出現(xiàn),web.xml中的配置優(yōu)先級(jí)最高。比如,如果主web.xml和一個(gè)web fragment同時(shí)聲明了相同的servlet,并且web fragment中的servlet聲明指明了一個(gè)*<load-on-startup>*元素,但是web.xml中沒(méi)有對(duì)應(yīng)的元素,那么來(lái)自web fragment的*<load-on-startup>*元素將會(huì)在合并的web.xml中被使用。
    * 如果最多能出現(xiàn)一次的元素在不同的web fragment中都出現(xiàn)了,而沒(méi)有出現(xiàn)在主web.xml,這將會(huì)被認(rèn)為是個(gè)錯(cuò)誤。比如,如果兩個(gè)web fragment聲明了同樣的servlet,但是有不同的*<load-on-startup>*元素;并且同樣的servlet在主web.xml也被聲明了,但是沒(méi)有*<load-on-startup>*元素,那么必須拋出一個(gè)錯(cuò)誤。
    * *<welcome-file>*聲明是累加的。
    * 有相同*<servlet-name>*的*<servlet-mapping>*元素在web-fragments之間是累加的。web.xml中的*<servlet-name>*元素會(huì)重寫(xiě)web-fragment中相同*<servlet-name>*的元素。
    * 有相同*<listener-class>*的多個(gè)*<listener>*元素被認(rèn)為是單個(gè)*<listener>*聲明。
    * 僅當(dāng)所有的web fragments被標(biāo)記為*<distributable>*,合并后的web.xml被認(rèn)為是<distributable>。
    * 一個(gè)web fragment的頂層*<icon>*及它的子元素,*<display-name>*,*<description>*元素將會(huì)被忽略。
    * jsp-property-group是累加的。當(dāng)綁定一個(gè)jar文件的*META-INF/resources*目錄中的靜態(tài)資源時(shí),推薦*jsp-config*元素使用**url-pattern**,而不是擴(kuò)展映射。如果一個(gè)fragment存在更多JSP資源,那么應(yīng)該放在一個(gè)子目錄中。這有助于防止一個(gè)web fragment的*jsp-property-group*元素影響到應(yīng)用的主文檔中的JSPs,且防止影響到一個(gè)fragment的*META-INF/resources*目錄中的JSPs。
  * 下列規(guī)則應(yīng)用于所有資源引用元素(env-entry, ejb-ref, ejb-local-ref, service-ref, resource-ref, resource-env-ref, message-destination-ref, persistence-context-ref和persistence-unit-ref):
      * 如果任何資源引用元素出現(xiàn)在一個(gè)web fragment,并且不出現(xiàn)在web.xml中,那么主web.xml繼承web fragment中的值。如果元素同時(shí)出現(xiàn)在web.xml和web fragment中,且有相同的名字,web.xml有最高優(yōu)先級(jí)。除了下面將描述的*injection-target*元素,沒(méi)有fragment的子元素會(huì)合并到主web.xml。比如,如果web.xml和web fragments都聲明了有相同*<resouce-ref-name>*的*<resource-ref>*元素,來(lái)的web.xml的*<resource-ref>*將會(huì)被使用,但是除了下述*<injection-target>*,沒(méi)有其它子元素會(huì)從fragment合并進(jìn)來(lái)。
    
    * 如果一個(gè)資源引用元素在兩個(gè)fragments中配置,同時(shí)并沒(méi)有在web.xml中配置,并且資源引用元素的的所有屬性和子元素都一樣,那么資源引用將會(huì)被合并進(jìn)主web.xml。如果資源引用元素在兩個(gè)fragment中有相同的名字,同時(shí)并不出現(xiàn)在主web.xml中,并且在兩個(gè)fragment中屬性和子元素都不相同,將會(huì)被認(rèn)為是一個(gè)錯(cuò)誤。錯(cuò)誤必須被報(bào)告出來(lái)且應(yīng)用必須失敗并停止部署。比如,如果兩個(gè)fragment聲明了有相同*<resource-ref-name>*的*<resource-ref>*元素,但是其中一個(gè)的類(lèi)型是*javax.sql.DataSource*,而另外一個(gè)中的類(lèi)型是一個(gè)*JavaMail* 資源,它會(huì)是個(gè)錯(cuò)誤且應(yīng)用應(yīng)該報(bào)錯(cuò)并停止部署。
    * 對(duì)于資源引用,來(lái)自fragment且有同名*<injection-target>*元素的元素將會(huì)被合并進(jìn)主web.xml。
  * 除了上述針對(duì)web-fragment.xml的合并規(guī)則,當(dāng)使用資源引用注解(*@Resource,@Resources,@EJB,@EJBs,@WebServiceRef,@WebServiceRefs,@PersistenceContext, @PersistenceContexts, @PersistenceUnit和@PersistenceUnits*)時(shí),下列規(guī)則將會(huì)被應(yīng)用。

如果一個(gè)資源引用被應(yīng)用在一個(gè)類(lèi)上,它等價(jià)于定義了一個(gè)資源,然而,它并不等價(jià)于定義一個(gè)injection-target。上述規(guī)則應(yīng)用于這個(gè)場(chǎng)景中的inject-target元素中。
如果一個(gè)資源引用被應(yīng)用于一個(gè)域上,等價(jià)于在web.xml中定義了一個(gè)injection-target元素。然而,如果在描述符中沒(méi)有injection-target元素,那么來(lái)自fragments的injection-target 元素將會(huì)被合并進(jìn)web.xml。
如果web.xml中有一個(gè)injection-target,以及有一個(gè)有相同資源名字的資源引用注解,那么它被認(rèn)為是對(duì)資源引用注解的重寫(xiě)。這種情況由于有一個(gè)injection-target在描述符中,除了為資源引用注解重寫(xiě)值,上述定義的規(guī)則將會(huì)應(yīng)用。
* 如果一個(gè)data-source元素在兩個(gè)fragment中被指定,同時(shí)不出現(xiàn)在主web.xml中,并且data-source元素的所有屬性和子元素都一樣,data-source元素將會(huì)被合并進(jìn)主web.xml。如果data-source元素在兩個(gè)fragment中有相同的名字,同時(shí)不出現(xiàn)在web.xml并且在兩個(gè)fragment中屬性和子元素不一樣,那么將會(huì)被認(rèn)為是一個(gè)錯(cuò)誤。這種情況下,一個(gè)錯(cuò)誤必須被報(bào)告出來(lái)且應(yīng)用必須立即失敗并停止部署。
下面是一些展示不同情況下結(jié)果的例子。

    CODE EXAMPLE 8-4

web.xml - 沒(méi)有resource-ref定義
Fragment 1
web-fragment.xml
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<injection-target-class>
com.foo.Bar.class
</injection-target-class>
<injection-target-name>
baz
</injecttion-target-name>
</injection-target>
</resource-ref>
有效的元數(shù)據(jù)將會(huì)是:
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<inject-target-class>
com.foo.Bar.class
</inject-target-class>
<injection-target-name>
baz
</injection-target-name>
</inject-target>
<resource-ref>
CODE EXAMPLE 8-5
web.xml
<resource-ref>
<resource-ref-name="foo">
...
</resource-ref>
Fragment 1
web-fragment.xml
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<injection-target-class>
com.foo.Bar.class
</injection-target-class>
<injection-target-name>
baz
</injection-target-name>
</injection-target>
</resource-ref>
Fragment 2
web-fragment.xml
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<injection-target-class>
com.foo.Bar2.class
</injection-target-class>
<injection-target-name>
baz2
</injection-target-name>
</injection-target>
</resource-ref>
有效的元數(shù)據(jù)將是:
<resource-ref>
<resource-ref-name="foo">
...
<inject-target>
<injection-target-class>
com.foo.Bar.class
</injection-target-class>
<injection-target-name>
baz
</injection-target-name>
</inject-target>
<injection-target>
<injection-target-class>
com.foo.Bar2.class
</injection-target-class>
<injection-target-name>
baz2
</injection-target-name>
</injection-target>
</resource-ref>
CODE EXAMPLE 8-6
<resource-ref>
<resource-ref-name="foo">
<injection-target>
<injection-target-class>
com.foo.Bar3.class
</injection-target-class>
<injection-target-name>
baz3
</injection-target-name>
...
</resource-ref>
Fragment 1
web-fragment.xml
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<injection-target-class>
com.foo.Bar.class
</injection-target-class>
<injection-target-name>
baz
</injection-target-name>
</injection-target>
</resource-ref>
Fragment 2
web-fragment.xml
<resource-ref>
<resource-ref-name="foo">
...
<injection-target>
<injection-target-class>
com.foo.Bar2.class
</injection-target-class>
<injection-target-name>
baz2
</injection-target-name>
</injection-target>
</resource-ref>
有效的元數(shù)據(jù)如下:
<resource-ref>
<resource-ref-name="foo">
<injection-target>
<injection-target-class>
com.foo.Bar3.class
</injection-target-class>
<injection-target-name>
baz3
</injection-target-name>
<injection-target-class>
com.foo.Bar.class
</injection-target-class>
<injection-target-name>
baz
</injection-target-name>
<injection-target-class>
com.foo.Bar2.class
</injection-target-class>
<injection-target-name>
baz2
</injection-target-name>
..
<resource-ref>
來(lái)自fragment 1和2的<injection-target>將會(huì)被合并到主web.xml。
* 如果主web.xml沒(méi)有配置任何<post-construct>元素,并且web-fragment沒(méi)有指定<post-construct>,那么來(lái)自fragment的<post-construct>元素將被合并到主web.xml。然而,在主web.xml中至少指定了一個(gè)<post-construct>元素,那么來(lái)自fragment的<post-construct>元素將不會(huì)被合并。web.xml的作者負(fù)責(zé)保證<post-construct>列表完成。
* 如果主web.xml沒(méi)有指定任何<pre-destroy>元素,并且web-fragment指定了<pre-destroy>,那么來(lái)自fragment的<pre-destroy>元素將會(huì)被合并進(jìn)主web.xml。然而,如果在主web.xml至少有一個(gè)<pre-destroy>,那么來(lái)自fragment的<pre-destroy>將不會(huì)被合并。web.xml的作者負(fù)責(zé)保證<pre-destroy>列表完成。
* 在處理web-fragment.xml之后,在處理下一個(gè)fragment之前,來(lái)自對(duì)應(yīng)fragment的注解將會(huì)被處理來(lái)為fragment完成有效元數(shù)據(jù)。下列規(guī)則被用來(lái)處理注解:
* 任何通過(guò)并不出現(xiàn)在描述符中的注解指定的元數(shù)據(jù)將會(huì)被用來(lái)擴(kuò)展有效描述符。
3. 主web.xml或者一個(gè)web fragment中的配置比注解的優(yōu)先級(jí)更高。
3. 對(duì)于通過(guò)@WebServlet注解定義的一個(gè)servlet,為了通過(guò)描述符重寫(xiě)值,描述符中servlet的名字必須匹配通過(guò)注解(如果沒(méi)有通過(guò)注解指定,那么顯示指定或者默認(rèn)名字)指定的servlet的名字。
3. 如果初始化參數(shù)的名字匹配通過(guò)注解指定的名字,那么通過(guò)注解為servlets何filter定義的初始化參數(shù)將會(huì)在描述符中的被重寫(xiě)。初始化參數(shù)在注解和描述符之間會(huì)累加。
3. 當(dāng)在描述符中為給定的servlet名字指定了url-patterns,它將會(huì)重寫(xiě)通過(guò)注解配置的url patterns。
3. 對(duì)于通過(guò)@WebFilter注解定義的一個(gè)filter,為了重寫(xiě)通過(guò)描述符的值,描述符中filter的名字必須匹配通過(guò)注解(如果沒(méi)有通過(guò)注解指定一個(gè),那么顯示指定或者默認(rèn)值)指定的filter的名字。
3. 當(dāng)在描述符中為一個(gè)指定的filter配置一個(gè)url-patterns時(shí)候,它會(huì)重寫(xiě)通過(guò)注解指定的url patterns。
3. 當(dāng)在描述符中為一個(gè)指定的filter名字指定了一個(gè)DispatcherTypes,它會(huì)重寫(xiě)通過(guò)注解指定的DispatcherTypes。
3. 下面的例子說(shuō)明了一些上述規(guī)則-
通過(guò)一個(gè)注解聲明的servlet并且與描述符中對(duì)應(yīng)的web.xml一起打包。
@WebServlet(urlPatterns="/MyPattern",initParams={@WebInitParam(name="ccc", value="333")})
public class com.acme.Foo extends HttpServlet
{
...
}
web.xml
<servlet>
<servlet-class>com.acme.Foo</servlet-class>
<servlet-name>Foo</servlet-name>
<init-param>
<param-name>aaa</param-name>
<param-value>111</param-value>
</init-param>
</servlet>
<servlet>
<servlet-class>com.acme.Foo</servlet-class>
<servlet-name>Fum</servlet-name>
<init-param>
<param-name>bbb</param-name>
<param-value>222</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/foo/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Fum</servlet-name>
<url-pattern>/fum/
</url-pattern>
</servlet-mapping>
由于通過(guò)注解聲明的servlet的名字并不匹配web.xml中聲明的servlet的名字,除了web.xml中的其它聲明,注解指定了一個(gè)新的servlet聲明,并且等價(jià)于:
<servlet>
<servlet-class>com.acme.Foo</servlet-class>
<servlet-name>com.acme.Foo</servlet-name>
<init-param>
<param-name>ccc</param-name>
<param-value>333</param-name>
</init-param>
</servlet>
如果上述web.xml用以下內(nèi)容代替:
<servlet>
<servlet-class>com.acme.Foo</servlet-class>
<servlet-name>com.acme.Foo</servlet-name>
<init-param>
<param-name>aaa</param-name>
<param-value>111</param-value> </init-param>
</servlet>
<servlet-mapping>
<servlet-name>com.acme.Foo</servlet-name>
<url-pattern>/foo/</url-pattern>
</servlet-mapping>
有效描述符將會(huì)等價(jià)于:
<servlet>
<servlet-class>com.acme.Foo</servlet-class>
<servlet-name>com.acme.Foo</servlet-name>
<init-param>
<param-name>aaa</param-name>
<param-value>111</param-value>
</init-param>
<init-param>
<param-name>ccc</param-name>
<param-value>333</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>com.acme.Foo</servlet-name>
<url-pattern>/foo/
</url-pattern>
</servlet-mapping>

  1. 共享庫(kù)/運(yùn)行時(shí)插件
    除了支持fragments和注解的使用,其中一個(gè)要求是我們不僅能插進(jìn)綁定在WEB-INF/lib中的一些東西,而且插件共享框架副本-包括能夠插進(jìn)web容器,如構(gòu)建在web容器之上的JAX-WS,JAX-RS和JSF。ServletContainerInitializer 允許處理此種情況。

容器會(huì)在容器或者應(yīng)用啟動(dòng)時(shí)刻,通過(guò)jar服務(wù)API查找ServletContainerInitializer 的一個(gè)實(shí)例。提供ServletContainerInitializer的實(shí)現(xiàn)的框架必須在jar文件的META-INF/services目錄下捆綁叫做javax.servlet.ServletContainerInitializer的文件,作為每個(gè)jar服務(wù)API,它指向ServletContainerInitializer的實(shí)現(xiàn)類(lèi)。

除了ServletContainerInitializer,我們也有一個(gè)注解-HandlesTypes。在ServletContainerInitializer的實(shí)現(xiàn)上,HandlesTypes注解用來(lái)表達(dá)對(duì)那些可以有在HandlersTypes的值里指定的注解(類(lèi)型,方法或者域級(jí)注解)的類(lèi)感興趣,或者如果它在類(lèi)的超類(lèi)的任意位置擴(kuò)展/實(shí)現(xiàn)了那些類(lèi)。容器使用HandlersTypes注解來(lái)決定何時(shí)調(diào)用初始化的onStartup方法。當(dāng)檢測(cè)一個(gè)應(yīng)用的類(lèi)來(lái)看它們是否匹配通過(guò)ServletContainerInitializerHandlersTypes注解指定的任意標(biāo)準(zhǔn)時(shí),如果缺少一個(gè)或者多個(gè)應(yīng)用的可選JAR文件,容器可以在類(lèi)加載問(wèn)題中執(zhí)行。由于容器不是在一個(gè)點(diǎn)決定這些加載失敗的類(lèi)的類(lèi)型是否會(huì)阻礙應(yīng)用正常工作,容器必須忽略它們,同時(shí)提供一個(gè)配置選項(xiàng)來(lái)記錄這些錯(cuò)誤日志。

如果ServletContainerInitializer的一個(gè)實(shí)現(xiàn)沒(méi)有@HandlersTypes注解,或者如果沒(méi)有匹配任意指定的HandlersType,那么它會(huì)為每一個(gè)應(yīng)用被調(diào)用一次,且集合的值為null。這允許初始化來(lái)決定基于應(yīng)用中的可用資源來(lái)判斷它是否需要初始化一個(gè)servlet或者filter。

當(dāng)在任意listener的事件調(diào)用之前一個(gè)應(yīng)用起來(lái)時(shí),ServletContainerInitializeronStartup方法將被調(diào)用。

ServletContainerInitializeronStartup方法獲得一個(gè)類(lèi)的集合,這些類(lèi)或者繼承/實(shí)現(xiàn)初始化程序感興趣的類(lèi),或者通過(guò)@HandlesTypes注解配置了。

下面是一個(gè)具體的例子來(lái)說(shuō)明這如何工作。

我們拿JAX-WS服務(wù)運(yùn)行時(shí)來(lái)說(shuō)。

JAX-WS運(yùn)行時(shí)的實(shí)現(xiàn)通常不捆綁在每個(gè)war文件中。這個(gè)實(shí)現(xiàn)會(huì)捆綁一個(gè)ServletContainerInitializer的實(shí)現(xiàn),并且容器將會(huì)使用服務(wù)API(jar文件在META-INF/services目錄中捆綁到一個(gè)文件,這個(gè)文件被稱(chēng)為javax.servlet.ServletContainerInitializer,且指向JAXWSServletContainerInitializer)來(lái)查找它們。

 @HandlesTypes(WebService.class)
 JAXWSServletContainerInitializer implements ServletContainerInitializer
 {
    public void onStartup(Set<Class<?>> c, ServletContext ctx)throws ServletException {
    // JAX-WS specific code here to initialize the runtime 
    // and setup the mapping etc.
    ServletRegistration reg = ctx.addServlet("JAXWSServlet","com.sun.webservice.JAXWSServlet");
    reg.addServletMapping("/foo"); 
}

框架jar文件也能被捆綁在war文件的WEB-INF/lib目錄下。如果ServletContainerInitializer在一個(gè)JAR文件中被捆綁到一個(gè)應(yīng)用的WEB-INF/lib的目錄下,它的onStartup方法在應(yīng)用啟動(dòng)階段僅被調(diào)用一次。另一方面,如果ServletContainerInitializer在JAR文件中被捆綁到WEB-INF/lib目錄下,但是仍然沒(méi)有被運(yùn)行時(shí)服務(wù)提供者查找機(jī)制發(fā)現(xiàn),它的onStartup方法將在每次應(yīng)用開(kāi)始時(shí)都被調(diào)用。

ServletContainerInitializer接口的實(shí)現(xiàn)將會(huì)被運(yùn)行時(shí)服務(wù)查找機(jī)制,或者與它等價(jià)的容器具體機(jī)制發(fā)現(xiàn)。無(wú)論哪種情況下,來(lái)自被從絕對(duì)順序中排除的web fragment JAR文件的ServletContainerInitializer服務(wù)必須被忽略,并且這些服務(wù)被發(fā)現(xiàn)的順序必須遵循應(yīng)用的類(lèi)加載委托模型。

JSP容器可插入性

通過(guò)讓Servlet容器僅負(fù)責(zé)解析web.xml和web-fragment.xml資源,且把Tag Library Descriptor(TLD)委托給JSP容器,ServletContainerInitializer和項(xiàng)目注冊(cè)的特點(diǎn)在Servlet和JSP容器之間提供了清晰的職責(zé)隔離。

之前,一個(gè)web容器必須為任何listener聲明掃描TLD資源。在Servlet 3.0中,這個(gè)職責(zé)可以委托給JSP容器。嵌入到兼容Servlet 3.0的Servlet容器的JSP容器可以提供它自己的ServletContainerInitializer 實(shí)現(xiàn),可以搜索為任何TLD資源傳遞給onStartupServletContext,可以為listener聲明掃描那些資源,以及用ServletContext注冊(cè)對(duì)應(yīng)的listeners。

除此之外,在Servlet 3.0之前,JSP容器通常會(huì)為了任何jsp-config相關(guān)的配置去掃描一個(gè)應(yīng)用的部署描述符。在Servlet 3.0,Servlet容器必須通過(guò)ServletContext.getJspConfigDescriptor方法準(zhǔn)備好來(lái)自應(yīng)用的web.xml和web-fragment.xml部署描述符的任何jsp-config 相關(guān)的配置。

任何在TLD中被發(fā)現(xiàn)以及通過(guò)編程方式注冊(cè)的ServletContextListener在它們提供的功能中會(huì)受限制。任何嘗試在它們之上去調(diào)用在Servlet 3.0中添加的ServletContext API方法會(huì)導(dǎo)致一個(gè)UnsupportedOperationException。

此外,兼容Servlet 3.0的Servlet容器必須提供一個(gè)有javax.servlet.context.orderedLibs 名字的ServletContext 屬性,它的值(java.util.List<java.lang.String>類(lèi)型)包含一個(gè)被ServletContext代表的應(yīng)用WEB-INF/lib目錄里JAR文件的名字列表,它會(huì)按照它們的web fragment名字(如果fragmentJAR文件被排除在absolute-ordering就會(huì)被排除)排序,或者如果應(yīng)用沒(méi)有指明任何絕對(duì)或相對(duì)順序就為null。

處理注解和fragments

Web應(yīng)用可能同時(shí)包含注解和web.xml/web-fragment.xml部署描述符。如果沒(méi)有部署描述符或者有一個(gè)但是沒(méi)有把metadata-complete設(shè)置為true,那么web.xml,web-fragment.xml和注解必須被處理。下列表格描述了是否需要處理注解和web.xml fragments
TABLE 8-1 注解和web fragment處理要求

Deployment descriptor metadata-complete process annotations
and web fragments
web.xml 2.5 yes no
web.xml 2.5 no yes
web.xml 3.0 yes no
web.xml 3.0 no yes

翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Web應(yīng)用被應(yīng)用開(kāi)發(fā)者創(chuàng)建,這些開(kāi)發(fā)者給予、賣(mài)應(yīng)用,或者為了安裝到一個(gè)運(yùn)行時(shí)環(huán)境,把應(yīng)用轉(zhuǎn)移應(yīng)用到一個(gè)部署者。應(yīng)用...
    Lucky_Micky閱讀 2,680評(píng)論 0 1
  • 本章為部署描述符中Web容器支持詳細(xì)描述Java Servlet 規(guī)范3.0版本要求。部署描述符在應(yīng)用開(kāi)發(fā)者,應(yīng)用...
    Lucky_Micky閱讀 2,127評(píng)論 0 0
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,759評(píng)論 11 349
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,254評(píng)論 6 342

友情鏈接更多精彩內(nèi)容