處理請求、給予回應的全部因素構成的整體稱作伺服器,包含硬體與軟體兩個面向。
伺服器處理請求、回應遵守的原則。
Server Applet,運行在伺服器端的java應用程序,使用java語言編寫,遵守java規範,Servlet規範的核心。
# Servlet在整個網路伺服器中扮演控制器的角色,將請求轉送給對應的業務邏輯處理。
tomcat是伺服器的軟體方面,是實現了Servlet規範與JSP規範的容器,由Apache提供、免費、開放原始碼的輕量級級應用程式伺服器,適用於中小型系統和並發存取用戶不是很多的場合。
Multipurpose Internet Mail Extensions,多用途互聯網郵件擴展協議,為每種類型的文件製定一種規範以便瀏覽器接收到檔案以後呼叫對應的元件開啟。
## ServletContext,Servlet上下文,是所用Servlet存在的環境,一個Web應用程序,一個實例對象,在整個應用
層級起作用。JSP內建物件application就是一個ServletContext實例,
ServletContext在檔案上對應於web.xml,web.xml包含整個伺服器中使用者自訂的全部訊息,從此可以看出
ServletContext的作用範圍。作用:ServletContext主要用於在Web伺服器啟動時載入各元件到伺服器,根據自訂參數初始化伺服器。
生命週期:ServletContext物件在伺服器啟動時創建,伺服器停止時銷毀。
servletContext.setAttribute(name, object);//向servletContext作用域中添加属性,该属性为所有用户共享servletContext.getAttribute(name);//获取servletContext作用域中指定属性的属性值servletContext.removeAttribute(name);//从servletContext作用域中删除指定属性servletContext.getRealPath(path);//根据相对于项目的路径获取资源的绝对路径URLservletContext.getResourceAsStream(path);//获取路径文件的输入流servletContext.getInitParameter(name);//获取初始化参数的值
ServletContext物件由Web伺服器在啟動時自動創建,程式設計師需要做的就是取得該實例對象,以下是幾種
取得方式。this.getServletContext();//HttpServlet提供了获取ServletContext实例对象的方法,在doGet或者doPost方法内部request.getServletContext();//通过request对象获取HttpSession session = request.getSession(); session.getServletContext();//通过session对象获取
用來初始化Servlet,一個Servlet,一個ServletConfig。 ###
public void init(ServletConfig config) throws ServletException { String initParameter = config.getInitParameter("initParamName"); }
Web容器启动时会创建两个与Servlet相关的Map集合,两个集合的key值均为urlPattern,即请求uri,第一个Map的value是Servlet的引用变量,第二个Map的value是Servlet的全限定性类名。请求到达Web容器后,系统先搜索第一个Map集合,如果存在与uri对应的引用变量,则获取该引用变量,如果不存在,继续搜索第二个Map集合,获取对应的全限定类型,创建对象,并把引用变量存到第一个Map集合中。
Servlet ServletConfig Serializable
| | |
--------------------------------------------------
|
|
Servlet接口是Servlet规范中定义的,服务器自动调用其中service方法处理请求,该接口有多个抽象方法,很多在实际开发中很少使用,实现该接口需要实现其中的全部抽象方法,因此不采用直接实现Servlet接口的方法创建Servlet。
GenericServlet实现Servlet接口中大多数抽象方法,保留了一个抽象方法service,继承该抽象类创建servlet,必须实现该抽象方法。HTTP中多个请求方法在处理请求前必须做一些固定的前置工作,如果实现该serivce方法就需要在每一个Servlet的service方法中都编写前置工作代码,造成代码冗余。为了解决此问题,tomcat提供了一个GenericServlet的子类HttpServlet,HttpServlet采用固定行为结构的模板方法模式将前置工作固定在一个方法,用户在创建Serlvet时继承HttpServlet,然后重写与请求方式对应的方法即可。(具体参考源码)
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException { String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logic doGet(req, resp); } else {long ifModifiedSince;try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -1; }if (ifModifiedSince < (lastModified / 1000 * 1000)) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else {//// Note that this means NO servlet supports whatever// method was requested, anywhere on this server.//String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
从中可以看出Get请求在实际执行前做了很多准备工作。
Servlet默认在调用时创建并初始化,这也是工作原理中第一个Map集合引用变量为空情况出现的原因。对一些必定会被访问并且访问频繁的Servlet可以设定为在容器启动时创建并初始化,提高访问速度。
<load-on-startup>int类型数字</load-on-startup>
小于0:默认值,调用时创建初始化。
等于大于0:在Web服务器启动时创建初始化,值越小,优先级越高。出现相同值时,不会出现异常,容器自定义顺序执行。
this.getInitParameter("");//获取初始化参数this.getServletName();//获取配置文件<servlet-name>标签的内容this.getServletContext();
6.由于继承HttpServlet创建的Servlet属于自定义类,系统不知晓,必须在配置文件中配置:
<servlet> <servlet-name>bothRegisterServlet</servlet-name> <servlet-class>com.servlet.register.BothRegisterServlet</servlet-class> <init-param> <param-name>xxx</param-name> <param-value>xxxx</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported></servlet><servlet-mapping> <servlet-name>bothRegisterServlet</servlet-name> <url-pattern>/bothRegisterServlet01</url-pattern></servlet-mapping>
由於訪問Web容器中的Servlet物件時,無論採用哪種方式都只能間接訪問,一般採用url訪問servlet,那麼就需要在訪問時使用的url與最終資源servlet之間建立一對一的映射關係,這就是servletMapping存在的原因。
允許為一個Servlet物件配置多個url格式。
Servlet規格定義的一個向Servlet提供客戶端請求資訊的物件。
請求參數:當表單輸入為空時,伺服器端取得的內容為空字串,不為null。
request作用域內的屬性:可以將資料存放到request作用域中,在同一請求中取得。
輸入流:檔案上傳時,可以透過request取得上傳檔案的輸入流。
Cookie:Cookie由伺服器生成,保存在客戶單,瀏覽器向伺服器發出請求時自動提交。
Part:代表上傳檔案的物件。
其他內建物件:如session\application。
其他與請求密切相關的資訊:如請求方式、使用的瀏覽器、協定、請求url、請求體長度、查詢字串、請求頭、客戶端IP、連接埠號碼等。
getParameter:取得請求參數的值,傳回值String,表單輸入為空時,回傳值為空字串,不為null。
getAttribute:取得作用域範圍內的屬性,傳回類型為屬性類型,屬性不存在時,傳回null。
getSession():與getSession(true)相同,基於Cookie取得Session對象,如果Cookie不存在,則新建session。
getSession(false):基於Cookie取得Session對象,如果Cookie不存在,則傳回null。
⑶request.setCharacterEncoding():設定請求體的編碼方式,也就是伺服器解析瀏覽器提交的資料時所採用的編碼方式。 由於只有POST請求的資料透過請求體傳輸,所以只對POST請求有意義,對GET請求無意義。
⑷request.getContentLength():取得請求體的位元組長度,只對POST有意義。
⑸request.getQueryString():取得追到URL後面的請求字串,只對GET請求有意義。
# # 持有回應權的資源不同:
### 4.什麼是相同請求? ############ 一次請求在結束前,如果未進行重定向操作,進行轉送與包含操作,則仍屬於同一請求的範疇。 ############ 5.相同請求的意義 ############ 同一請求共享請求參數與作用域中的屬性等資訊。 ######
Servlet规范定义的一个向浏览器发出响应的对象,由Servlet容器自动创建。
由于存在多种响应数据类型,因此服务器在响应前必须指明数据类型,以便浏览器根据指定类型处理接收的数据。
response.setContentType("text/html;charset=UTF-8");//以HTML方式处理
response.setHeader(name,value);
response.addCookie(Cookie cookie);
response.sendRedirect(url);
PrintWriter out=reponse.getWriter();
ServletOutputStream sos=response.getOutputStream();
代码继续执行,对响应结果或者request作用域没有影响,因为响应已经结束,请求已转发给其他资源,自身就失去了处理请求的能力,但对session\applicatiion作用域可以产生影响。通常不在响应或者跳转之后对整个处理过程施加影响。
由服务器生成,保存在浏览器端的存储会话信息的对象。
Servlet规范提供了Cookie类,用于创建Cookie对象:
Cookie cookie=new Cookie(name,value);
由服务器创建,保存在客户端,浏览必须允许保存Cookie将cookie:
response.addCookie(cookie);
Cookie在访问服务器时自动提交,这种提交不是无选择地对任何访问路径都提交,而是只对设定的路径提交。在未单独设定Cookie的绑定路径时,Cookie与生成Cookie的访问路径绑定,访问该路径下任何一个资源时,浏览器自动提交Cookie。
可以设定Cookie的绑定路径,使Cookie不受生成路径的限制:
cookie.setPath(String uri);
默认情况下,Cookie保存在浏览器缓存中,浏览器关闭,Cookie销毁。如果希望Cookie中保存的信息长期存在,可以将Cookie保存到本地硬盘中,前提是当前浏览器支持。
cookie.setMaxAge(int expiry);//以秒为单位
取值大于0:将Cookie保存到硬盘中,无论浏览器是否关闭,指定时长过去后,Cookie被销毁。
等于0:立即销毁Cookie。
小于0:默认情况,将Cookie保存在浏览器缓存中,浏览器关闭,Cookie销毁。
String name = cookie.getName();//获取Cookie的名称String value = cookie.getValue();//获取Cookie的值cookie.setValue(newValue);//修改Cookie的值
Cookie主要用于保存浏览器关闭以后需要保留的会话信息,如登陆信息,实现免登陆功能。
在HTTP协议中,浏览器向服务器发送请求,服务器响应完毕后,连接结束,服务器端没有保存本次请求的任何信息,这就是HTTP协议的无状态特性。如果需要保存会话信息,就必须提供一种解决方案,而Session就是这个解决方案。
Session是用来在服务器端保存会话信息的对象,比如保存用户在网站上的足迹等。
在浏览器开启Cookie的情况下,从浏览器第一个访问网站到浏览器关闭的时间内浏览器与服务器所有的互动都是在同一个会话中。如果浏览器关闭了Cookie,那么浏览器每向服务器发送一次请求,都开启一个新的会话,即服务器端都会新建一个Session对象。
第一次访问时,服务器会自动创建一个Session对象,并为Session对象分配一个唯一的32位的id,同时生成一个Cookie对象,name为JSESSIONID,value为Session的id。在Cookie的有效期内,再次访问服务器时,根据value值获取对应的Session对象,这样就保证了在同一次会话中存在的始终是同一个Session对象。
Session对象在浏览器第一次访问服务器时创建,如果浏览器长时间不向服务器发送请求,在指定的时长之后,服务器会销毁Session对象。
这里所说的时长不是从Session创建到销毁的时间长度,而是浏览器长时间发送请求,服务器保存Session对象的最大时间长度,通过以下方法设置,以秒为单位:
session.setMaxInactiveInterval(int interval);
通过以下方法,浏览器可以主动销毁Session对象:
session.invalidate();
Session主要用于在同一会话中共享数据,所以对Session的主要操作是操作作用域中的属性:
session.setAttribute(name,value);//向作用域中添加属性session.getAttribute(name);//获取作用域中的属性session.removeAttribute(name);//从作用域中删除属性
服务器接收到请求以后,首先对请求进行预处理,然后将请求转发给其他的资源继续处理,这种转发叫做请求转发。
服务器调用特定的方法向浏览器发送一个资源路径,浏览器访问该路径,这一过程叫做重定向。
请求转发是服务器调用不同的资源处理同一请求,始终是同一请求。
重定向使得浏览器再次向服务器发送请求,前后是两个不同的请求。
为耗时的任务分配一个线程,主线程继续执行后面的代码,执行完毕,将主线程归还线程池,以便执行其他的请求。
Servlet是单例多线程的,允许并发访问的线程数目有限,为此Servlet建立了一个线程池,请求必须从线程池中获取了线程才能访问Servlet。若一个请求长时间占有线程,可能导致后面的请求长时间等待,降低了程序的吞吐能力。如果一个线程从Servlet线程池中获取了线程以后,另外开启一个线程处理耗时的任务,及时将主线程归还线程池,就解决这个问题。
异步机制的作用主要不是为了提高单次执行速度,而是提高吞吐量,即同一时间段内允许更多的请求访问Servlet。为了提高访问的线程数目,降低每次访问占有Servlet线程的时间,将耗时的任何交个另外一个线程处理,将主线程及时归还线程池。
Servlet接收到请求以后,对请求进行初步处理,然后开启一个异步线程处理具体的业务,Servlet线程继续执行后面的代码,执行完毕后,将Servlet线程归还线程池,以便其他请求使用。等待异步线程执行完毕后一起响应,异步线程执行完毕主要有两个标志:
在异步线程内部调用complete方法。
超时时间结束。
超时时间不并代表异步线程的生命时长,而是最大生命时长。如果在超时时长内,异步线程调用了complete方法,异步线程提前结束。
异步线程默认的超时时长是10s(Servlet不同版本不同),当主线程执行完毕,同时超时时长结束或者异步线程提前结束,服务器开始向浏览器发送响应,销毁request、response对象,关闭输出流,如果异步线程未执行完毕,那么异步线程中已执行的响应会响应到浏览器,未执行的响应不会响应到浏览器。
分配给异步线程的时间不是无限的,因此存在一个响应时机的问题。
在主线程执行完毕并且异步线程超时时长用完或者提前结束时响应。
AsyncContext ac=request.startAsync(); ac.setTimeout(int mills); ac.start(Runnable run);//将异步线程管理对象ac作为参数传入异步线程中,通过该参数可以获取request\response
由于异步机制的设计目的是为了使请求尽快归还Servlet线程,提高程序的吞吐量,并未显著提高响应速度。异步机制通常不直接用作向浏览器输出响应内容,如在异步线程内部使用out.write直接向浏览器输出,而是用来处理耗时的任务,将处理结果存放到session/application等作用域中。
8.还可以使用AsyncContext对象为异步线程添加监听器,监听异步线程的执行过程。
1.配置Servlet、Filter、Listener、contextParams等构成应用程序的重要信息。
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>default.html</welcome-file></welcome-file-list>
注:在配置文件中书写路径时,只有欢迎列表中的路径不需要在前面加“/”,其他地方的路径都需要在前面加“/”,因为欢迎页面只能放在WebContent根路径下,不能放在WebContent根路径下的文件夹内,其路径相对固定,因此可以简写。
<error-page> <error-code>404</error-code> <location>/xxxx</location>//可以放在WebContent目录下任意位置</error-page>
<error-page> <exception-type>java.lang.NullException</exception>//异常类必须写完整的类名 <location>/xxxx</location></error-page>
<context-param><param-name>paramName</param-name><param-value>paramValue</param-value></context-param>
Servlet3.0新增注解式开发,注解式开发就是将在编写在配置文件web.xml中的信息转移到类文件上。
注解方式:
在类名上添加注解:
@WebServlet(urlPatterns={"",""},loadOnStartup=1,initParams={@WebInitParam(name="",value="")},asyncSupported=true)
两种注册方式同时存在,如果映射路径不相同,相当于存在一个集中了所用路径的Servlet,如果存在相同路径,服务器无法启动。
1.Servlet3.0提供一个Part类型,该类封装了上传文件信息。
2.文件名存储在一个名为Content-Disposition的请求头中。
Part part=request.getPart(文件字段名);//获取文件封装对象String fileName=part.getHeader("Content-Dispostion");//获取文件名part.write("parentPath/"+fileName);//将文件保存到指定路径,只能使用绝路径
1.Servlet提供了ServletOutputStream用作文件下载的输出流。
2.文件下载时必须设置浏览器以附件的形式处理从服务器获取的数据:
response.setHeader("Content-disposition","attachment;filename=xxxx");
如果请求头中设置的文件名含有中文必须转化为ISO-8859-1的编码方式。
3.从服务器获取输入流,利用ServletOutputStream输出流将文件写到用户指定路径:
InputStream is = getServletContext().getResourceAsStream("/Files/upload.txt"); ServletOutputStream os = response.getOutputStream();
有了輸入流與輸出流,後續操作就是將輸入流中的內容寫入輸出流,這是IO常見的操作。
Servlet以單例的形式運行在多線程的環境中,其中的實例變數儲存在堆中的物件中,而堆是多線程共享的,因此實例變數存在線程安全問題,而靜態變數儲存在方法區,方法區也是多執行緒共享的,靜態變數也存在執行緒安全性問題,而局部變臉儲存在堆疊中,堆疊中的資料在內部是共享的,在堆疊間是不共享的,即一個線程一個棧,因此局部變數時線程安全的。
#將全域變數轉換為局部變數。
將修改全域變數的程式碼加入同步方法或同步區塊。
將全域變數儲存到ThrealLocal中,為每個執行緒分配一個變數的副本,各個執行緒相互獨立操作。
開放性資源:對所用使用者公開,無需權限就可以存取的資源。
權限性資源:保存使用者個人訊息,經過驗證後才可以存取的資源。
登入網站以後,下次使用相同瀏覽器造訪網站,不需要重新登入。
# 將登入資訊(使用者名稱、密碼)儲存在Cookie中,將Cookie儲存在本地,造訪網站時瀏覽器自動提交Cookie,經Filter或Interceptor驗證通過後,無需登入直接存取。
同一個瀏覽器:免登入需要從本機取得Cookie物件驗證資訊。
# Servlet3.0元件具有可查性,指的是Servlet、Filter、Listener可以作為架包插入項目中。
# 建立專案Web Fragment Project。
打包jar File。
放入lib目錄下,當作架包。
# 可以將一些常用的Servlet元件封裝成架包,直接放在lib目錄下使用,例如解決POST請求時中文亂碼的Filter。
在Web執行時註冊Servlet、Filter、Listener。
為了安全,只能在網路伺服器啟動時註冊,也就是透過ServletContextListener監聽器註冊。
以上是服務程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!