J2EE 基礎(Servlet篇)
java 基礎 是 servlet 的基礎 servlet 是 jsp 的基礎,Java本身不適合web 開發,但是 servlet 可以做到,jsp 適合做頁面
java –> servlet –> jsp
2. 常見的java伺服器
JBOSS
WebLogic
3.Tomcat 的目錄結構
1.bin:
主要負責 tomcat 的啟動和停止(二進位制核心檔案)
2.conf
tomcat 的配置檔案目錄,奇珍有三個 xml 檔案非常重要,分別是 server.xml 、 tomcat-users.xml 、 web.xml
server.xml:
主要用於配置和server 相關的資訊,比如 tomcat 的啟動埠以及 Host
web.xml
主要配置和 web 應用(web站點)相關的,比如我們規定hello.jsp 為網站的預設首頁,我們就可以在這個頁面裡面加
<welcome-file-list> <welcome-file>hello.jsp</welcome-file> </welcome-file-list>
Tomcat-users.xml
配置使用者名稱密碼和使用者的許可權,包括預設頁面的manager
3.lib
該目錄放置 tomcat 執行所需的 jar 包
4.logs
日誌目錄
5.webapps
該目錄下放置我們的web應用
6.work 目錄
用於存放java 檔案被訪問後生成的 .class 和 servlet 檔案
4.web應用目錄規範
如下圖所示:
classes 目錄存放的是 class檔案,lib 目錄存放的是該專案由需要的jar 檔案
5.Tomcat 虛擬目錄
如果我們的 webapps 所在的磁碟已經空間不足,那麼我們能不能實現將web 應用放在別的地方但是還讓tomcat 來管理呢?當然是可以的,我們這個時候就要配置虛擬目錄
我們只要在 server.xml 裡面的 <HOST>
中間新增一個 <Context>
節點,格式如下:
<Context docBase="E:\jspstudy\WWW" path=""></Context>
path 是待會要在url 中顯示的那個目錄,可以隨意指定(視覺效果)
docBase 是絕對路徑
當然除了這兩個引數以外,還有兩個引數需要了解一下
1.reloadable 這個引數設為true 以後 Tomcat 就會及時的發現檔案的變化,然後更新,開銷較大,建議只在開發時開啟
2.upackWAR 這個引數設定為 true 以後我們通過tomcat 上傳的 war 就能自動的解壓,並放在webapps 目錄下
注意:
HOST 標籤可以有很多個,有一個host就有一個虛擬主機
6.配置本地主機名(非必須)
如果我們不想看到loclhost ,根據網頁的請求原理,我們可以在本地的hosts 檔案中配置主機名,配置好了以後可以在 server.xml 中 找到 HOST 標籤修改 name 引數
補充:tomcat 解析資源的流程
tomcat 拿到請求以後會先解析主機(一個tomcat 能管理多個主機),然後會解析web 應用,因為一個主機可能會有多個web 應用,就如我們之前說的可以設定,如下
<Context docBase="E:\jspstudy\WWW" path=""></Context>
最後才是解析資源並獲取資源
7.tomcat 的體系結構
如圖:
在引擎(engine)中可以配置預設主機
8.tomcat 和 servlet 在網路中的位置
如圖所示:
二、servlet 快速入門
1.開發 servlet 有三種方法
(1)實現 servlet 介面
(2)繼承 GeneraicServlet
(3)繼承 HttpSerlvet
1.通過實現 servlet 介面的方式實現
(1)步驟一
下面的程式碼實際上是實現了一個介面,就是把所有的方法都實現了
示例程式碼:
package com.test; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; class myServlet implements Servlet{ //用於初始化 servlet,就是把 servlet 裝載到記憶體中 //該函式只會被呼叫一次 public void init(ServletConfig config) throws ServletException{ } //得到 ServletConfig 物件 public ServletConfig getServletConfig(){ return null; } //該函式是服務函式,我們的業務邏輯程式碼寫在這裡 //瀏覽器每請求一次就會被呼叫一次 public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ System.out.println("hello,world"); } // 該函式得到 servlet 的配置資訊 public java.lang.String getServletInfo(){ return null; } // 銷燬 servlet從記憶體中清除該servlet public void destroy(){ } }
(2)步驟二
根據 servlet 的規範我們還要在 WEB-INF/web.xml 中部署servlet
示例程式碼:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.test.myServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/ABC</url-pattern> </servlet-mapping> </web-app>
解釋:
<servlet>
標籤下的子標籤
1.servlet-name 自定義該servlet 的名字,預設使用該servlet 的檔名
2.servlet-class 指明該 servlet 是放在哪個包下面的,形式:com.test.myServlet
<servlet-mapping>
標籤(servlet 的對映)下的子標籤
1.servlet-name 和上面的保持一致
2.url-pattern 是將來訪問這個 servlet的URL 的一部分,預設是這個servlet 的名字 的資源名(當做出一個請求以後,首先找這個對應的,然後根據名字找到servlet檔案)
(3)步驟三
因為我的web 目錄是在 testweb ,於是我們訪問 localhost:8080/testweb/ABC 就能在控制檯輸出 hello,world
(4)步驟四
現在我們想在網頁上看到返回資訊,而不是在控制檯,那我們只要在 service 這個函式中使用 res.getWriter().println()方法
示例程式碼:
//該函式是服務函式,我們的業務邏輯程式碼寫在這裡 //瀏覽器每請求一次就會被呼叫一次 public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ System.out.println("hello ,world"); res.getWriter().println("hello ,world"); }
補充:
如果用 javac 去編譯一個 java 檔案,需要帶上.(點號)
javac -d . 檔案路徑
2.servlet 的生命週期
1.當 servlet 第一次被呼叫的時候,會觸發 init 函式,然後將servlet例項載入到記憶體,這個函式只會被呼叫一次
2.當第一次以後(包括第一次)就會去呼叫 service 函式,伺服器已經將我們的請求變成一個物件傳遞給這個函式,並將返回結果以一個物件打包(當然還會對其進行解析,將必要的結果返回給瀏覽器)
3.當站點 reload 或者 伺服器 restart 的時候 呼叫 destory 函式,銷燬
2.通過繼承 GeneraicServlet 來實現
這個是因為覺得 servlet 實現五個方法比較煩,於是把其他四個不是很重要的方法隱藏了,就留了一個service
3.通過繼承 HttpServlet 來實現
myHttpServlet.java
package com.test; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class myHttpServlet extends HttpServlet{ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException{//響應 get方式的提交 resp.getWriter().println("呼叫 doGet 方法"); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException{//響應 post 方式的提交 resp.getWriter().println("呼叫 doPost 方法"); } }
web.xml
<servlet> <servlet-name>myHttpServlet</servlet-name> <servlet-class>com.test.myHttpServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myHttpServlet</servlet-name> <url-pattern>/myHttpServlet</url-pattern> </servlet-mapping>
3.servlet 的一些細節
- servlet-mapping 和 servlet-name 並不是一一對映的關係,可以多個url 對映到一個 servlet 上
- url-parttern 對映可以有多層,並且可以是檔名,比如說 hello.html ,但是不要真的以為訪問的是一個html
-
servlet 對映到 URL 也可以使用萬用字元,萬用字元只能有兩種格式,一種就是: .副檔名 另一種是 `/xxx/ `,這種通配可以起到遮蔽資訊,或者統一報錯回覆上
注意:在匹配的時候參考的標準
(1)如果同時匹配到多個,誰長得最像就是訪問誰
(2)*.do 這種形式的優先順序最低
-
servlet 是一個供 servlet 引擎(web伺服器)呼叫的java 類,他不能獨立執行,他的執行完全是由servlet 引擎控制和排程的
-
servlet 在被第一次呼叫後就載入到記憶體,然後記憶體中的 srvlert 就會對各個請求進行服務,不會重新建立,這也就是說 servlet 是單例,可能會出現執行緒安全問題(類變數),於是應該加同步機制 synchromized(this){}
-
<load-on-stratup>
配合執行緒 解決網站啟動時初始化或者定時完成任務的問題
我們在 web.xml 的servlet 標籤中使用
<servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.test2.myServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
這裡的數字代表該servlet 的啟動順序
我們在 servlet 檔案中 寫上
@Override public void init() throws ServletException { super.init(); System.out.println("init 函式被呼叫 "); }
在啟動服務的時候就會被呼叫,而不用特地的去訪問這個servlet ,這就能完成一些類似資料庫之類的東西的初始化操作
3.servletConfig 物件
在 servlet 的配置檔案中可以使用 <init-param>
標籤為 servlet 配置一些初始化的引數
我們在 web.xml 這樣寫
<servlet> <servlet-name>ServletConfigTest</servlet-name> <servlet-class>com.test2.ServletConfigTest</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </servlet>
servlet 檔案中就能用下面的語句去載入這個配置,這樣就不是寫死的了
response.getCharacterEncoding(this.getServletConfig().getInitParameter("encoding"));
當然上面的配置只是針對某一個 servlet 使用,如果想對多個 servlet 都應用的話我們可以
<context-param> <param-name></param-name> <param-value></param-value> </context-param>
三、HttpServletResponse 詳解
web 伺服器收到客戶端的 http 請求以後,會針對每一次請求建立一個代表請求的request 物件 和 代表相應的 response 物件,因此我們要獲取客戶機提交上來的資料只要找request 物件就可以了,如果我們想要向客戶機輸出資料只要找 response 物件就可以了
1.getWriter() 和 getOutputStream() 的區別
1.getWriter() 得到的是 PrintWriter 物件,用於向客戶機會送字元資料
2.getOutputStream() 得到的是 OutputStream物件,用於向客戶機會送字元資料或者二進位制資料
注意:
這兩個流不能同時使用,因為一旦你在一個流中做出了返回,伺服器就會去檢測和這個 response 相關的流有沒有關閉,如果沒有關,則強制關閉,於是下一次再去接收相同的資訊是接收不到的
2.SendRedirect() 重定向
實際上這個重定向也是一個 get 請求,也就是說我們能通過? 利用 get方式傳參
LoginCLServlet.java
if("admin".equals(name) && "123456".equals(pass)){ response.sendRedirect("/UserManager/MainFrameServlet?uname="+name); }else{ response.sendRedirect("/UserLogin/LoginServlet"); }
MainFrameServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); String name = request.getParameter("uname"); out.println("歡迎"+name+"進入管理介面</br>"); out.println("<a href='/UserLogin/LoginServlet'>返回重新登陸</a>"); }
當然這種方法只能傳遞字串,不能傳遞物件,如果想傳遞物件可以用 session 的方式
3.session 傳參
LoginCLServlet.java
if("admin".equals(name) && "123456".equals(pass)){ request.getSession().setAttribute("loginpass",pass); response.sendRedirect("/UserManager/MainFrameServlet?uname="+name); }else{ response.sendRedirect("/UserLogin/LoginServlet"); }
MainFrameServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); String name = request.getParameter("uname"); String pass = (String) request.getSession().getAttribute("loginpass"); out.println("歡迎"+name+"進入管理介面,你的密碼是:"+ pass + "</br>"); out.println("<a href='/UserLogin/LoginServlet'>返回重新登陸</a>"); }
四、j2ee 中文亂碼詳解
1.亂碼在何處發生
只要是在有資料傳遞的地方就會出現中文亂碼
(1)form 表單(get/post)
(2)URL傳參
2.為什麼會有亂碼
我們的UTF-8 的請求首先是伺服器接收,伺服器是外國人編寫的,預設編碼一般為 ISO-8859-1,兩者編碼不一致就會出現亂碼
3.對於 POST 請求
可以使用下面的方式修改請求體中的編碼
request.setCharacterEncoding("utf-8");
但是這個設定對通過 get 方式在請行中傳遞的引數是不起作用的
4.對於GET請求
String u = new String(request.getParameter("username").getBytes("iso-8859-1"),"UTF-8");
5.對於超連結
由於也是通過 get 方式傳參,因此和get 的處理方式一樣
6.對於sendRedirect()
相當於返回到瀏覽器重新發起了 get 請求也是通過 get 請求的方式處理
五、HttpServletRequest 詳解
1.常見函式
(1)getRequestURL()
(2)getRequestURI()
(3)getQueryString()
(4)getRemoteAddr()
(5)getRemoteHost()
(6)getRemotePort()
(7)getLocalPort()
(8)getLocalAddr()
(9)getLocalName()
(10)getHeader()
(11)getHeaderNames()
(12)getParameter()
(13)getParameterValues()
(14)getParameterNames()
2.常見應用
1.實現請求轉發
請求轉發:一個web 資源受到伺服器的請求以後,通知伺服器去呼叫另一個web 資源進行處理的過程
我們可以通過他實現我們之前實現過得跳轉並傳遞引數的功能
request.getRequestDispatcher("轉向的地址").forward(request,response);
上面的程式碼實際上是使用了轉向的方法將 request 和 response 物件轉發到了下一個地址
這句話和 sendRedirect最本質的區別就是,他的轉向不是打回給瀏覽器重新發起請求,而是打回給伺服器