Java Web 學習與總結(二)Servlet核心介面+Servlet3.0配置
Servlet3.0版本對Servlet配置進行了重大變革,Servlet類不需要再麻煩的去編輯web.xml檔案了,只需要在類上面進行註釋就可以了,獲得了 Java 社群的一片讚譽之聲,以下是新增的註解支援。
@WebServlet
@WebServlet 用於將一個類宣告為 Servlet,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為 Servlet。該註解具有下表給出的一些常用屬性(以下所有屬性均為可選屬性,但是 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存,如果同時指定,通常是忽略 value 的取值):
表 1. @WebServlet 主要屬性列表
屬性名 | 型別 | 描述 |
---|---|---|
name | String | 指定 Servlet 的 name 屬性,等價於 <servlet-name>。如果沒有顯式指定,則該 Servlet 的取值即為類的全限定名。 |
value | String[] | 該屬性等價於 urlPatterns 屬性。兩個屬性不能同時使用。 |
urlPatterns | String[] | 指定一組 Servlet 的 URL 匹配模式。等價於 <url-pattern> 標籤。 |
loadOnStartup | int | 指定 Servlet 的載入順序,等價於 <load-on-startup> 標籤。 |
initParams | WebInitParam[] | 指定一組 Servlet 初始化引數,等價於 <init-param> 標籤。 |
asyncSupported | boolean | 宣告 Servlet 是否支援非同步操作模式,等價於 <async-supported> 標籤。 |
description |
String | 該 Servlet 的描述資訊,等價於 <description> 標籤。 |
displayName | String | 該 Servlet 的顯示名,通常配合工具使用,等價於 <display-name> 標籤。 |
下面是一個簡單的示例:
1 2 3 4 5 |
@WebServlet(urlPatterns = {"/simple"}, asyncSupported = true,
loadOnStartup = -1, name = "SimpleServlet", displayName = "ss",
initParams = {@WebInitParam(name = "username", value = "tom")}
)
public class SimpleServlet extends HttpServlet{ … }
|
如此配置之後,就可以不必在 web.xml 中配置相應的 <servlet> 和 <servlet-mapping> 元素了,容器會在部署時根據指定的屬性將該類釋出為 Servlet。它的等價的 web.xml 配置形式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
< servlet >
< display-name >ss</ display-name >
< servlet-name >SimpleServlet</ servlet-name >
< servlet-class >footmark.servlet.SimpleServlet</ servlet-class >
< load-on-startup >-1</ load-on-startup >
< async-supported >true</ async-supported >
< init-param >
< param-name >username</ param-name >
< param-value >tom</ param-value >
</ init-param >
</ servlet >
< servlet-mapping >
< servlet-name >SimpleServlet</ servlet-name >
< url-pattern >/simple</ url-pattern >
</ servlet-mapping >
|
@WebInitParam
該註解通常不單獨使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是為 Servlet 或者過濾器指定初始化引數,這等價於 web.xml 中 <servlet> 和 <filter> 的 <init-param> 子標籤。@WebInitParam 具有下表給出的一些常用屬性:
表 2. @WebInitParam 的常用屬性
屬性名 | 型別 | 是否可選 | 描述 |
---|---|---|---|
name | String | 否 | 指定引數的名字,等價於 <param-name>。 |
value | String | 否 | 指定引數的值,等價於 <param-value>。 |
description | String | 是 | 關於引數的描述,等價於 <description>。 |
@WebFilter
@WebFilter 用於將一個類宣告為過濾器,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為過濾器。該註解具有下表給出的一些常用屬性 ( 以下所有屬性均為可選屬性,但是 value、urlPatterns、servletNames 三者必需至少包含一個,且 value 和 urlPatterns 不能共存,如果同時指定,通常忽略 value 的取值 ):
表 3. @WebFilter 的常用屬性
屬性名 | 型別 | 描述 |
---|---|---|
filterName | String | 指定過濾器的 name 屬性,等價於 <filter-name> |
value | String[] | 該屬性等價於 urlPatterns 屬性。但是兩者不應該同時使用。 |
urlPatterns | String[] | 指定一組過濾器的 URL 匹配模式。等價於 <url-pattern> 標籤。 |
servletNames | String[] | 指定過濾器將應用於哪些 Servlet。取值是 @WebServlet 中的 name 屬性的取值,或者是 web.xml 中 <servlet-name> 的取值。 |
dispatcherTypes | DispatcherType | 指定過濾器的轉發模式。具體取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。 |
initParams | WebInitParam[] | 指定一組過濾器初始化引數,等價於 <init-param> 標籤。 |
asyncSupported | boolean | 宣告過濾器是否支援非同步操作模式,等價於 <async-supported> 標籤。 |
description | String | 該過濾器的描述資訊,等價於 <description> 標籤。 |
displayName | String | 該過濾器的顯示名,通常配合工具使用,等價於 <display-name> 標籤。 |
下面是一個簡單的示例:
1 2 |
@WebFilter(servletNames = {"SimpleServlet"},filterName="SimpleFilter")
public class LessThanSixFilter implements Filter{...}
|
如此配置之後,就可以不必在 web.xml 中配置相應的 <filter> 和 <filter-mapping> 元素了,容器會在部署時根據指定的屬性將該類釋出為過濾器。它等價的 web.xml 中的配置形式為:
1 2 3 4 5 6 7 8 |
< filter >
< filter-name >SimpleFilter</ filter-name >
< filter-class >xxx</ filter-class >
</ filter >
< filter-mapping >
< filter-name >SimpleFilter</ filter-name >
< servlet-name >SimpleServlet</ servlet-name >
</ filter-mapping >
|
@WebListener
該註解用於將類宣告為監聽器,被 @WebListener 標註的類必須實現以下至少一個介面:
- ServletContextListener
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- HttpSessionListener
- HttpSessionAttributeListener
該註解使用非常簡單,其屬性如下:
表 4. @WebListener 的常用屬性
屬性名 | 型別 | 是否可選 | 描述 |
---|---|---|---|
value | String | 是 | 該監聽器的描述資訊。 |
一個簡單示例如下:
1 2 |
@WebListener("This is only a demo listener")
public class SimpleListener implements ServletContextListener{...}
|
如此,則不需要在 web.xml 中配置 <listener> 標籤了。它等價的 web.xml 中的配置形式如下:
1 2 3 |
< listener >
< listener-class >footmark.servlet.SimpleListener</ listener-class >
</ listener >
|
@MultipartConfig
該註解主要是為了輔助 Servlet 3.0 中 HttpServletRequest 提供的對上傳檔案的支援。該註解標註在 Servlet 上面,以表示該 Servlet 希望處理的請求的 MIME 型別是 multipart/form-data。另外,它還提供了若干屬性用於簡化對上傳檔案的處理。具體如下:
表 5. @MultipartConfig 的常用屬性
屬性名 | 型別 | 是否可選 | 描述 |
---|---|---|---|
fileSizeThreshold | int | 是 | 當資料量大於該值時,內容將被寫入檔案。 |
location | String | 是 | 存放生成的檔案地址。 |
maxFileSize | long | 是 | 允許上傳的檔案最大值。預設值為 -1,表示沒有限制。 |
maxRequestSize | long | 是 | 針對該 multipart/form-data 請求的最大數量,預設值為 -1,表示沒有限制。 |
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package javax.servlet; 7 8 import java.util.Enumeration; 9 10 public interface ServletConfig { 11 String getServletName(); 12 13 ServletContext getServletContext(); 14 15 String getInitParameter(String var1); 16 17 Enumeration<String> getInitParameterNames(); 18 }ServletConfig介面的主要方法: getInitParameter(String param); 根據給定的初始化引數名稱,返回引數值,若引數不存在,返回null getInitParameterNames():返回一個列舉集合物件,裡面包含了所有的初始化引數名稱 getServletContext():返回當前ServletContext()物件 getServletName():返回當前Servlet的名字,即@WebServlet的name屬性值。如果沒有配置這個屬性,則返回Servlet類的全限定名(絕對路徑) 2.ServletContext介面:
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package javax.servlet; 7 8 import java.io.InputStream; 9 import java.net.MalformedURLException; 10 import java.net.URL; 11 import java.util.Enumeration; 12 import java.util.EventListener; 13 import java.util.Map; 14 import java.util.Set; 15 import javax.servlet.ServletRegistration.Dynamic; 16 import javax.servlet.descriptor.JspConfigDescriptor; 17 18 public interface ServletContext { 19 String TEMPDIR = "javax.servlet.context.tempdir"; 20 String ORDERED_LIBS = "javax.servlet.context.orderedLibs"; 21 22 String getContextPath(); 23 24 ServletContext getContext(String var1); 25 26 int getMajorVersion(); 27 28 int getMinorVersion(); 29 30 int getEffectiveMajorVersion(); 31 32 int getEffectiveMinorVersion(); 33 34 String getMimeType(String var1); 35 36 Set<String> getResourcePaths(String var1); 37 38 URL getResource(String var1) throws MalformedURLException; 39 40 InputStream getResourceAsStream(String var1); 41 42 RequestDispatcher getRequestDispatcher(String var1); 43 44 RequestDispatcher getNamedDispatcher(String var1); 45 46 /** @deprecated */ 47 @Deprecated 48 Servlet getServlet(String var1) throws ServletException; 49 50 /** @deprecated */ 51 @Deprecated 52 Enumeration<Servlet> getServlets(); 53 54 /** @deprecated */ 55 @Deprecated 56 Enumeration<String> getServletNames(); 57 58 void log(String var1); 59 60 /** @deprecated */ 61 @Deprecated 62 void log(Exception var1, String var2); 63 64 void log(String var1, Throwable var2); 65 66 String getRealPath(String var1); 67 68 String getServerInfo(); 69 70 String getInitParameter(String var1); 71 72 Enumeration<String> getInitParameterNames(); 73 74 boolean setInitParameter(String var1, String var2); 75 76 Object getAttribute(String var1); 77 78 Enumeration<String> getAttributeNames(); 79 80 void setAttribute(String var1, Object var2); 81 82 void removeAttribute(String var1); 83 84 String getServletContextName(); 85 86 Dynamic addServlet(String var1, String var2); 87 88 Dynamic addServlet(String var1, Servlet var2); 89 90 Dynamic addServlet(String var1, Class<? extends Servlet> var2); 91 92 Dynamic addJspFile(String var1, String var2); 93 94 <T extends Servlet> T createServlet(Class<T> var1) throws ServletException; 95 96 ServletRegistration getServletRegistration(String var1); 97 98 Map<String, ? extends ServletRegistration> getServletRegistrations(); 99 100 javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2); 101 102 javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2); 103 104 javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2); 105 106 <T extends Filter> T createFilter(Class<T> var1) throws ServletException; 107 108 FilterRegistration getFilterRegistration(String var1); 109 110 Map<String, ? extends FilterRegistration> getFilterRegistrations(); 111 112 SessionCookieConfig getSessionCookieConfig(); 113 114 void setSessionTrackingModes(Set<SessionTrackingMode> var1); 115 116 Set<SessionTrackingMode> getDefaultSessionTrackingModes(); 117 118 Set<SessionTrackingMode> getEffectiveSessionTrackingModes(); 119 120 void addListener(String var1); 121 122 <T extends EventListener> void addListener(T var1); 123 124 void addListener(Class<? extends EventListener> var1); 125 126 <T extends EventListener> T createListener(Class<T> var1) throws ServletException; 127 128 JspConfigDescriptor getJspConfigDescriptor(); 129 130 ClassLoader getClassLoader(); 131 132 void declareRoles(String... var1); 133 134 String getVirtualServerName(); 135 136 int getSessionTimeout(); 137 138 void setSessionTimeout(int var1); 139 140 String getRequestCharacterEncoding(); 141 142 void setRequestCharacterEncoding(String var1); 143 144 String getResponseCharacterEncoding(); 145 146 void setResponseCharacterEncoding(String var1); 147 }ServletContext也成為Servlet上下文,代表當前Servlet執行環境,是Servlet與Servlet容器之間直接通訊的介面。Servlet容器在啟動一個Web應用時,會為該應用建立一個唯一的ServletContext物件供該應用中所有的Servlet物件共享,Servlet物件可以通過ServletContext物件來訪問容器的各種資源。 獲取ServletContext物件可以通過ServletConfig介面或者Generic抽象類中的getServletContext()方法 ServletContext介面中提供了以下幾種型別的方法: 獲取應用範圍的初始化引數的方法: getInitParameter(String param); 根據給定的初始化引數名稱,返回引數值,若引數不存在,返回null getInitParameterNames():返回一個列舉集合物件,裡面包含了所有的初始化引數名稱 存取應用範圍域屬性的方法: setAttribute(String name,Object obj)把一個物件和一個屬性名以key+value的形式繫結並存放在ServletContext中 getAttribute(String name) 返回用上一方法中存的物件,根據其對應的屬性名 getAttributeNames():返回一個列舉集合物件,該物件包含了所有存放在ServletContext中的屬性名 removeAttribute(String name):同getAttribute()方法相反,刪除一個匹配的物件 獲取當前Web應用資訊的方法: getContextPath():返回當前web應用的根路徑 getServletContextName():返回web應用的名字,即<web-app>中<display-name>元素的值 getRequestDispatcher(String path):返回一個用於其他web元件轉發請求的RequestDispatcher物件 getContext(Srting uripath):根據引數制定的URL返回當前Servlet容器中其他web應用的ServletContext物件,URL必須時以“/”開頭的絕對路徑 獲取當前容器資訊和輸出日誌的方法: getServletInfo():返回Web容器的名字和版本 getMajorVersion():返回Web容器支援的Servlet API主版本號 getMinorVersion():返回Web容器支援的Servlet API次版本號 log(String msg):用於記錄一般的日誌 log(String msg throwable):用於記錄帶異常型別的日誌 獲取伺服器端檔案資源的方法: getResourceAsStream(String path):返回一個讀取引數指定的檔案的輸入流,引數路徑必須以“/”開頭 getResouce(String path):返回由path指定的的資源路徑對應的一個URL物件,引數路徑必須以“/”開頭 getRealPath(String path):根據引數指定的虛擬路徑,返回檔案系統中的一個真實的路徑 getMimeType(String path):返回引數指定的檔案的MIME型別 3.HttpServletRequest介面
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package javax.servlet.http; 7 8 import java.io.IOException; 9 import java.security.Principal; 10 import java.util.Collection; 11 import java.util.Collections; 12 import java.util.Enumeration; 13 import java.util.Map; 14 import javax.servlet.ServletException; 15 import javax.servlet.ServletRequest; 16 17 public interface HttpServletRequest extends ServletRequest { 18 String BASIC_AUTH = "BASIC"; 19 String FORM_AUTH = "FORM"; 20 String CLIENT_CERT_AUTH = "CLIENT_CERT"; 21 String DIGEST_AUTH = "DIGEST"; 22 23 String getAuthType(); 24 25 Cookie[] getCookies(); 26 27 long getDateHeader(String var1); 28 29 String getHeader(String var1); 30 31 Enumeration<String> getHeaders(String var1); 32 33 Enumeration<String> getHeaderNames(); 34 35 int getIntHeader(String var1); 36 37 default HttpServletMapping getHttpServletMapping() { 38 return new HttpServletMapping() { 39 public String getMatchValue() { 40 return ""; 41 } 42 43 public String getPattern() { 44 return ""; 45 } 46 47 public String getServletName() { 48 return ""; 49 } 50 51 public MappingMatch getMappingMatch() { 52 return null; 53 } 54 }; 55 } 56 57 String getMethod(); 58 59 String getPathInfo(); 60 61 String getPathTranslated(); 62 63 default PushBuilder newPushBuilder() { 64 return null; 65 } 66 67 String getContextPath(); 68 69 String getQueryString(); 70 71 String getRemoteUser(); 72 73 boolean isUserInRole(String var1); 74 75 Principal getUserPrincipal(); 76 77 String getRequestedSessionId(); 78 79 String getRequestURI(); 80 81 StringBuffer getRequestURL(); 82 83 String getServletPath(); 84 85 HttpSession getSession(boolean var1); 86 87 HttpSession getSession(); 88 89 String changeSessionId(); 90 91 boolean isRequestedSessionIdValid(); 92 93 boolean isRequestedSessionIdFromCookie(); 94 95 boolean isRequestedSessionIdFromURL(); 96 97 /** @deprecated */ 98 @Deprecated 99 boolean isRequestedSessionIdFromUrl(); 100 101 boolean authenticate(HttpServletResponse var1) throws IOException, ServletException; 102 103 void login(String var1, String var2) throws ServletException; 104 105 void logout() throws ServletException; 106 107 Collection<Part> getParts() throws IOException, ServletException; 108 109 Part getPart(String var1) throws IOException, ServletException; 110 111 <T extends HttpUpgradeHandler> T upgrade(Class<T> var1) throws IOException, ServletException; 112 113 default Map<String, String> getTrailerFields() { 114 return Collections.emptyMap(); 115 } 116 117 default boolean isTrailerFieldsReady() { 118 return false; 119 } 120 }
在Servlet API中,ServletRequest介面被定義為用於封裝請求的資訊,ServletRequest物件由Servlet容器在使用者每次請求Servlet時建立並傳入Servlet的service方法中。
HttpServletRequest介面繼承自ServletRequest介面,該介面提供了具有如下HTTP請求資訊的處理
獲取請求報文資訊(包括請求行,請求頭和請求正文)的方法: 獲取請求行資訊: getMethod():獲取請求使用的HTTP方法,例如GET,POST和PUT getRequestURL():獲取請求行中的資源名部分 getProtocol():獲取協議和版本號 getQueryString():獲取請求URL後面的查詢字串,只對GET有效 getServletPath():獲取Servlet所對映的路徑 getContextPath():獲取請求資源所屬於的Web應用的路徑 獲取請求頭資訊: getIntHeader(String name):獲取整數型別的請求頭 getDateHeader(String name):獲取單值毫秒型別的請求頭 getContextLength():獲取請求內容的長度,以位元組為單位 getContentType():獲取請求的文件型別和編碼 getLocale():獲取使用者瀏覽器設定的locale資訊 getCookies():獲取一個包含在這個請求中所有cookie的陣列 獲取請求正文: getParameter(String name):返回由name指定的使用者請求引數的值 getParameterNames():返回所有使用者請求的引數名 getParameterValues(String name):返回由name制定的使用者請求引數對應的一組值 getParameterMaps():返回一個請求引數的MAP物件 ServletInputStream getInputStream():獲取上傳檔案二進位制輸出流 BufferedReader getReader():獲取上傳檔案字元緩衝輸入流 獲取網路連線資訊的方法: getRequestURL:返回客戶端發出請求時的完整URL。getRequestURI:返回請求行中的資源名部分。
getRemoteAddr:返回發出請求的客戶機的IP地址。
getRemoteHost:返回發出請求的客戶機的完整主機名。
getRemotePort:返回客戶機所使用的網路埠號。
getLocalAddr:返回WEB伺服器的IP地址。
getLocalName:返回WEB伺服器的主機名。 存取請求域屬性的方法: setAttribute(String name,Object obj)把一個物件和一個屬性名以key+value的形式繫結並存放在ServletContext中 getAttribute(String name) 返回用上一方法中存的物件,根據其對應的屬性名 getAttributeNames():返回一個列舉集合物件,該物件包含了所有存放在ServletContext中的屬性名 removeAttribute(String name):同getAttribute()方法相反,刪除一個匹配的物件 HttpServletResponse介面:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package javax.servlet.http; import java.io.IOException; import java.util.Collection; import java.util.Map; import java.util.function.Supplier; import javax.servlet.ServletResponse; public interface HttpServletResponse extends ServletResponse { int SC_CONTINUE = 100; int SC_SWITCHING_PROTOCOLS = 101; int SC_OK = 200; int SC_CREATED = 201; int SC_ACCEPTED = 202; int SC_NON_AUTHORITATIVE_INFORMATION = 203; int SC_NO_CONTENT = 204; int SC_RESET_CONTENT = 205; int SC_PARTIAL_CONTENT = 206; int SC_MULTIPLE_CHOICES = 300; int SC_MOVED_PERMANENTLY = 301; int SC_MOVED_TEMPORARILY = 302; int SC_FOUND = 302; int SC_SEE_OTHER = 303; int SC_NOT_MODIFIED = 304; int SC_USE_PROXY = 305; int SC_TEMPORARY_REDIRECT = 307; int SC_BAD_REQUEST = 400; int SC_UNAUTHORIZED = 401; int SC_PAYMENT_REQUIRED = 402; int SC_FORBIDDEN = 403; int SC_NOT_FOUND = 404; int SC_METHOD_NOT_ALLOWED = 405; int SC_NOT_ACCEPTABLE = 406; int SC_PROXY_AUTHENTICATION_REQUIRED = 407; int SC_REQUEST_TIMEOUT = 408; int SC_CONFLICT = 409; int SC_GONE = 410; int SC_LENGTH_REQUIRED = 411; int SC_PRECONDITION_FAILED = 412; int SC_REQUEST_ENTITY_TOO_LARGE = 413; int SC_REQUEST_URI_TOO_LONG = 414; int SC_UNSUPPORTED_MEDIA_TYPE = 415; int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; int SC_EXPECTATION_FAILED = 417; int SC_INTERNAL_SERVER_ERROR = 500; int SC_NOT_IMPLEMENTED = 501; int SC_BAD_GATEWAY = 502; int SC_SERVICE_UNAVAILABLE = 503; int SC_GATEWAY_TIMEOUT = 504; int SC_HTTP_VERSION_NOT_SUPPORTED = 505; void addCookie(Cookie var1); boolean containsHeader(String var1); String encodeURL(String var1); String encodeRedirectURL(String var1); /** @deprecated */ @Deprecated String encodeUrl(String var1); /** @deprecated */ @Deprecated String encodeRedirectUrl(String var1); void sendError(int var1, String var2) throws IOException; void sendError(int var1) throws IOException; void sendRedirect(String var1) throws IOException; void setDateHeader(String var1, long var2); void addDateHeader(String var1, long var2); void setHeader(String var1, String var2); void addHeader(String var1, String var2); void setIntHeader(String var1, int var2); void addIntHeader(String var1, int var2); void setStatus(int var1); /** @deprecated */ @Deprecated void setStatus(int var1, String var2); int getStatus(); String getHeader(String var1); Collection<String> getHeaders(String var1); Collection<String> getHeaderNames(); default void setTrailerFields(Supplier<Map<String, String>> supplier) { } default Supplier<Map<String, String>> getTrailerFields() { return null; } }
HttpServletResponse同HttpServletRequest一樣都是繼承與javax.servlet包內的介面,它被用來進行HTTP相應資訊的處理
HttpServletResponse介面提供了具有如下功能型別的方法:
設定響應狀態的方法:
setStatus(int sc):以指定的狀態碼相應返回給客戶端
setError(int sc):使用制定的狀態碼向客戶端返回一個錯誤響應
sendError(int sc,String msg):使用指定的狀態碼和狀態描述向客戶端返回一個錯誤響應
sendRedirect(String):請求的重定向
構建響應頭的方法:
setContentType(String mimeType):設定Content-type報頭。
setContentLength(int length):設定Content-length報頭,用於瀏覽器持續性HTTP連線。
addCookie(Cookie c):向set-cookie報頭插入一個cookie。
addHeader(String name, String value):新增String型別值到名為name的http頭部
addIntHeader(String name, int value):新增Int型別值到名為name的http頭部
addDateHeader(String name, long value):新增long型別值到名為name的http頭部
建立相應正文的方法:
getOutputStream():返回位元組輸出流物件ServletOutputStream
getWriter():返回字元輸出流物件PrintWrite