1. 程式人生 > >深入Jetty原始碼之Servlet框架及實現(ServletContext)

深入Jetty原始碼之Servlet框架及實現(ServletContext)

publicinterface ServletContext {
// Servlet Container為當前Web Application設定臨時目錄,並將該臨時目錄的值儲存到當前ServletContext的屬性中使用的屬性名。// Jetty使用WebInfConfiguration(在preConfig ure()方法中)來設定該值,設定temp目錄的規則: 
// 1. 如果存在WEB-INF/work目錄,則temp目錄的值為:WEB-INF/work/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string> 
// 2. 如果"javax.servlet.context.tempdir"已經在外部被設定,並且該目錄存在且可寫,則temp目錄直接設定為該目錄例項。 
// 3. 如果系統變數中"jetty.home"目錄下存在"work"目錄且可寫,則設定temp目錄的值為:${jetty.home}/work/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>
// 4. 如果存在"org.eclipse.jetty.webapp.basetempdir"的屬性,且該目錄存在並可寫,設定temp目錄為:${
org.eclipse.jetty.webapp.basetempdir}/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>
// 5. 如果以上條件都不成立,則設定temp目錄為:${java.io.tmpdir}/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>,且刪除已存在臨時目錄。 
// 注:對temp目錄的父目錄不是work,會註冊在JVM退出時刪除該temp目錄,並在temp目錄下建立.active目錄。 

publicstaticfinal String TEMPDIR = "javax.servlet.context.tempdir";
// Servlet 3.0中新引入的特性,即可以在WEB-INF/lib下的jar包中定義/META-INF/web-fragment配置響應的Servlet等。 
// 如果在web.xml檔案中定義了absolute-ordering,或者在jar包中存在/META-INF/web-fragment.xml檔案,且定義了ordering, 
// 則該屬性的值即為根據規範中定義的規則計算出來的讀取jar包中web-fragment.xml檔案的jar包名順序,它時一個List<String>型別,包含jar包名字。 
// 在Jetty中使用Ordering來表示這種順序,它有兩個實現類:AbsoluteOrdering和RelativeOrdering用來分別表示在web.xml和web-fragment.xml中定義的absolute-ordering和ordering定義, 
/並且將最終的解析結果彙總到Metadata類中,並根據規範中定義的規則以及metadata-complete的定義來計算實際的解析順序
/而對這兩種配置檔案的解析由WebDescriptor和FragmentDescriptor來實現,它們都包含了metadata-complete解析,而真正的解析入口在WebXmlConfiguration和FragmentConfiguration中。 
// 該規範的定義參考:https://blogs.oracle.com/swchan/entry/servlet_3_0_web_fragmentpublicstaticfinal  String ORDERED_LIBS = "javax.servlet.context.orderedLibs"; 
// 返回當前Web Application的Context Path,即當前Web Application的根路徑,Servlet Container根據該路徑以及一個Request URL的匹配情況來選擇一個Request應該交給那個Web Application處理該請求。 
// Context Path以"/"開始,但是不能以"/"結尾,對預設的根Context,它返回"",而不是"/"。該值在配置Jetty的ContextHandler中設定。 
// 有些Servlet Container支援多個Context Path指向同一個Context,此時可以使用HttpServletRequest中的getContextPath()來獲取該Request實際對應的Context Path,此時這兩個Context Path的值可能會不同,但是ServletContext中返回的Context Path值是主要的值。另外Jetty也不支援這種特性。
public  String getContextPath(); 
// 通過給定一個Context Path以在當前Servlet Container中找到其對應的ServletContext例項。 
// 可以通過該方法獲取Servlet Container中定義的另一個Web Application的ServletContext例項,並獲得其RequestDispatcher,並將當前請求Dispatch到那個Web Application中做進一步的處理。這裡的uripath必須以"/"開頭,且其路徑相對於當前Server的根路徑。出於安全考慮,該方法可能會返回null。 
// 在Jetty的實現中,這裡uripath可以是一個具體的路徑,並且支援查詢最準確的匹配。如:對uripath為/foo/goo/abc.html,在Server中由以下Context Path定義:"/", "/foo", "/foo/goo",則最終查詢到的ServletContext為"/foo/goo"作為Context Path對應的ServletContext例項。
public  ServletContext getContext(String uripath); 

//返回Servlet規範的主版本,如3
publicint  getMajorVersion(); 
// 返回Servlet規範的次版本,如0
publicint  getMinorVersion(); 
// 返回當前Web Application基於的Servlet規範的主版本,如在web.xml檔案中定義的version(<web-app version="..." ...>...</web-app>,即Jetty中的實現)
publicint  getEffectiveMajorVersion(); 
/返回當前Web Application基於的Servlet規範的次版本,如在web.xml檔案中定義的version(<web-app version="..." ...>...</web-app>,即Jetty中的實現)
publicint  getEffectiveMinorVersion(); 
// 返回給定file的MIME type,返回null如果無法計算出其MIME type。這個對映關係由Servlet Container定義或在web.xml檔案中定義(mime-mapping, extension, mine-type)。 
// 常見的MIME type有:text/html, image/gif等。Jetty使用MimeTypes類來封裝所有和MIME type相關的操作,MimeTypes類中定義了所有預設支援的MIME type以及編碼型別, 
// 並且預設從org/eclipse/jetty/http/mime.properties檔案中載入預設的對映,如css=text/css, doc=application/msword等,使用addMimeMapping()方法向該類註冊web.xml中定義的檔名副檔名到MIME type的對映。 
// 而從org/eclipse/jetty/http/encoding.properties檔案中載入MIME type的預設編碼型別,如text/xml=UTF-8等。 
// 在使用檔名查詢MIME type時,根據檔名的副檔名查詢已註冊或預設註冊的MIME type。使用者自定義的對映優先。使用者定義的MIME type對映支援extension為"*",表示任意副檔名。
public  String getMimeType(String file); 

// 對於給定目錄,返回該目錄下所有的資源以及目錄。path必須以"/"開頭,如果它不是指向一個目錄,則返回空的Set。所有返回的路徑都相對當前Web Application的根目錄, 
/或對於/WEB-INF/lib中jar包中的/META-INF/resources/目錄,如一個Web Application包含以下資源:/catalog/offers/music.html, /WEB-INF/web.xml, 
// 以及/WEB-INF/lib/catalog.jar!/META-INF/resources/catalog/moreOffers/books.html,則getResourcePaths("/catalog")返回{"/catalog/offers/", /catalog/moreOffers/"} 
// Jetty的實現中,在MetaInfConfiguration中,它會掃描WEB-INF/lib目錄下所有的jar包,如果發現在某個jar包中存在META-INF/resources/目錄, 
/就會將該目錄資源作為baseResource在WebInfConfiguration中註冊到ContextHandler(WebAppContext)中。從而實現jar包中的META-INF/resources/目錄作為根目錄的查詢。
public  Set<String> getResourcePaths(String path); 

// 返回給定path的URL,path必須以"/"開頭,它相對當前Web Application的根目錄或相對/WEB-INF/lib中jar包中的/META-INF/resources/目錄。 
/其中查詢順序前者優先於後者,但是在/WEB-INF/lib/目錄下的jar包的查詢順序未定義。該方法不同於Class.getResource()在於它不使用ClassLoader,如果沒有找到給定資源,返回null。 
// 在WebAppContext實現中,它還支援Alias查詢,並且如果其extractWAR的變數為false,給定的資源在WAR包中,則該URL返回WAR包中的URL。
public  URL getResource(String path)  throws  MalformedURLException; 

// 參考getResource(path)的查詢實現,如果其返回的URL為null,則該方法返回null,否則返回URL對應的InputStream。
public  InputStream getResourceAsStream(String path); 
// 建立一個RequestDispatcher用於將一個Request、Response分發到path對應的URL中,這裡path必須以"/"開頭,且它相對於當前Context Path。如果無法建立RequestDispatcher,返回null。 
// path可以包含query資料用於傳遞引數:uriInContext?param1=abc&param2=123....該方法可以和getContext(uripath)一起使用,以將當前請求分發到另一個Web Application中。 
// 該方法的另一種用法是先有一個Servlet或Filter處理基本的邏輯,然後使用這個RequestDispatcher將當前請求forward到另一個URL中或include一個JSP檔案生成響應頁面,如果在處理過程中出錯,則將其當前請求分發到錯誤處理的流程中。
// RequestDispatcher支援兩種型別的分發:forward和include,唯一的區別是include只可以改變Response的內容,不可以改變其Header資訊,forward則沒有這種限制。
public  RequestDispatcher getRequestDispatcher(String path); 
// 建立一個RequestDispatcher用於將一個Request、Response分發到name對應的Servlet(JSP)中。如果沒能找到響應的Servlet,返回null。
public  RequestDispatcher getNamedDispatcher(String name);     
// 將msg列印到Servlet對應的log檔案中,在Jetty中,使用INFO級別列印,logger名稱為web.xml定義的display-name,或者context path。 
/Jetty預設使用SLF4J作為日誌列印框架,可以使用"org.eclipse.jetty.util.log.class"系統屬性改變其日誌列印框架。
publicvoid  log(String msg);    
// 列印message和throwable到Servlet對應的log檔案中,在Jetty中使用WARN級別列印該資訊。
publicvoid  log(String message, Throwable throwable); 
// 返回給定path在當前機器上作業系統對應的位置。對/META-INF/resources下的資源,除非他們已經解壓到本地目錄,否則對那些資源該方法返回null。 
// 在Jetty實現中,使用getResource()方法相同的實現獲取Resource例項,如果其getFile()返回不為null,則返回該File的canonical路徑。
public  String getRealPath(String path); 
// 返回Servlet Container的名稱和版本號,其格式為:<servername>/<versionnumber>,如:Jetty/8.1.9.v20130131。
public  String getServerInfo(); 
// ServletContext級別的初始引數操作,可以在web.xml中使用context-param定義,也可以手動設定。在get中如果找不到對應的項,返回null,在set時,如果已存在name,則返回false,並且不更新相應的值。
public  String getInitParameter(String name); 
public  Enumeration<String> getInitParameterNames(); 
publicboolean  setInitParameter(String name, String value); 
// ServletContext級別的屬性操作,其中屬性名遵循包命名規則。在set中,如果object為null表示移除該屬性,如果name以存在,則會替換原有的值,如果註冊了ServletContextAttributeListener,則會出發相應的attributeRemoved、attributeReplaced、attributeAdded事件。在remove中,如果name存在且被移除了,則會觸發attributeRemoved事件。 
// 在Jetty中使用ContextHandler中的Context內部類實現ServletContext,在ContextHandler中定義了兩個相關欄位:_attributes以及_contextAttributes,其中_attributes表示在Jetty內部通過ContextHandler設定的屬性,而_contextAttributes表示使用者設定的屬性,但是在獲取屬性值時,兩個欄位的屬性都會考慮進去,在移除屬性時,如果是移除_attributes欄位中的值,則不會觸發attributeRemoved事件。
public  Object getAttribute(String name); 
public  Enumeration<String> getAttributeNames(); 
publicvoid  setAttribute(String name, Object object); 
publicvoid  removeAttribute(String name); 
// 返回當前Web Application在web.xml中的display-name定義的ServletContext名字,在Jetty實現中,如果該值為null,則返回context path。
public  String getServletContextName(); 
// 該部分具體的使用可以參考:http://www.blogjava.net/yongboy/archive/2010/12/30/346209.html
// 動態的向ServletContext中註冊Servlet,註冊的Servlet還可以通過返回的ServletRegistration繼續配置,如addMapping、setInitParameter等。 
// 在使用className例項話Servlet時,使用當前ServletContext相關聯的ClassLoader。在建立Servlet例項時,會根據該類中定義的以下Annotation做相應的配置: 
// javax.servlet.annotation.ServletSecurity、javax.servlet.annotation.MultipartConfig、javax.annotation.security.RunAs、javax.annotation.security.DeclareRoles
public  ServletRegistration.Dynamic addServlet(String servletName, String className); 
public  ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet); 
public  ServletRegistration.Dynamic addServlet(String servletName, Class <?  extends  Servlet> servletClass); 
// 建立給定Servlet類的Servlet例項,並且會根據該類中定義的以下Annotation做相應的配置: 
// javax.servlet.annotation.ServletSecurity、javax.servlet.annotation.MultipartConfig、javax.annotation.security.RunAs、javax.annotation.security.DeclareRoles
// 在建立Servlet例項後,一般還要呼叫addServlet()方法將其註冊到ServletContext中。
public  <T  extends  Servlet> T createServlet(Class<T> clazz)  throws  ServletException; 
// 根據servletName獲取ServletRegistration例項
public  ServletRegistration getServletRegistration(String servletName); 
// 獲取所