JavaWeb入門筆記


Java web筆記

一、HTTP協議

HTTP(超文本傳輸協議),它是一種主流B/S架構中應用的通信協議。具有以下特點:

1、無狀態

服務端不會記錄客戶端每次提交的請求,服務器一旦相應客戶端之后,就會結束本次的通信過程。客戶端下一次的請求是一個新的 連接,和上一次通信沒有任何關系。

2、簡單靈活

HTTP是基於請求(request)和響應(response)的模型

3、支持客戶端與服務端

支持主流的B/S架構的通信以及C/S架構的通信。

注意:C/S架構可選的協議有多種,例如:TCP/IP,UDP,HTTP

​ 而B/S架構通常只支持HTTP協議

二、服務器

1、概念

服務器通常由硬件和軟件部分構成,統一對用戶提供多種不同的服務。

1、硬件:包括響應的CPU、內存、磁盤等等

2、軟件:包括操作系統、運行環境、服務器軟件、數據庫等等

2、web服務器

web服務器是提供服務端程序運行的一個環境,它本身也是一個軟件。

例如:將我們編寫HTML文件放入到web服務器中,那么外界就可以通過瀏覽器訪問我們的html頁面

常見的web服務器有Apache,Tomcat、Jetty、Nginx等等。

而Tomcat、Jetty這些web服務器更准確的說是一個Servlet容器。

三、JavaWeb項目結構

項目根目錄,例如:myweb、ch01     通常存放靜態資源文件(如:html等等)
  WEB-INF   這個目錄是當前項目私有的一個文件夾,只能提供給項目內部訪問,對於客戶端來說是訪問不到了,通常這個目錄下存放的是Java源代碼、編譯后的字節碼文件以及Servlet的核心配置文件web.xml
    src 存放java源代碼的目錄
    classes 存放編譯后的字節碼文件
    lib lib目錄存放當前項目所需要的jar文件
    JSP 用於存放JSP動態頁面
    web.xml 項目的配置文件,用於配置Servlet的請求映射、過濾器、監聽器等等信息。每一個web項目都對應一個web.xml配置文件
  META-INF   配置應用程序、擴展程序、類加載服務等等

四、Servlet基礎

1、什么是Servlet

Servlet是JavaEE中標准組件,專門用於處理客戶端提交的HTTP請求。並且它必須依賴於Servlet容器才可以運行(Tomcat就是一個標准的Servlet容器),Servlet容器給Servlet提供一個運行環境,所以Servlet組件必須要這個環境中可以運行,而不能脫離這個環境而單獨執行。因為Servlet的實例是由容器創建和銷毀的,並不是通過我們平常使用的new關鍵創建出來。

2、開發一個Servlet的步驟

1.編寫一個類,然后繼承HttpServlet這個父類

2.重寫父類的service方法,這個就是專門處理客戶端請求的方法,這個方法有兩個參數(HttpServletRequest,HttpServletResponse),同時這個方法會拋出兩個異常(ServletException,IOException)

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

//要讓當前的類是一個Servlet,必須繼承HttpServlet
public class HelloServlet extends HttpServlet{

     //重寫父類的service方法,處理客戶端請求,
     //這個方法私有servlet容器去調用,
     //並且request和response參數都是由servlet容器傳遞進來的
     public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         System.out.println("Hello Servlet");
         //響應一些信息回饋給客戶端瀏覽器
         //1.設置要響應的類型,這里就響應簡單的html文本類型
         //通過response參數來進行設置,如:text/html,text/plain
         response.setContentType("text/html;charset=utf-8");
         //2.獲取輸出流並寫回html數據
         response.getWriter().println("<h1>Hello Servlet</h1>");
     }
}

 

 

3.編譯Servlet,需要依賴servlet-api.jar文件

4.編寫web.xml,為servlet配置請求映射的URL

<?xml version="1.0" encoding="utf-8"?>
<!-- 配置根節點 -->
<web-app>
   <!-- 配置servlet類 -->
   <servlet>
         <!-- 指定servlet的別名 -->
         <servlet-name>hello</servlet-name>
         <!-- 指定Servlet的完整類名-->
         <servlet-class>HelloServlet</servlet-class>
   </servlet>
   <!-- 配置請求映射-->
   <servlet-mapping>
          <!-- 這里的servlet-name和上面的servlet-name要一一對應 -->
         <servlet-name>hello</servlet-name>
         <!-- 配置請求映射的url,必須以“/”開頭-->
         <url-pattern>/test</url-pattern>
   </servlet-mapping>
</web-app>

 

5.將項目部署到Tomcat的webapps目錄中

3、servlet處理請求的流程

1.瀏覽器發起http的請求,這個請求首先會被servlet容器(Tomcat)截獲,然后容器會根據web.xml文件中配置servlet的<url-pattern>來找到相應的<servlet-name>這個別名,然后再根據這個別名找到具體Servlet的類,然后容器會創建這個Servlet類的實例並調用Servlet方法來處理這個請求。

請求網頁地址http://127.0.0.1:8080/ch02/test

4、Servlet的生命周期

所謂的生命周期,就是從Servlet的創建一直到它銷毀的整個過程。並且它的 整個生命周期都是由Servlet容器(Tomcat)負責管理和維護的。(補充:在Tomcat中,Servlet是以單實例多線程的方式處理客戶端請求)

4.1 Servlet對象創建的過程

當第一次請求某個Servlet的時候,容器會先查找之前有沒有創建過這個Servlet的實例,如果沒有則創建一個實例並緩存起來。后續 所有請求這個Servlet的時候,都會使用這個緩存的對象來處理客戶端請求。(注意“這里說的是第一次請求時創建。另外一種情況則是在容器啟動的時候就創建Servlet的實例,在web.xml中為Servlet指定<load-on-startup>配置,這個配置的值是一個整形,數值越小,則初始化 的優先級別越高)

4.2 生命周期方法

方法名 描述
init 在Servlet對象創建之后立即執行的初始化方法,且只執行一次
service 核心的請求處理方法,這個方法可以執行多次
destroy 容器准備銷毀Servlet實例之前執行的方法,也是執行一次

 

5、HTTP請求報文

5.1 請求報文

請求行:請求報文的第一行就是請求行。包括請求方法、請求URL地址、HTTP協議版本

請求頭:請求行之后的信息就是請求頭,它是以“名稱:內容”的格式體現。主要包括服務器主機地址及端口號、連接狀態、接收的數據類型、編碼、語言等等

請求體:請求頭結束之后會有一個空行,空行之后就是請求體的內容。通常使用POST提交的數據信息會存放在請求體中,然后傳遞給服務器。

5.2 響應報文

狀態行:主要包括HTTP協議、響應狀態碼(例如:200表示OK,成功響應)

響應頭:主要包括服務器信息、響應的類型及編碼、內容的長度、響應的時間等

響應體:服務器將信息攜帶到響應體中,帶回客戶端。

6、HTTP請求方法

在HTTP/1.1協議中,請求方法主要包括8個,下面列舉常用的請求方法進行說明。

請求方法 說明
GET 向服務器請求指定的資源,並返回響應主體。一般來說GET方法應該只用於數據的讀取(類似於查詢)
POST 向指定的服務器提交數據(例如:表單數據的提交、文件上傳等),並且提交的數據會放入請求體中(類似於新增)
PUT 向服務器提交數據,但是和POST有所區別。如果服務器不存在此資源的時候,則執行新增,如果存在則執行修改。(類似於修改)
DELETE 根據uri的表示刪除服務器上的某個資源(類似於刪除)
... ...

備注:GET與POST區別:

1.GET主要用於獲取數據,POST用於提交數據。

2.GET請求所帶的參數是放在請求行的url地址后面,而POST這是放在請求體中。

3.通常瀏覽器會對GET請求的url長度有所限制 ,而POST通常在請求體中,可以提交更多的數據信息。

4.瀏覽器會對GET請求進行緩存。

7、Servlet的請求處理方法

方法 說明
service 可以處理任何的請求類型
doGet 處理對應的GET請求
doPOST 處理對應的POST請求
doPut 處理對應的PUT請求
doDelete 處理對應的DELETE請求

說明:通過HttpServlet的源代碼得知,默認的所有請求都會先經過service方法,然后service方法根據請求的方法類型判斷來決定交給doGet或者是doPOST方法來處理請求。如果子類重寫了service方法同時還重寫了其他的doXxx的方法,那么只有service方法會處理請求,其他方法將失效。

8、請求和響應對象

當web容器調用某個Servlet的Service方法時,會創建一個HTTPServletRequest和HTTPServletResponse對象作為參數傳入到這個方法中,那么我們可以通過HTTPServletRequest來獲取相關的請求內容等,而響應客戶端可以利用HttpServletResponse對象來完成。

8.1 HttpServletRequest常用API

方法 說明
getParameter(String name) 獲取請求參數的值,根據請求參數的name指定
getParameterValues(String name) 獲取相同name的請求參數,返回的是字符串數組
getParameterMap() 獲取所有請求參數,包括參數名稱和值
getMethod() 獲取請求方法的類型
getHeader(Stirng name) 根據請求頭的名稱獲取響應的信息
getRemoteAddr() 獲取遠程客戶端的IP地址
getServletPath() 獲取Servlet的請求地址,也就是url-pattern
getRequestURL() 獲取請求完整的URL地址
getRealPath(String path) 獲取項目的絕對路徑。(這個方法在request對象中已廢棄,建議通過ServletContext對象獲取)
其他 ...
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        System.out.println("---------------獲取參數值----------------");
        //獲取請求參數,根據請求的參數名
        String name = request.getParameter("uname");
        String age = request.getParameter("age");
        System.out.println(name);
        System.out.println(age);
        //獲取相同參數名的參數
        String[] addrs = request.getParameterValues("address");
        for (String addr : addrs) {
            System.out.println(addr);
        }
        System.out.println("--------------獲取參數名---------------");
        //獲取所有的參數名
        Enumeration<String> es = request.getParameterNames();
        //枚舉使用迭代器來循環遍歷
        while(es.hasMoreElements()) {
            String paramName = es.nextElement();
            System.out.println(paramName);
        }
        System.out.println("---------------獲取所有的參數名和參數值-------------------");
        //map泛型的第一個參數表示請求的參數名稱,第二個參數是請求的參數值,值可以有多個,所以是數組
        Map<String,String[]> map = request.getParameterMap();
        //map的key對應的是參數名,value就是參數的值
        for (String key : map.keySet()) {
            System.out.println("參數名:" + key);
            System.out.println("參數值: ");
            String[] values = (String[])map.get(key);
            for (String value : values) {
                System.out.println(value);
            }
            System.out.println("~~~~~~~~~~~");
        }
        System.out.println("-------------獲取客戶端的請求方法--------------");
        String method = request.getMethod();
        System.out.println("請求方法:" + method);
        System.out.println("--------------獲取請求頭部的信息---------------");
        String header = request.getHeader("Host");
        System.out.println("請求頭信息:" + header);
        System.out.println("--------------獲取遠程客戶端的IP地址--------------");
        String addr = request.getRemoteAddr();
        System.out.println("客戶端的IP地址" + addr);
        System.out.println("------------獲取Servlet的url-pattern---------------");
        String servletPath = request.getServletPath();
        System.out.println("url-pattern: " + servletPath);
        System.out.println("------------獲取請求的完整URL---------------------------");
        String url = request.getRequestURL().toString();
        System.out.println(url);
        System.out.println("-------------獲取項目的絕對路徑------------------");
        String path = request.getServletContext().getRealPath("/");
        System.out.println(path);
    }

 



 

8.2 HttpServletResponse常用API

方法 說明
setContentType(String str) 設置響應內容的類型及編碼
getWriter() 獲取響應字符輸出流
getOutputStream() 獲取字節輸出流
setHeader(String name,String value) 設置響應頭信息,如果存在響應頭信息,則執行更新
addHeader(String name,String value) 設置響應頭,不管存不存在都會新加入一個
setStatus(int code) 設置響應狀態碼
其他 ...
 
        
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //設置響應的內容類型及編碼,包括(text/html,text/plain,application/json)
        response.setContentType("text/html;charset=utf-8");
        //設置響應頭信息
        response.addHeader("myheader", "hello header");
        response.addHeader("myheader", "my servlet");
        //設置響應的狀態碼
        response.setStatus(200);
        //獲取字節輸出流
        OutputStream os = response.getOutputStream();
        //獲取響應的字符輸出流
        PrintWriter pw = response.getWriter();
        pw.println("<html>");
        pw.println("<head><title>index</title></head>");
        pw.println("<body>");
        pw.println("<h3>Hello Servlet</h3>");
        pw.println("</body>");
        pw.println("</html>");
        
    }

 

 

8.3 常見的響應狀態碼

狀態碼 說明
200 請求成功
401 禁止訪問,未授權
404 找不到請求的資源
405 請求的行的方法不被支持
500 服務器內部錯誤
其他 ...

 

 

9、Servlet之間的通信

9.1 轉發

所謂轉發,就是在多個Servlet之間共享請求和響應對象,所有參與轉發過程的Servlet都可以獲取同一個請求對象的信息。在Servlet的API中,轉發的操作是有HttpServletRequest對象完成的。

示例代碼:

public class ServletA extends HttpServlet{
    
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        //獲取頁面提交的參數
        String name = request.getParameter("userName");
        
        //轉發由HttpServletRequest完成
        //第一步先獲取一個請求轉發器RequestDispatcher
        //獲取請求轉發器的同時要告訴轉發器轉發到哪里,轉發給誰
        //如果要轉發給ServletB,那么就是對應ServletB的url-pattern
        RequestDispatcher rd = request.getRequestDispatcher("servletB");
        //調用轉發器的forward方法執行轉發,同時將request和response對象一並轉發ServletB
        rd.forward(request, response);
        
    }
}
public class ServletB extends HttpServlet{

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        //這里在同一個請求中再次獲取頁面的參數
        String name = request.getParameter("userName");
        System.out.println("ServletB獲取請求參數:"+name);
    }
}

 

轉發的特點:

1、URL地址欄不會發生改變

2、轉發的過程是在服務端自動完成

請求作用域:

每一個請求對象都有一個獨立的空間,這個空間我們稱之為請求作用域(RequestScope),可以為當前這個請求攜帶額外的一些數據信息,這些信息同樣可以在多個Servlet之間進行共享。

示例代碼:

 
        
public class ServletA extends HttpServlet{
    
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        
        //在ServletA中為request對象設置請求作用域
        request.setAttribute("age", 35)
    }
}
public class ServletB extends HttpServlet{

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //在ServletB中獲取統一個請求對象作用域的值域的值
        Integer age = (Integer)request.getAttribute("age");
    }
}

 

9.2 重定向

重定向的機制和轉發不同,一次重定向的過程中會有兩次請求和兩次響應,服務端在接收第一次請求后會先做一次302的響應(302表示重定向狀態碼),告訴客戶端瀏覽器必須發起一個新的地址,服務端再次接收這個請求處理,最后再次響應客戶端。

重定向特點:

1、URL地址欄會發生改變

2、重定向的操作是在客戶端瀏覽器完成的

示例代碼:

方式一:

public class ServletC extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //執行重定向
        //方式一:設置302響應狀態碼,並在響應頭中添加location屬性指定重定向的地址
        response.setStatus(302);
        response.addHeader("location", "http://localhost:8080/ch06/servletD");
    }
}

 

方式二:

 
        
public class ServletC extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("請求到達ServletC");
        System.out.println("ServletC獲取請求參數:"+request.getParameter("userName"));
        //執行重定向
        
        //方式二:使用response的sendRedirect方法
        response.sendRedirect("servletD");
    }
}

 

10、會話跟蹤

 

由於HTTP協議是無狀態的,服務端並不會記錄每一個客戶端的狀態,因此,如果想要實現服務器能記錄客戶端的狀態的話,那么就需要會話跟蹤技術。

10.1 cookie

cookie是客戶端瀏覽器的內部的一個文本文件,專門用於記錄服務器發送過來的一些文本信息,那么在每次請求的時候,客戶端都把這個cookie信息由提交回給相應的服務器,那么服務器就可以獲取cookie的信息,達到會話跟蹤的目的。使用cookie的機制是基於客戶端瀏覽器來維護與服務器的狀態跟蹤。

示例代碼:

設置cookie

public class SetCookieServlet extends HttpServlet{

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //創建一個Cookie的實例
        Cookie cookie = new Cookie("userId","10001");
        //將cookie對象設置到響應對象中
        response.addCookie(cookie);
        System.out.println("成功設置cookie");
    }
}

 

獲取cookie

public class GetCookieServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse repsonse) throws ServletException, IOException {
        //cookie是通過request對象來得到的
        //從請求中可以獲取多個cookie對象
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            //判斷cookie,只獲取name為userId的cookie對象
            if("userId".equals(cookie.getName())) {
                System.out.println(cookie.getValue());
            }
        }
    }    
}

 

 

cookie保存中文:

在保存cookie的時候如果需要保存中文,那么中文信息需要經過編碼后才可以寫入cookIe

示例代碼:

編碼使用URLEncoder

 
        
String str=URLEncoder.encode("張三","utf-8");
Cookie cookie = new Cookie("userName",str);

 

解碼使用URLDecoder

 
        
String str=URLDecoder.decode(cookie.getValue(),"utf-8");
System.out.println(str);

 

 

cookie的生命周期:

默認cookie只會保存在瀏覽器進程的內存中,並不會寫入cookie文件,如果關閉了瀏覽器,那么瀏覽器的進程也就消失了,那么對應的內存就會釋放空間,因此cookie的也就銷毀。如果想將cookie寫入文件,那么就必須設置cookie的生命時長,一旦設置了生命時長,那么就表示這個cookie會在文件中保留多長時間,到了這個時間之后,瀏覽器就會自動銷毀這個cookie。

設置cookie存活時間:

 
        
//設置為0表示立即刪除cookie
cookie.setMaxAge(0);
//設置為正數表示cookie在cookie文件的存活時間,單位:秒
cookie.setMaxAge(5);
//設置為-1表示cookie只保留在瀏覽器的進程中,關閉瀏覽器之后會銷毀cookie
cookie.setMaxAge(-1);

 

 

10.2 Session

Session是基於服務端來保存用戶的信息,這個是和cookie的最大區別。不同的客戶端在請求服務器的時候,服務器會為每一個客戶端創建一個Session對象並保存在服務器端,這個Session對象是每個客戶端所獨有的,相互之間不能訪問。服務器為了區別不同的Session屬於哪一個客戶端,因此Session對象也有一個唯一標識,叫做SessionID。而這個SessionID是以cookie的機制保存在客戶端瀏覽器。每次請求的時候,瀏覽器都會把這個SessionID帶回服務端,服務端根據這個SessionID就可以找到對應的Session對象。

示例代碼:

public class SessionServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //HttpSession對象是在第一次調用request的getSession()方法時才會創建
        //注意:getSession()的方法會先判斷之前是否為客戶端創建了session實例,
        //如果創建了,則使用之前創建好的Session對象,沒有則創建一個新的Session
        HttpSession session = request.getSession();
        //創建HttpSession的同時,會創建一個唯一的標識SessionID
        //這個sessionId會保存在瀏覽器的cookie中,每次請求會帶回這個id找到相應的session對象
        String sessionId = session.getId();
        System.out.println(sessionId);
    }
}

 

session的生命周期:

1. SessionId是保存在瀏覽器的cookie中,但是不會寫入cookie文件,這也就表示當關閉瀏覽器之后SessionId就會銷毀。SessionId銷毀以后,服務端的Session就沒有任何作用了。但是服務器並不會立刻銷毀這個Session對象,至於什么時候銷毀是由服務器自己決定的。除非我們手動調用了session.invalidate()方法,服務器就會立即銷毀這個session實例。

示例代碼:

 
        
HttpSession session=request.getSession();
//立即銷毀session
session.invalidate();

 

2.Session默認也有存活時間,服務器在創建Session的時候為Session設置默認的存活時間為30分鍾,如果在30分鍾之內,客戶端沒有發起任何到服務器,那么服務器就會銷毀這個Session對象。我們也可以設置Session的存活時間。可以為當前的Session設置,也可以為全局(服務器端的所有Session)的Session設置。

設置當前Session的存活時:

 
        
HttpSession session=request.getSession();
//設置當前Session的存活時間,單位:秒
session.setMaxInactiveInterval(3600);

 

設置 全局的Session的存活時間 :

在web.xml中進行設置

 
        
<!-- 設置全局Session的存活時間,單位:分鍾 -->
<session-config>
    <session-timeout>60</session-timeout>
</session-config>

 

Session的作用域:

當我們需要將一些數據信息存入Session的時候,就需要操作Session作用域(SessionScope),它和請求作用域類似,也有相應setAttitude和getAttribute方法,只不過Session作用域的范圍要比請求作用域更寬。請求作用域在一次請求響應只有就會消失(因為響應之后請求就會銷毀)。而Session對象只要瀏覽器不關閉或者未超時,那么Session對象會一直駐留在服務器端,因此不管重新請求 多少次還是轉發和重定向,都可以從Session中獲取之前保存的數據信息。

示例代碼:

User user = new User();
user.setUid("1001");
user.setUserName("wangl");
HttpSession session = request.getSession();
//將數據保存在會話作用域中
session.setAttribute("user", user);

從會話作用域取值

HttpSession session = request.getSession();
//取值
User user = (User)session.getAttribute("user");

 

URL重寫:

瀏覽器是可以禁用cookie的,一旦禁用了cookie,那么SessionID將無法寫入cookie的緩存中,這樣就導致無法實現會話跟蹤了,因此解決辦法就是使用URL重寫。URL重寫的目的就是把SessionID放在請求URL地址的后面提交回服務器(類似GET請求后面帶上參數,而這個參數就是一個SessionID),服務器會解析和這個URL的地址並得到SessionID。

示例代碼:

//重寫請求的URL地址,這個地址后面會自動帶上SessionId
String url = response.encodeRedirectURL("getSession");
//重定向URL
response.sendRedirect(url);

 

瀏覽器地址演示

 http://localhost:8080/ch07/getSession;jsessionid=6F1 BA8C92D7E5D7CC479ED8DD30D3ED0

注意:“;”后面跟着就是SessionID

11、Servlet上下文

web容器在啟動時會為每一個web應用創建唯一的上下文對象,這個對象就是Servlet上下文對象(ServletContext),這個上下文對象可以理解為是當前項目的一個共享內存空間,為項目中的所有Servlet提供一個共享的區域。

常用API:

方法 說明
getContextPath() 獲取項目的相對路徑
getRealPath(String path) 獲取項目的絕對路徑
getInitParameter(String name) 獲取上下文的初始化參數(web.xml中配置的)
setAttribute(String name, String value) 將數據放入上下文作用域
getAttribute(String name) 從上下文作用域中去獲取數據

上下文作用域:

上下文作用域是為當前項目所有Servlet提供的一個共享內存區域,可以將需要的數據信息保存在作用域中。這個作用域的的范圍是最大的,只要容器沒有停止,它就會一直存在。

三種作用域:

結合前面所學的作用域,那么一共有三個,分別是:請求作用域,會話作用域,上下文作用域。

范圍從小到大來划分:

請求作用域<會話作用域<上下文作用域

12、過濾器

過濾器可以在請求到達Servlet之前和Servlet響應客戶端之前進行攔截,相當於一個攔截器,主要用於進行一些請求和響應的預處理操作。常用的場景有權限的控制、統一字符編碼等等。

12.1 編寫過濾器

要實現一個過濾器,必須實現一個Filter接口,只有實現了這個接口的類才稱之為過濾器。

示例代碼

 
        
public class DemoFilter implements Filter{
    ...
}

 

web.xml配置過濾器:

 
        
 <filter>
        <filter-name>demoFilter</filter-name>
        <filter-class>edu.nf.ch09.filter.DemoFilter</filter-class>
        <!-- 初始化參數 -->
        <init-param>
            <param-name>param</param-name>
            <param-value>hello</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>demoFilter</filter-name>
        <!-- 什么請求可以經過此過濾器,/*表示所有請求 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 

 

12.2 過濾器的生命周期

與Servlet類似,Filter同樣也是有容器負責創建和銷毀,與Servlet的區別在於,容器會在啟動的時候最先創建所有的過濾器,並執行init方法進行初始化。

生命周期方法:

方法 說明
init 初始化方法,容器啟動時執行一次
doFilter 請求過濾方法,決定請求是否放行
destroy 容器銷毀過濾器之前執行的方法

示例代碼:

 
        
public class DemoFilter implements Filter{
​
    @Override
    public void destroy() {
        
        System.out.println("准備銷毀DemoFilter");
    }
​
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        //FilterChain表示一個過濾鏈對象,因為過濾器可能會存在多個
        //同時這個對象將決定是否放行當前請求,
        //放行的話則請求會繼續到達下一個過濾器或者servlet中
        System.out.println("請求經過DemoFileer..放行");
        chain.doFilter(request, response);
        System.out.println("響應前經過DemoFilter...");
    }
​
    @Override
    public void init(FilterConfig config) throws ServletException {
        String name = config.getInitParameter("param");
        System.out.println("初始化DemoFilter,獲取初始化參數:"+name);
    }
​
}

 

 

12.3 過濾鏈

在一個web項目中可能存在多個過濾器,當有多個過濾器存在的時候就會形成一個過濾鏈。請求會按照過濾器鏈的順序一直傳遞下去,最終到達某個Servlet來處理請求。(注意:過濾鏈的順序是按照web.xml中的先后配置順序決定的)

配置示例:

 
        
<!-- 按先后順序配置 -->
    <!-- 配置第一個過濾器 -->
    <filter>
        <filter-name>firstFilter</filter-name>
        <filter-class>edu.nf.ch09.filter.FirstFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 配置第二個過濾器 -->
    <filter>
        <filter-name>secondFilter</filter-name>
        <filter-class>edu.nf.ch09.filter.SecondFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>secondFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 

 

13、監聽器

監聽器用於監聽對象的上的事件發生,在Servlet中監聽器主要監聽請求對象、會話對象、上下文對象以及監聽這些對象的作用操作。JavaEE為我們提供了一系列的監聽器接口,開發時按需實現響應的接口即可。

13.1 監聽作用域對象的創建於銷毀

監聽器 說明
ServletRequestListener 監聽請求對象的創建和銷毀
HttpSessionListener 監聽會話對象的創建和銷毀
ServletContextListener 監聽Servlet上下文對象的創建和銷毀

代碼實例:

1.請求對象監聽器

 
        
public class DemoRequestListener implements ServletRequestListener{
​
    /**
     * 當請求對象銷毀后容器執行此方法
     * 銷毀方法中同樣也有一個ServletRequestEvent事件對象
     */
    @Override
    public void requestDestroyed(ServletRequestEvent event) {
        //通過這個事件對象就可以獲取當前的請求對象
        HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
        System.out.println("銷毀請求對象..."+request);
    }
​
    /**
     * 當請求對象創建之后容器調用此方法
     * ServletRequestEvent這個參數就是一個事件對象
     */
    @Override
    public void requestInitialized(ServletRequestEvent event) {
        //通過這個事件對象就可以獲取當前的請求對象
        HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
        System.out.println("初始化了請求對象..."+request);
    }
​
}

 

web.xml 配置

 
        
 <!-- 配置監聽器 -->
  <listener>
    <!-- 指定監聽器的完整類名 -->
    <listener-class>edu.nf.ch10.listener.DemoRequestListener</listener-class>
  </listener>

 

2.會話對象監聽器

 
        
public class DemoSessionListener implements HttpSessionListener{
​
    /**
     * 監聽HttpSession對象的創建
     * HttpSessionEvent參數是一個事件對象
     * 通過它可以獲得當前的HttpSession
     */
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        System.out.println("創建了Session對象"+session);
    }
​
    /**
     * 監聽HttpSession對象的銷毀
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        System.out.println("銷毀了Session對象"+session);
    }
}

 

注意:當第一次調用了request.getSession()方法創建Session時,監聽器才會起作用。

web.xml配置

 
        
<listener>
    <!-- 指定監聽器的完整類名 -->
    <listener-class>edu.nf.ch10.listener.DemoSessionListener</listener-class>
  </listener>

 

3.Servlet上下文監聽器

 
        
public class DemoContextListener implements ServletContextListener{
​
    /**
     * 監聽ServletContext的銷毀
     */
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        //通過事件對象獲取ServletContext
        ServletContext sc = event.getServletContext();
        System.out.println("銷毀了ServletContext對象..."+sc);
    }
​
    /**
     * 監聽SerlvetContext的創建
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        //通過事件對象獲取ServletContext
        ServletContext sc = event.getServletContext();
        System.out.println("創建了ServletContext對象..."+sc);
    }
}

 

web.xml


 
<listener>
    <!-- 指定監聽器的完整類名 -->
    <listener-class>edu.nf.ch10.listener.DemoContextListener</listener-class>
  </listener>

 

 

13.2 監聽作用域的操作

監聽器 說明
ServletRequestAttributeListener 監聽請求作用域的操作
HttpSessionAttributeListener 監聽會話作用域的操作
ServletContextAttributeListener 監聽Servlet上下文作用域的操作

示例代碼:

這里以HttpSessionAttributeListener說明,其他作用域監聽器用法相似。

 
        
public class DemoSessionAttributeListener implements HttpSessionAttributeListener{
​
    /**
     * 當有數據添加到會話作用域時,執行此方法
     */
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        //獲取存入作用域的鍵和值
        System.out.println("存入會話作用域..."+event.getName() + " : " + event.getValue());
        
    }
​
    /**
     * 當從會話作用域移除數據時,執行此方法
     */
    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
        System.out.println("移除會話作用域..."+event.getName() + " : " + event.getValue());
        
    }
​
    /**
     * 當替換了會話作用域中的某個數據時,執行此方法
     */
    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        System.out.println("替換的值: "+session.getAttribute("userName").toString());
        //注意:這里event.getValue()獲取到的是被替換的值
        System.out.println("替換會話作用域..."+event.getName() + " : " + event.getValue());
    }
}

 

web.xml

 
        
<listener>
    <!-- 指定監聽器的完整類名 -->
    <listener-class>edu.nf.ch10.listener.DemoSessionAttributeListener</listener-class>
  </listener>

 

14、注解配置

Servlet3.0開始提供了一系列的注解來配置Servlet、Filter、Listener等等。這種方式可以極大的簡化在開發中大量的xml的配置。從這個版本開始,web.xml可以不再需要,使用相關的注解同樣可以完成相應的配置。

注解 說明
@WebServlet 這個注解標識在類上,用於配置Servlet。例如:@WebServlet(name="hello",urlPatterns="/hello")也可以簡化配置@WebServlet("/hello")
@WebFilter 這個注解標識在類上,用於配置Filter。例如:@WebFilter(filterName="encode",urlPatterns="/*")也可以簡化配置@WebFilter("/*")
@WebListener 這個注解標識在類上,用於配置監聽器

15、文件上傳

從Servlet3.0開始提供了文件上傳的功能,操作起來更加的簡單和方便。

15.1

想要使用這個功能,首先必須在web.xml或者使用注解開啟Servlet的上傳功能,否則無效

xml配置:

<servlet>
        <servlet-name>upload</servlet-name>
        <servlet-class>edu.demo.UploadServlet</servlet-class>
        <!-- 開啟上傳功能 -->
        <multipart-config/>
    </servlet>
    <servlet-mapping>
        <servlet-name>upload</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>

 

注解配置:

@WebServlet("/login")
@MultipartConfig //開啟上傳功能
public class LoginServlet extends HttpServlet{
    ...
}

 

參數說明:

參數 說明
location 指定文件上傳的目錄
maxFileSize 限制單個文件上傳的大小
maxRequestSize 限制一次請求上傳總文件的大小
fileSizeThreshold 設置緩存的大小,當達到緩存大小的時候,會將內存的數據寫入location指定的目錄中(也就是寫入磁盤)

15.2

文件上傳的核心接口是Part,通過HTTPServletRequest對象可獲得該接口的實體類對象

//獲取單個文件的Part
Part part = request.getPart("name");
//獲取多個上傳文件的Part
Collection<Part> cool=request.getParts();

 

常用API:

方法 說明
getContentType() 獲取上傳的文件類型
getSize() 獲取上傳文件的大小
getSubmittedFileName() 獲取上傳文件的文件名
write() 將文件上傳(寫入)到指定位置

5.13

當客戶端使用form表單來上傳文件時,必須將表單的enctype屬性設置為"multipart/form-data"

 <form method="post" action="upload" enctype="multipart/form-data">
     Username:<input type="text" name="userName"/><br/>
     <!-- input使用file類型 -->
     File:<input type="file" name="file"/><br/>
     <input type="submit" value="submit"/>
 </form>

 

案例:

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
​
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //指定上傳的路徑
        String uploadPath = "/Users/wangl/uploads";
        //創建文件夾,如果不存在的情況下
        File dir = new File(uploadPath);
        if(!dir.exists()){
            dir.mkdir();
        }
​
        //上傳單個文件,參數對用input的name屬性的值
        //Part part = req.getPart("file");
        //uploadPath = uploadPath + "/" + part.getSubmittedFileName();
        //執行上傳
        //part.write(uploadPath);
        //獲取文件的類型
        //System.out.println(part.getContentType());
        //獲取文件的大小
        //System.out.println(part.getSize());
        //獲取上傳的文件名
        //System.out.println(part.getSubmittedFileName());
//上傳多個文件
        Collection<Part> parts = req.getParts();
        for (Part p : parts) {
            p.write(uploadPath + "/" + p.getSubmittedFileName());
        }
    }
}

 


upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>文件上傳</h2>
 <form method="post" action="upload" enctype="multipart/form-data">
     Username:<input type="text" name="userName"/><br/>
     File1:<input type="file" name="file"/><br/>
     File2:<input type="file" name="file"/><br/>
     <input type="submit" value="submit"/>
 </form>
</body>
</html>

 

 

五、JSP基礎

1、簡介

JSP全名為Java Server Pages,中文名叫java服務器頁面,是一種動態頁面技術,而HTML是屬於靜態頁面JSP可以在HTML中嵌入java腳本代碼,因為JSP本質上還是一個Servlet,因此JSP也必須依賴於web容器才能運行。JSP的出現並不是為了取代Servlet,而是簡化了Servlet的工作,將Servlet中繁瑣的視圖呈現代碼脫離出來,交給JSP來完成,讓Servlet專注於請求的處理,所以在開發中通常將JSP和Servlet結合一起使用。

2、JSP引擎

由於JSP本質上就是一個Servlet,那么JSP引擎主要負責將JSP文件轉義成一個Servlet的java源文件,然后再通過javac將這個源文件,編譯成class字節碼文件並裝載到JVM中執行。JSP引擎的核心類是JSPServlet,位於Jasper.jar文件中,並且在Tomcat的web.xml中也默認就配置好了這個類(JspServlet也是一個Servlet)。因此,凡是以".jsp"結尾的請求都會先經過JspServlet,那么這個引擎就可以開始工作。通常引擎轉義和編譯后的文件放在容器的work工作目錄中。

注意:如果第一次訪問Jsp文件的時候,由於work目錄中並不存在源文件和字節碼文件,JSP引擎就必須完成這兩個工作,因此,有可能在第一次訪問JSP時會比較緩慢。當字節碼編譯出來加載后,第二次訪問時速度就會很快了。

3、JSP的三大元素

3.1 指令元素

語法:<%@ %>

指令 說明
page指令 用於設置JSP頁面的相關信息以及編碼
include指令 用於靜態包含其他的JSP頁面代碼,所謂靜態包含,就是在編譯期,將另外的JSP頁面的代碼合並到當前的JSP中,最終只會產生一個java源文件
taglib 這個指令用於引入標簽庫

 

3.2 動作元素

語法:< jsp:xxx >

動作 說明
include 動態包含其他JSP頁面的內容,所謂的動態包括是指在編譯期將不同的JSP文件轉義成不同java源文件,然后在運行時在將目標內容包含到當前的JSP頁面中
forward 相當於Servlet中的轉發,轉發到其他的JSP頁面或者Servlet
param 用於傳遞參數,通常結合其他的動作一起使用,例如轉發時需要提交一些額外的參數
useBean 用於在JSP頁面中使用JavaBean對象,通常結合setProperty和getProperty來使用,完成bean對象的賦值和取值操作

3.3 腳本元素

腳本元素主要就是在JSP中嵌入java腳本代碼,包括聲明、表達式、java腳本

聲明語法:<%! %>

表達式:<%= %>

java腳本:<% %>

示例代碼:

 
        
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
   <%-- 聲明變量和方法,這里聲明的變量a是實例變量 --%>
   <%! 
        int a = 10;
        public void say(){
            System.out.println("hello");
        }
    %>
    
    <%-- 表達式,注意:表達式后面是不允許有;號結束的 --%>
    3 + 1 = <%=3+1%><br/>
    
    <%-- Java腳本,腳本代碼最終會生成在servlet中的service方法中作為代碼片段 --%>
    <table border="1">
        <tr>
           <th>Name</th>
           <th>Age</th>
        </tr>
        <% for(int i=0;i<5;i++){%>
          <tr>
            <td>user<%=i%></td>
            <td><%=10+i%></td>
          </tr>
        <%}%>
    </table>
</body>
</html>

 

 

4、JSP內置對象

內置對象,是在容器運行時將創建好的9個對象內嵌在JSP中,在JSP里可以直接拿來使用的對象。

對象 說明
out 字符流輸出對象
config 等同於Servlet中的ServletConfig
page 表示當前JSP頁面,類似於Java類中的this關鍵字
request 等同於 Servlet中的HTTPServletRequest
response 等同於 Servlet中的HTTPServletResponse
session 等同於Servlet中的HTTPSession
application 等同於Servlet中的Servlet
pageContext 表示當前JSP頁面的上下文對象
exception 表示當前JSP中的異常對象

示例代碼:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <%-- 常用內置對象 --%>
  <%
     //使用request,API使用同HttpServletRequest一樣
     request.getParameter("userName");
     request.setAttribute("user", "user1");
     request.getAttribute("user");
     
     //使用session,API使用等同於HttpSession
     session.setAttribute("user", "user2");
     session.getAttribute("user");
     session.getId();
     
     //使用response
     //response.sendRedirect("demo.jsp");
     
     //out對象,等同於字符輸出流對象,在JSP頁面輸出相關內容
     out.println("hello world");
     //這個是輸出在控制中
     System.out.println("hello");
     
     //使用application,等同於ServletContext
     application.setAttribute("userName", "user3");
     
     //pageContext使用
     //從pageContext中獲取相關的其他對象
     HttpServletRequest req = (HttpServletRequest)pageContext.getRequest();
     HttpServletResponse res = (HttpServletResponse)pageContext.getResponse();
     HttpSession ses = pageContext.getSession();
     ServletConfig conf = pageContext.getServletConfig();
     ServletContext sc = pageContext.getServletContext();
     //也可以通過pageContext來統一設置不同的作用域
     //第三個參數表示要放入到哪個作用域,是一個int類型的參數
     //1代表page作用域(當前頁面有效)PageContext.APPLICATION_SCONPE
     //2代表請求作用域 PageContext.SESSION_SCOPE
     //3代表會話作用域 PageContext.REQUEST_SCOPE
     //4代表上下文作用域 PageContext.PAGE_SCOPE
     pageContext.setAttribute("userName", "zhangsan", 2);
     //也可以指定中哪個作用域取出相應的值
     String name = (String)pageContext.getAttribute("userName", 2);
     out.println(name);
  %>
</body>
</html>

 

 

5、EL表達式

EL(Expression Language),全球叫做表達式語言,是JSP2.0推出的一種技術。主要簡化了在JSP中使用Java腳本表達式。EL表達式的特點在於使用簡單,支持四則運算、邏輯運算等,並且還可以對Servlet API中的對象進行數據訪問。EL的語法:${expression}

運算:

示例 結果
${1+1} 2
${2*2} 4
${1==1} true
${2>5} false
${"10"==10} true
其他 ...

數據訪問:

1.使用“.”來訪問

示例 說明
${param.參數名} 獲取請求參數的值,相當於使用request.getParameter()方法
${requestScope.xxx} 從請求作用域中訪問數據
${sessionScope.xxx} 從會話作用域中訪問數據
${applicationScope.xxx} 從上下文作用域中訪問數據
${xxx} 不指定作用域范圍時,默認按照作用域范圍從小到大的順序自動查找
其他 ...

示例代碼:

 
        
姓名:${param.userName}<br/>
   <%-- 訪問請求作用域 --%>
   請求作用域:${requestScope.userName}<br/>
   會話作用域: ${sessionScope.userName}<br/>
   上下文作用域: ${applicationScope.userName}<br/>
   自動從作用域中取值: ${userName}<br/>
   
   <h2>訪問對象中的屬性</h2>
   Name:${userName}<br/>
   Age:${age}

 

 

2.使用"[]"來訪問

示例 說明
${requestScope[“userName”]} 從請求作用域中取值
${sessionScope[“userName”]} 從會話作用域取值
其他... 同上

示例代碼:

<h2>使用"[]"來訪問數據</h2>
<!-- 可以獲取使用帶.的參數名稱-->
   請求作用域:${requestScope["user.userName"]}<br/>
   會話作用域: ${sessionScope["user.userName"]}<br/>
   上下文作用域: ${applicationScope["user.userName"]}<br/>

 

 

注意:通常使用"[]"來訪問數據的時候,主要是訪問一些特殊的名稱,例如:request.setAttribute("user.userName")

這種方式如果使用${requestScope.user.userName}是訪問不到的,應該改為

${requestScope["user.userName"]}來訪問

6、JSTL核心標簽庫

JSTL是JSP中的標准標簽庫,主要用於取代JSP中大量的Java腳本代碼,讓頁面看起來更趨向於HTML。使用也很簡單,通常結合EL表達式一起使用

示例:

核心標簽庫(core) 說明
c:out 輸出標簽
c:set 聲明某個變量並存入指定的作用域
c:redirect 重定向標簽
c:if 條件判斷
c:forEach 循環標簽
其他 ...

備注:其他標簽庫請參閱相關官方文檔

代碼示例:

 
        
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>   
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSTL</title>
</head>
<body>
  <%
    out.println("hello JSTL");
  %>
  <!-- 使用輸出標簽替換上面的腳本 -->
  <c:out value="hello JSTL"></c:out>
  
  <%
    session.setAttribute("userName", "wangl");
  %>
  <!-- 使用set標簽替換上面的腳本,var指定變量名,value指定值,scope指定要放入的作用域 -->
  <c:set var="userName" value="wangl" scope="session"></c:set>
  <!-- 重定向標簽 -->
  <%--<c:redirect url="demo.jsp"/>--%>
  
  <!-- 使用條件判斷 -->
  <c:set var="age" value="70"/>
  <c:if test="${age<=18}">
    <h2>很年輕</h2>
  </c:if>
  <c:if test="${age>18 && age<50}">
    <h2>還好</h2>
  </c:if>
  <c:if test="${age>60}">
    <h2>老了</h2>
  </c:if>
</body>
</html>

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>       
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <!-- 聲明變量i,起始為0, 到10結束 -->
  <%-- 
  <c:forEach var="i" begin="0" end="10">
     ${i}<br/>
  </c:forEach>
  --%>
  <table border="1">
     <tr>
        <th>姓名</th>
        <th>年齡</th>
        <th>性別</th>
        <th>籍貫</th>
        <th>班級</th>
     </tr>
     <!-- list中存放的map “List<Map<String,Object>>”-->
     <%-- 
     <c:forEach items="${list}" var="m">
        <tr>
            <td>${m.s_name}</td>
            <td>${m.s_age}</td>
            <td>${m.s_sex}</td>
            <td>${m.origin}</td>
            <td>${m.c_name}</td>
        </tr>
     </c:forEach>
     --%>
     
     <!-- list中放的是Object[]  “List<Object[]>” -->
     <c:forEach items="${requestScope.list}" var="o">
        <tr>
            <td>${o[0]}</td>
            <td>${o[1]}</td>
            <td>${o[2]}</td>
            <td>${o[3]}</td>
            <td>${o[4]}</td>
        </tr>
     </c:forEach>
  </table>
  
</body>
</html>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM