可抽換性


在 Servlet 3.0 之後,可以使用標註來設定 Servlet 的相關資訊,實際上,Web 容器並不僅讀取 /WEB-INF/classes 中的 Servlet 標註訊息,如果一個 JAR 檔案中有使用標註的 Servlet,Web 容器也可以讀取標註資訊、載入類別並註冊為 Servlet 進行服務。

實際上,在 Servlet 3.0 之後,一個 JAR 檔案確實可以當作一個 Web 應用程式的部份模組,就如上面所說明的,事實上不僅是 Servlet,傾聽器、過濾器等,也可以在撰寫、 定義標註完畢後,封裝在 JAR 檔案中,視需要放置至 Web 應用程式的 /WEB-INF/lib 之中,以增減 Web 應用程式的功能性。

一個 JAR 檔案中,除了可使用標註定義的 Servlet、傾聽器、過濾器之外,也可以擁有自己的部署描述檔,這個檔案的名稱是 web-fragment.xml,必須放置在 JAR 檔案中的 META-INF 目錄之中,基本上,web.xml 中可定義的元素,在 web-fragment.xml 中也可以定義,舉個例子來說,你可以在 web-fragment.xml 中定義如下的內容:

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/web-fragment_4_0.xsd" version="4.0">
    <name>WebFragment1</name>
    <servlet>
        <servlet-name>helloworld</servlet-name>
        <servlet-class>cc.openhome.HelloWorld</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloworld</servlet-name>
        <url-pattern>/helloworld.view</url-pattern>
    </servlet-mapping>
</web-fragment>

注意到 web-fragment.xml 的根標籤是 <web-fragment> 而不是 <web-app>。實際上,web-fragment.xml 中所指定的類別,不一定要在 JAR 檔案中,也可以是在 web 應用程式的 /WEB-INF/classes 中。

Servlet 對 web.xml 與標註的配置順序並沒有定義,對 web-fragment.xml 及標註的配置順序也沒有定義,然而你可以決定 web.xml 與 web-fragment.xml 的配置順序,其中一個設定方式是在 web.xml 中使用 <absolute-ordering> 定義絕對順序。例如在 web.xml 中定義:

<web-app ...>
    <absolute-ordering>  
        <name>WebFragment1</name>  
        <name>WebFragment2</name>  
    </absolute-ordering>     
    ...
</web-app>

各個 JAR 檔中 web-fragment.xml 所定義的名稱不得重複,若有重複,則會忽略掉重複的名稱。另一個方式,是直接在每個 JAR 檔的 web-fragment.xml 中使用 <ordering>,在其中使用 <before><after> 來定義順序。以下多個 web-fragment.xml 例子:

web-fragment.xml

<web-fragment ...>
    <name>WebFragment1</name>
    <ordering>
        <after><name>MyFragment2</name>
    </after></ordering>
    ...
</web-fragment>

web-fragment.xml

<web-fragment ...>
    <name>WebFragment2</name>
    ..
</web-fragment>

web-fragment.xml

<web-fragment ...>
    <name>WebFragment3</name>
    <ordering>
        <before><others/></before>
    </ordering>
    ..
</web-fragment>

則載入定義的順序是 web.xml、WebFragment3、WebFragment2、WebFragment1。

如果將 web.xml 中 <web-app>metadata-complete 屬性設定為 true(預設是 false),則表示 web.xml 中已完成 Web 應用程式的相關定義,部署時將不會掃描標註與 web-fragment.xml 中的定義,如果有 <absolute-ordering><ordering> 也會被忽略。例如:

<web-app 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
           http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 
           metadata-complete="true" version="4.0">

    ...
</web-app>

如果 web-fragment.xml 中指定的類別可以在 web 應用程式的 /WEB-INF/classes 中找到,就會使用該類別,要注意的是,如果該類別本身有標註,而 web-fragment.xml 又有定義該類別為Servlet,則此時會有兩個 Servlet 實例。如果將 <web-fragment>metadata-complete 屬性設定為 true(預設是 false),則就只會處理自己 JAR 檔案中的標註資訊。

你可以參考 Servlet 規格書內容,當中有更多的 web.xml、web-fragment.xml 的定義範例。