0%

JavaWeb Review

JavaWeb

  1. Java Web基礎知識
    • 資源分類
      • 靜態資源:所有用戶訪問之後,得到的結果都是一樣的,比如HTML,CSS,JavaScript。 瀏覽器請求靜態資源,客戶端可以直接把靜態資源返回給瀏覽器,瀏覽器内置了靜態資源解析器可以直接解析
      • 動態資源:每個用戶訪問相同資源之後,得到的結果可能不一致,比如Servlet,JSP。瀏覽器請求動態資源,服務器要把動態資源轉換成靜態資源,然後把靜態資源返回給瀏覽器
    • 網路通信三要素:IP地址,端口,傳輸協議
    • 常見的Java web服務器軟件
      • webLogic:Oracle,大型JavaEE服務器,支持所有JavaEE規範,收費產品
      • WebSphere: IBM,大型JavaEE服務器,支持所有JavaEE規範,收費產品
      • JBoss:JBoss, 大型的JavaEE服務器,支持所有JavaEE規範,收費產品
      • Tomcat: Apache, 中小型JavaEE服務器,支持少量的JavaEE規範,比如servlet,jsp,免費的
    • Web項目結構
      • src目錄放java代碼
      • web目錄放各種資源
        • WEB-INF目錄放web.xml,claasses目錄(設置爲output), lib目錄(設置為jar directory,注意爲了讓Idea識別Servlet接口,要把Tomcat的lib目錄下的servlet-api.jar放到lib目錄中)。WEB-INF目錄下的資源不能被瀏覽器訪問到
  2. Tomcat
    • 啓動:windows上面用startup.bat, linux上面用startup.sh。 如果沒有配置JAVA_HOME, 那麽startup.bat不能用
    • 關閉:直接關閉startup.bat的窗口,應該用shutdown.bat或者在startup.bat中用Ctrl+C關閉
    • 部署項目的方式
      • 方法1:把項目放在Tomcat安裝目錄的webapps目錄下面。 或者把項目打包成.war文件,放在Tomcat安裝目錄的webapps目錄下面,Tomcat會把.war文件自動解壓縮。 這種方法虛擬目錄名字就是項目名字
      • 方法2:修改Tomcat的conf目錄下的server.xml文件,在<Host>内部添加一個<Context docBase=項目實際存放路徑 path=項目訪問路徑(虛擬路徑)/>標簽。 這種方法虛擬目錄名字就是path設置的虛擬目錄名字
      • 方法3:在Tomcat根目錄/conf/Catalina/localhost目錄下面新建一個xml文件,在上面添加一個<Context docBase=項目實際存放路徑/>標簽。這個方法虛擬目錄名字就是xml文件名字。 這個是熱部署方式,推薦使用
    • 訪問項目方式: localhost/虛擬目錄/項目資源
    • IDEA新建JavaWeb項目并且集成Tomcat
      • 新建Java項目,項目右鍵選擇Add Framework Support, 選擇Web Application項目,勾選create web.xml
      • Run -> Edit Configurations -> 左邊加號選擇 Tomcat Server Local -> 右邊Server下面選擇Tomcat服務器版本, 在On Update actionOn frame deactivation選擇Update classes and resources;在Deployment下面添加當前項目,根據需求修改Application context(虛擬目錄名字)
  3. Servlet
    • Servlet是server applet的縮寫,意思是運行在服務器的小程序
    • 瀏覽器訪問一個動態資源的時候,需要在服務器上有一個類來處理請求,這個類就是Servlet的實現類。Servlet是一個接口,定義了Java類被瀏覽器訪問到(被Tomcat識別到)的規則,我們需要實現Servlet接口,重寫對應的方法
    • XML配置Servlet: 爲了讓資源和Servlet類對應起來,需要在web.xml中配置servlet和servlet-mapping
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <!-- 配置Servlet類 -->
      <servlet>
      <!-- name是Servlet的識別名稱 -->
      <servlet-name>intro</servlet-name>
      <!-- class是Servlet類的全限定類名 -->
      <servlet-class>com.yanxuanshaozhu.web.servlet.ServletDemo</servlet-class>
      </servlet>
      <!-- 配置Servlet類和資源路徑的對應 -->
      <servlet-mapping>
      <!-- Servlet類的識別名稱 -->
      <servlet-name>intro</servlet-name>
      <!-- 資源路徑的URL,以/開頭 -->
      <url-pattern>/intro</url-pattern>
      </servlet-mapping>
    • Servlet3.0及以上版本支持用注解配置Servlet,不需要web.xml:在類上面使用@WebServlet(urlPatterns = "/資源路徑"),簡寫為@WebServlet("/資源路徑")。一個注解可以定義多個訪問路徑:@WebServlet("/資源路徑1", "/資源路徑2, ..., "/資源路徑n"), 資源路徑也可使使用通配符,例如@WebServlet("/*")
    • 訪問資源和Sevlet對應:服務器收到請求後解析訪問的URL,遍歷web.xml搜索對應的url-pattern標簽,然找到servlet-class對應的類名,把字節碼加載到服務器内存中,然後通過反射創建實例,調用類中的方法
    • Servlet的生命周期
      • 創建:
        • 默認第一次資源被訪問的時候,Servlet被創建,然後調用init方法,init方法只執行一次。
        • 可以通過配置把Servlet改成在啓動服務器后創建,創建後調用init方法,需要在web.xml進行如下配置:
          1
          2
          3
          4
          <servlet>
          <!-- 啓動時創建,n>= 0;第一次訪問創建,n < 0。默認值n = -1,默認不用配置這個-->
          <load-on-startup>n</load-on-startup>
          </servlet>
        • Servlet只會創建一次,是單例對象,多綫程創建可能會有安全問題。所以不要在Servlet類中定義成員變量,防止多綫程共享數據不一致問題
      • 提供服務: 每次資源被訪問的時候,service方法被調用,這個方法可以被多次調用
      • 銷毀:
        • 在服務器正常關閉的時候,Servlet被銷毀,然後調用destroy方法,destroy方法只執行一次
        • 如果服務器不是正常關閉,destroy方法也不會被調用
      • getServletConfig():獲取Servlet配置, getServletInfo:獲取Servlet信息
    • Servlet體系結構
      • Servlet是接口
      • GenericServlet是實現Servlet接口的抽象類,它對Servlet接口方法做了默認空實現,所以繼承GenericServlet只需要重寫service方法
      • HttpServlet是繼承GenericServlet抽象類,他是對HTTP協議的封裝,繼承HttpServlet一般只需要重寫doGetdoPost方法
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        @WebServlet("/login")
        public class HttpWebServlet extends HttpServlet{

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGot!");
        }

        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPosted!");
        }
        }
  4. HTTP概述
    • HTTP是基於TCP/IP的高級協議,默認端口號是80,基於請求響應模型(請求和相應一一對應), 無狀態(每次請求是獨立的,不能交互數據)
    • 請求消息格式
      • 請求行:格式是 "請求方式 URL 請求協議/版本",
      • 請求頭:格式是 "key:value", User-Agent表示瀏覽器版本信息,服務器可以用這個來解決兼容性問題,Referer:告訴服務器當前請求從哪裏來(防止盜鏈,便於統計)
      • 請求空行: 空行,用來分割請求頭和請求體
      • 請求體: 格式是"key=value",GET方式請求參數在URL後面,POST方式請求參數在請求體中
    • Request對象
      • ServletRequest對象和ServletResponse對象:收到請求後,服務器創建req和res對象,把請求消息封裝到req中,把req和res傳給service方法,在service方法中用req獲取請求消息,對請求進行處理,把響應消息封裝到res中,最後服務器從res中拿出響應消息返還給瀏覽器
      • ServletRequestHttpServletRequest都是接口,RequestFacade實現了HttpServletRequest接口,這個類就是Tomcat用的實現類
      • Request對象功能
        • 獲取請求數據
          • 獲取請求行: req.getMethod()獲取請求方式,req.getContextPath() 獲取虛擬目錄, req.getServletPath() 獲取Servlet路徑,req.getQueryString() 獲取請求參數,req.getRequestURL() 獲取請求URL,req.getProtocol()獲取請求協議和版本, req.getRemoteAddr() 獲取客戶端ip地址
          • 獲取請求頭: req.getHeader(key) 通過請求頭名稱獲取請求頭值, req.getHeaderNames()獲取所有請求頭名稱
            1
            2
            3
            4
            5
            6
            Enumeration<String> names = req.getHeaderNames();
            while (names.hasMoreElements()) {
            String name = names.nextElement();
            String value = req.getHeader(name);
            System.out.println(name + " => " + value);
            }
          • 獲取請求體:請求體數據是流,需要先獲取流,再獲取具體數據。req.getReader()獲取字符流數據,只適用於字符數據,req.getInputStream()獲取字節流流數據,適用於所有數據類型
          • 獲取請求參數的通用方法:req.geParameter(key):獲取value,req.getParameterNames():獲取所有key,req.getParameterValues(key):獲取所有value,適用於複選框,req.getParameterMap():獲取參數的k-v組合
        • 請求轉發
          • 請求轉發是在服務器内部的一種資源跳轉方式,Servlet需要各自功能邏輯相對獨立,因此一個請求可能需要多個Servlet進行協作,因此就需要請求轉發
          • 流程:
            • 通過request對象獲取請求轉發器對象: req.getRequestDispatcher(redirectTargetPath)
            • 通過請求轉發器進行轉發: dispatcher.forward(req, res)
          • 轉發的特點
            • 轉發之後瀏覽器地址欄路徑沒有變化(轉發是服務器内部的,客戶端不知道發生了轉發)
            • 轉發只能轉發到服務器内部
            • 轉發的時候客戶端只對服務器發送了一次請求
        • 數據共享
          • 域對象:一個有作用範圍的對象,範圍内部可以共享數據
          • request域:一次請求的範圍,因爲轉發只有一次請求,因此多個Servlet可以共享數據
          • request實現共享數據:req.setAttribute(key, value),一個Servlet設置了req屬性之後,請求轉發的另一個servlet可以通過req.getAttribute(key)獲取
        • 獲取ServletContext對象: 通過req.getServletContext()獲取
    • 一個登錄案例
      • 用戶登錄:成功顯示用戶名成功,失敗顯示失敗
      • 邏輯:用戶html頁面提交post請求到loginServlet,在裏面用把請求參數封裝成User對象,通過UserDao和數據庫交互,拿到返回的User對象,如果不是null,把返回的User對象封裝到request中,并且通過請求轉發器轉發到successServlet,輸出成功結果,如果是null,通過請求轉發器轉發到failServlet,輸出失敗結果
      • 頁面顯示中文: resp.setContentType("text/html;charset=utf-8");可以,但是resp.setCharacterEncoding("utf-8");不行
      • UserDao放在dao目錄下面,使用JdbcTemplate和數據庫進行交互
      • User放在domain目錄下面
      • JDBCUtils放在util目錄下面
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        public class JDBConnector {

        private static ComboPooledDataSource ds = new ComboPooledDataSource();

        // 初始化連接池
        static {
        Properties prop = new Properties();
        try {
        BufferedReader buffer = new BufferedReader(new FileReader("D:\\MyWork\\Java\\JavaWeb\\LoginDemo\\src\\db.properties"));
        prop.load(buffer);
        ds.setDriverClass(prop.getProperty("driver"));
        ds.setJdbcUrl(prop.getProperty("url"));
        ds.setUser(prop.getProperty("username"));
        ds.setPassword(prop.getProperty("password"));
        } catch (Exception e) {
        e.printStackTrace();
        }
        }

        /**
        * 獲取連接池對象
        */

        public static DataSource getDataSource() {
        return ds;
        }

        /**
        * 獲取鏈接對香港
        */
        public static Connection getConnection() throws SQLException {
        return ds.getConnection();
        }
        }
    • 響應消息格式
      • 響應行
        • 響應行格式: "協議/版本 響應狀態碼 響應狀態碼描述"
        • 響應狀態碼:
          • 1xx: 服務器接受數據沒接收完成,等待后發送1xx給客戶端
          • 2xx: 成功
          • 3xx: 302重定向,304訪問緩存
          • 4xx: 客戶端錯誤,404請求路徑沒有資源,405請求方式在服務器沒有對應了方法
          • 5xx: 服務器錯誤,500服務器内部異常
      • 響應頭:格式是 "key:value", Content-Type服務器告訴客戶端響應躰數據格式和編碼格式, Content-disposition服務器告訴客戶端以什麽格式打開響應躰數據,比如in-line或者attachment
      • 響應空行: 空行,用來分割響應頭和響應躰
      • 響應體: 服務器返回給客戶端的響應數據
    • Response對象
      • ServletResponseHttpServletResponse是接口
      • Response對象功能
        • 設置響應消息
          • 設置響應狀態碼: res.setStatus(int sc)
          • 設置響應頭: res.setHeader(String name, String value)
          • 設置響應躰
            1. 獲取輸出流
              • 字符輸出流: res.getWriter()
              • 字節輸出流: res.getOutputStream()
            2. 使用輸出流,將數據輸出到客戶端
        • 重定向
          • 定義:瀏覽器請求服務器,服務器告訴瀏覽器重定向到另一個地址,就是重定向
          • 重定向步驟
            1. 瀏覽器設置響應狀態碼302: res.setStatus(302)
            2. 瀏覽器在響應頭中設置key是location, value是重定向目標的新資源地址:res.setHeader("location", "/虛擬目錄/新資源地址")
            3. 另一種方法:因爲重定向和狀態碼和響應頭key是固定的,只需要傳入重定向地址,因此可以直接使用req.sendRedirect(String location)即可完成重定向
          • 重定向特點
            • 重定向瀏覽器地址欄發生變化
            • 重定向可以訪問外部資源
            • 重定向至少兩次請求,不能使用request域對象來共享數據
          • 路徑的寫法
            • 相對路徑
            • 絕對路徑:如果請求是從客戶端訪問的,絕對路徑是/虛擬目錄/資源路徑,虛擬目錄使用req.getContextPath()獲取,比如重定向中的路徑;如果請求是從服務器訪問的,絕對路徑是/資源路徑,比如轉發中的路徑
        • 設置驗證碼
          • 驗證碼是一張圖片,目的是防止惡意表達注冊
          • 驗證碼可以從圖庫中挑選,也可以用程序隨機生成: 基礎的可以把程序生成的驗證碼圖片通過字節流輸出到瀏覽器中
          • 生成驗證碼的Java代碼
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 1. 創建圖片
            // 單位是pixel
            int width = 100;
            int height = 50;
            // 在内存中創建驗證碼圖片
            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            // 2. 美化圖片
            // 獲取畫筆
            Graphics g = img.getGraphics();
            // 設置畫筆顔色
            g.setColor(Color.pink);
            // 畫筆畫一個舉行填充背景顏色
            g.fillRect(0, 0, width, height);
            g.setColor(Color.blue);
            g.drawRect(0, 0, width - 1, height - 1);
            // 設置驗證碼文字内容
            g.setColor(Color.black);
            String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxya0123456789";
            Random rand = new Random();
            for (int i = 1; i <= 4; i++) {
            int index = rand.nextInt(base.length());
            String sub = base.charAt(index) + "";
            g.drawString(sub, width / 5 * i, height / 2);
            }
            // 加上干擾綫
            g.setColor(Color.green);
            for (int i = 0; i < 10; i++) {
            int x1 = rand.nextInt(width);
            int x2 = rand.nextInt(width);
            int y1 = rand.nextInt(height);
            int y2 = rand.nextInt(height);
            g.drawLine(x1, x2, y1, y2);
            }
            // 3. 將圖片輸出到瀏覽器頁面上
            ImageIO.write(img, "jpg", resp.getOutputStream());
            }
          • 在瀏覽器中點擊更換驗證碼圖片
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            <!DOCTYPE html>
            <html lang="en">
            <head>
            <meta charset="UTF-8">
            <title>Title</title>
            <script type="text/javascript">
            window.onload = () => {
            let img = document.getElementById("verificationcode");
            let div = document.getElementById("change");
            // 因爲瀏覽器對同一個地址會緩存,所以要對地址加上一段無重複數據保證加載的是新生成的驗證碼
            div.onclick = () => {
            let time = new Date().getTime();
            img.src = "/verificationCode?" + time;
            }
            }
            </script>
            </head>
            <body>
            <img src="/verificationCode" id="verificationcode">
            <br>
            <div id="change">點擊切換驗證碼</div>
            </body>
            </html>
    • ServletContext對象
      • 概念:代表整個web應用,可以和程序的容器(服務器)進行通信
      • 獲取ServletContext對象
        • 通過了request對象獲取: req.getServletContext()
        • 通過HttpServlet對象獲取: this.getServletContext()
      • 功能
        • 獲取MIME類型
          • MIME類型是在互聯網通信過程中定義的一種文本數據類信得過
          • MIME類型格式: 大類型/小類型, 如: text/html, image/jpeg
          • 獲取MIME類型的方法: context.getMimeType(String fileName),實際上通過擴展名來獲取的
        • 共享數據
          • ServletContext對象是一個域對象,可以共享數據
          • context.getAttribute(String name), context.setAttribute(String name, Object value), context.removeAttribute(String name)
          • 域範圍:可以共享服務器上所有的數據, 比如一個Servlet可以訪問另一個Servlet的數據
        • 獲取文件的真實(服務器)路徑
          • 意義:先獲取到配置文件在服務器上的真實路徑,然後再讀取配置文件
          • 獲取方法:context.getRealPath(String path)
            • 如果文件在web目錄下面,那麽path就是/文件名
            • 如果文件在web/WEB-INFO目錄下面,那麽path就是/WEB-INFO/文件名
            • 如果文件在src目錄下面,那麽path就是/WEB-INFO/classes/文件名,因爲src所有的文件會被服務器放到web/classes目錄下面
          • 在非Servlet類中讀取文件路徑
            1
            2
            3
            4
            5
            Properties prop = new Properties();
            // 直接寫db.properties,db.properties文件放在src下面即可
            String path = TestDemo.class.getClassLoader().getResource("db.properties").getPath();
            FileInputStream fis = new FileInputStream(path);
            prop.load(fis);
  5. 會話
    • 基本概念
      • 定義:瀏覽器第一次給服務器資源發送請求,會話建立,知道有一方斷開爲止
      • 一次會話中包含多次請求和響應
      • 會話技術的實現
        • 客戶端會話技術: Cookie
        • 服務器端會話技術: Session
    • Cookie
      • 定義:將數據保存在客戶端的會話技術
      • Cookie作用
        • 一般用於存儲少量不敏感的數據
        • 在不登陸的情況下,完成服務器對客戶端的身份識別
      • 流程:
        1. 服務器創建Cookie,在response中返回給瀏覽器: new Cookie(String name, String value), resp.addCookie(Cookie cookie)
        2. 瀏覽器收到Cookie中進行存儲,在下一次發送請求的時候會自動把Cookie發送給服務器
        3. 服務器收到請求,拿到Cookie中的數據:req.getCookies()拿到Cookie數組, cookie.getName(), cookie.getValue()拿到Cookie的key和value
      • Cookie實現原理
        • 瀏覽器收到相應,響應頭: set-cookie:key=value,瀏覽器會把這個數據保存到本地Cookie中
        • 瀏覽器第二次發送請求,請求頭: cookie:key=value
      • Cookie的相關細節
        • 服務器可以創建多個Cookie,多次調用resp.addCookie(Cookie cookie)把所有Cookie放到響應中
        • 默認Cookie只存在瀏覽器内存中,瀏覽器關閉之後Cookie就消失了;持久化Cookie需要使用cookie.setMaxAge(int secondes)設置Cookie的存活時間,設置為0代表刪除cookie,設置正數代表存活時間,設置負數代表會話cookie,瀏覽器關閉cookie就消失了
        • Tomcat的cookie不支持特殊字符,需要手動把格式轉換成沒有特殊字符的格式
      • Cookie共享
        • 一個Tomcat服務器上的多個項目之間默認是不可以共享Cookie的
        • cookie.setPath(String path)可以設置Cookie的路徑,默認這個路徑是當前項目的虛擬目錄,可以設置成服務器根路徑/,這樣服務器上所有項目可以共享這個Cookie
      • Cookie的特點
        • 數據存在瀏覽器,容易丟失,安全性也比服務器差
        • Cookie對單個Cookie的大小有限制,對同一個域名下的Cookie的總數量也有限制
    • Session
      • 定義:將數據保存在服務器的會話技術
      • Session作用:在一次會話的多次請求之間共享數據
      • 獲取HttpSession對象: req.getSession()
      • HttpSession對象相關方法:getAttribute(String name), setAttribute(String name, Object value), removeAttribute(String name)
      • Session原理
        • 服務器需要確保一次會話的多次請求之間的Session對象是同一個,這樣才能獲得存在Session中的數據
        • Session是通過Cookie實現的:第一次請求的響應頭的set-cookie中有Session的唯一id,之後的請求的請求頭的cookie中有帶著這個session的id,服務器會在自己内存中查找對應的session對象
      • Session的相關細節
        • 客戶端關閉了,服務器沒關閉,獲取的Session是不是同一個
          • 默認不是同一個,因爲客戶端關閉,會話已經關閉了
          • 如果要訪問同一個Session,需要創建Cookie,new Cookie("JSESSIONID", session.getId()) 并且設置Cookie的有效期
        • 服務器關閉了,客戶端沒關閉,獲取的Session是不是同一個
          • 默認不是同一個,因爲服務器内存中的Session對象被銷毀了
          • 服務器關閉,Session被銷毀,但是Session中的數據不應該丟失,需要Session的鈍化和活化技術
            • Session鈍化:在服務器關閉之前,把服務器内存的Session對象序列化到硬盤之上
            • Session活化:在服務器啓動之後,把硬盤上的Session對象反序列化到服務器内存中
          • IDEA配置的Tomcat服務器不能完成Session鈍化和活化,但是原生的Tomcat會自動完成Session的鈍化和火花
        • 銷毀Session
          • 服務器被關閉Session被銷毀
          • session.invalidate()方法可以銷毀自己
          • Session對象的默認失效時間是30分鐘
            • 修改所有項目的Session失效時間:/Tomcat根目錄/conf/web.xml中修改<session-timeout>失效時間的分鐘數</session-out>
            • 修改本項目的Session失效時間:/WEB-INF/web.xml中修改<session-timeout>失效時間的分鐘數</session-out>
        • Session的特點
          • 用於存儲一次會話的多次請求之間的數據,存在服務器的内存中
          • Session可以存儲任意類型,任意大小的數據
  6. JSP
    • JSP(Java server pages):java服務器頁面,既可以定義html標簽,也可以直接寫java代碼
    • JSP的原理
      • 瀏覽器請求jsp文件資源,服務器把jsp變成對應的java文件,然後編譯成class文件
      • 瀏覽器實際訪問的是class字節碼文件
      • JSP實際上是一個Servlet
    • JSP脚本:需要注意的是,脚本是可以被截斷,從而配合HTML標簽一起使用的
      • <% 代碼%>: 定義的Java代碼在Servlet的service方法内部
      • <%! 代碼%>:定義的Java代碼是Servlet的成員變量或者成員方法
      • <%= 代碼%>: 定義的Java代碼會輸出到頁面上
    • JSP内置對象
      • JSP有9個内置對象:
        • 域對象:pageContext(當前頁面共享數據,也可以用來獲取其他8個内置對象),request(一次請求訪問多個資源共享數據,轉發會用到),session(一次會話的多個請求之間共享數據,重定向會用到),application(所有用戶共享數據,服務器上的單例對象)
        • 其他對象:response, out(輸出内容到頁面上), page(相當於java的this,指當前Servlet
        • ), configure(Servlet的配置對象), exception
      • out.write()寫在頁面什麽位置就在什麽位置輸出,resp.getWriter().write()會在頁面最前面輸出,因爲tomcat會先訪問response對象緩衝區,再訪問out對象緩衝區。所以一般不要在jsp中使用resp.getWriter().write(),而是使用out.write()
    • JSP指令
      • 作用: 用來配置JSP頁面,導入資源文件
      • 格式: <%@ 指令名稱 屬性名1=屬性值1 屬性名2=屬性值2 %>
      • 指令分類
        • <%@ page %>: 配置JSP頁面的屬性
          • 頁面總體設置<%@ page contentType"text/html;charset=utf-8" pageEncoding="utf-8" language="java" buffer="16kb" %>
          • 導入需要的Java包,每一個Java包單獨寫一個標簽: <%@ page import="java包名" %>
          • 設置錯誤頁面: <%@ page errorPage="錯誤頁面路徑" isErrorPage="true/false" %>, 默認isErrorPage設置的是false,如果設置成爲true,那麽就可以在頁面使用exception内置對象
        • <%@ taglib %>: 配置JSP頁面包含的内容
          • 格式:<%@ taglib prefix="前綴" uri="URI" %>
          • 導入JSTL標簽庫
            • jakarta.servlet.jsp.jstl-2.0.0.jarjakarta.servlet.jsp.jstl-api-2.0.0.jar放到WEB-INF/lib
            • 加上<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>標簽
          • 一般用來導入標簽庫
        • <%@ include %>: 導入資源文件
          • 格式:<%@ include file="文件路徑名" %>
          • 這個可以用來設置頁面格式模板
    • JSP注釋
      • HTML注釋:只能注釋HTML代碼片段,寫法是<!-- -->
      • JSP注釋:可以注釋所有内容,寫法是<%-- --%>
    • 一個Servlet的方法改寫成jsp的例子
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("text/html;charset=utf-8");
      Cookie[] res = req.getCookies();
      boolean flag = false;
      if (res != null && res.length > 0) {
      for (Cookie cookie : res) {
      if ("lasttime".equals(cookie.getName())) {
      cookie.setMaxAge(60 * 60 * 24 * 30);
      String value = cookie.getValue();
      resp.getWriter().write("欢迎回来,上次登陆时间 " + value);
      String pattern = "MM-dd-yyyy";
      SimpleDateFormat formatter = new SimpleDateFormat(pattern);
      String date = formatter.format(new Date());
      cookie.setValue(date);
      resp.addCookie(cookie);
      flag = true;
      break;
      }
      }
      }
      if (flag == false || res == null || res.length == 0) {
      String pattern = "MM-dd-yyyy";
      SimpleDateFormat formatter = new SimpleDateFormat(pattern);
      String date = formatter.format(new Date());
      Cookie cookie = new Cookie("lasttime", date);
      cookie.setMaxAge(60 * 60 * 24 * 30);
      resp.addCookie(cookie);
      resp.getWriter().write("你好,欢迎登陆!");
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      <%@ page import="java.text.SimpleDateFormat" %>
      <%@ page import="java.util.Date" %>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>Title</title>
      </head>
      <body>
      <%

      Cookie[] res = request.getCookies();
      boolean flag = false;
      if (res != null && res.length > 0) {
      for (Cookie cookie : res) {
      if ("lasttime".equals(cookie.getName())) {
      cookie.setMaxAge(60 * 60 * 24 * 30);
      String value = cookie.getValue();
      %>
      <!-- 這裏截斷了java代碼,加入了HTML標簽,并且用<%=value%>輸出結果到頁面上 -->
      <h1>"欢迎回来,上次登陆时间 " <%=value%></h1>
      <%
      String pattern = "MM-dd-yyyy";
      SimpleDateFormat formatter = new SimpleDateFormat(pattern);
      String date = formatter.format(new Date());
      cookie.setValue(date);
      response.addCookie(cookie);
      flag = true;
      break;
      }
      }
      }
      if (flag == false || res == null || res.length == 0) {
      String pattern = "MM-dd-yyyy";
      SimpleDateFormat formatter = new SimpleDateFormat(pattern);
      String date = formatter.format(new Date());
      Cookie cookie = new Cookie("lasttime", date);
      cookie.setMaxAge(60 * 60 * 24 * 30);
      response.addCookie(cookie);
      %>
      <h1>"你好,欢迎登陆!"</h1>
      <%
      }
      %>
      </body>
      </html>
    • 登錄+輸入驗證碼的例子
      • 實現邏輯
        • 登錄Servlet和驗證碼Servlet是不同的Servlet,要保存驗證碼内容到Session中
        • 登錄成功,重定向到成功頁面,要保存成功信息到Session對象中
        • 登錄失敗,轉發到登陸頁面,要保存失敗的信息數據到request的屬性中
      • LoginServlet代碼實現
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String verificationCode = req.getParameter("verificationCode");
        String codeString = (String)req.getSession().getAttribute("verificationCodeString");
        // 忽略大小寫的比較
        if (!verificationCode.equalsIgnoreCase(codeString)) {
        // 驗證碼錯誤,轉發到登陸頁面
        req.setAttribute("code_error", "驗證碼錯誤");
        req.getRequestDispatcher("/login.jsp").forward(req, resp);
        } else {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        UserDao dao = new UserDao();
        User res = dao.login(user);
        if (res == null) {
        // 用戶名或密碼錯誤,轉發到登陸頁面
        req.setAttribute("login_error","用戶名或密碼錯誤");
        req.getRequestDispatcher("/login.jsp").forward(req, resp);
        } else {
        // 登陸成功,重定向到成功頁面
        req.getSession().setAttribute("user", username);
        resp.sendRedirect(req.getContextPath()+"/success.jsp");
        }
        }
        }
  7. MVC開發模式
    • 歷史發展
      • 最開始全部用response輸出標簽數據,非常麻煩
      • 有了JSP之後,簡化了Servlet的開發,如果過度使用JSP,在JSP寫了大量Java代碼,難以維護,難以分工協作
      • 之後JavaWeb藉鑒了MCV開發模式,使得程序設計更加合理
    • MVC
      • Model,模型,完成具體業務操作,實際上是JavaBean
      • View,視圖,展示數據,實際上是JSP頁面
      • Controller,控制器,接受輸入,調用模型,將數據交給View展示,實際上是Servlet
      • 瀏覽器請求數據,首先Controller接受請求,然後調用Model進行業務操作,得到結果之後Controller把結果交給View進行展示
    • 優缺點
      • 優點:耦合度點,方便維護,利於分工協作;重用性高
      • 缺點:使得項目結構變得複雜,便於分工協作
  8. EL(Expression Language)表達式
    • 背景:JSP值進行數據展示,不應該有大量Java代碼,這些代碼有兩個替代選擇:EL表達式和JSTL標簽
    • 目的:替代和簡化JSP頁面中Java代碼的編寫
    • 語法: ${表達式}
    • JSP默認支持EL表達式,有兩種方式可以忽略EL表達式
      • 忽略頁面中所有EL表達式: <%@ page isELIgnored="true" %>
      • 忽略頁面中單個EL表達式: \${表達式},反斜杠轉義
    • EL表達式的功能
      • 進行運算: / 或者div, %或者mod, &&或者and, ||或者or, !或者not, empty(判斷字符串,集合,數組是否是null或者是空的)
      • 從域對象中獲取值:
        • ${域名稱.key名稱},域名稱是pageScope, requestScope, sessionScope, applicationScope; 或者${key名稱},從範圍小到範圍大依次尋找屬性值(page -> request -> session -> application)
        • 獲取對象的屬性值: ${域名稱.key名稱.value對象的屬性名稱},本質上會調用對應屬性的getter方法,所以對應的Java類裏面可以只有getter方法但是這個名字的屬性並不存在
        • 獲取List的值: ${arr[index]},如果index越界,不會報錯,結果是空字符串
        • 獲取Map的值: ${map.key名稱}或者${map["key名稱"]},如果key不存在,不會報錯,結果是空字符串
    • EL表達式隱式對象
      • EL有11個隱式對象
      • pageContext對象可以獲取JSP的其他8個内置對象,比如pageContext.request拿到了request對象, pageContext.request.contextPath在JSP頁面中動態的獲取虛擬目錄
  9. JSTL(Java Server Pages Library)標簽
    • 目的:替換和簡化JSP頁面中Java代碼的標簽
    • 使用步驟
      • 導入jstl相關jar包: 把jakarta.servlet.jsp.jstl-2.0.0.jarjakarta.servlet.jsp.jstl-api-2.0.0.jar放到WEB-INF/lib
      • 通過<%@ taglib %>導入標簽庫: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    • 常用的JSTL標簽
      • if標簽: 判斷boolean表達式,如果是true則顯示標簽内容到頁面,否則不顯示
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        <%
        request.setAttribute("number", 3);
        %>
        <c:if test="${requestScope.number % 2 !=0}">
        ${requestScope.number}是奇數
        </c:if>
        <!-- 因爲if標簽沒有else,需要手寫if來模擬else的情況 -->
        <c:if test="${requestScope.number % 2 ==0}">
        ${requestScope.number}是偶數
        </c:if>
      • choose標簽: 判斷表達式的值,如果匹配某一個when標簽,則輸出匹配的值,如果沒有匹配,輸出otherwise標簽的值
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        <%
        Random random = new Random();
        int day = random.nextInt(1, 10);
        request.setAttribute("day", day);
        %>
        <c:choose>
        <c:when test="${requestScope.day == 1}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 2}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 3}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 4}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 5}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 6}">星期${requestScope.day}</c:when>
        <c:when test="${requestScope.day == 7}">星期${requestScope.day}</c:when>
        <c:otherwise>數字${requestScope.day}輸入有無</c:otherwise>
        </c:choose>
      • foreach標簽
        • 完成重複操作
          1
          2
          3
          for (int i = 0; i < 10; i+= 2) {
          System.out.println(i);
          }
          1
          2
          3
          <c:forEach begin="0" end="9" var="i">
          ${i}
          </c:forEach>
        • 遍歷容器
          1
          2
          3
          for (Integer i: list) {
          System.out.println(i);
          }
          1
          2
          3
          4
          5
          6
          <c:forEach items="${list}" var="i" varStatus="s">
          ${i} <br>
          ${s.index}<br> <!--容器中元素的索引,從0開始 -->
          ${s.count}<br> <!-- 循環次數,從1開始-->

          </c:forEach>
  10. 三層架構
    • 表示層(界面層/Web層): 用戶可以通過表示層的組件和服務器進行交互
    • 業務層(Service層):處理業務邏輯,組合Dao層的簡單方法,組成業務邏輯
    • 持久層(數據訪問層/Dao層): 操作數據存儲文件,和數據庫交互,實現各種CRUD方法
  11. Filter
    • 作用: 一般用於完成一些通用的操作,例如登陸驗證,統一編碼處理,敏感字符過濾
    • JavaWeb三大組件:Servlet, Filter, Listener
    • 配置過濾器
      • 定義一個類,實現接口Filter: jakarta.servlet.Filter
      • 重寫繼承方法: init(), doFilter(), destroy(),在doFilter()方法内部需要調用filterChain.doFilter(servletRequest, servletResponse);對能夠訪問的目標資源放行,否則是訪問不到目標資源的
      • 配置過濾器攔截路徑:
        • 使用注解配置: @WebFilter(urlPatterns={"攔截路徑"})
        • 使用web.xml配置: 和Servlet配置非常相似
          1
          2
          3
          4
          5
          6
          7
          8
          <filter>
          <filter-name>自定義Filter名稱</filter-name>
          <filter-class>實現Filter的類的全類名</filter-class>
          </filter>
          <filter-mapping>
          <filter-name>filter標簽中的Filter名稱</filter-name>
          <url-pattern>想要Filter生效的URL攔截路徑</url-pattern>
          </filter-mapping>
    • Filter執行流程:
      • 請求被Filter攔截,一般在這裏對request進行增强
      • Filter使用filterChain.doFilter(req, resp)會傳遞給下一個Filter,如果沒有下一個Filter就執行目標資源
      • 經過doFilter()方法之後,resp對象已經是有内容的了,一般在這裏對response進行增强
    • Filter生命周期
      • 服務器啓動之後會創建Filter對象,然後調用init方法,這個方法只執行一次
      • 每次請求被攔截的時候,doFilter方法被調用,這個方法會執行多次
      • 服務器關閉之後,Filter被銷毀,如果服務器是正常關閉,在Filter銷毀之前會調用destroy方法,這個方法只執行一次
    • Filter配置攔截路徑
      • 具體資源路徑: 比如/index.jsp,只有訪問這個具體資源的時候,Filter才會執行
      • 設置攔截目錄: 比如/user/*,訪問攔截目錄下的所有資源,Filter都會執行
      • 後綴名攔截: 比如*.jsp(注意這裏沒有/),訪問所有帶這個後綴名的資源,Filter都會執行
      • 攔截所有資源: /*,訪問所有資源的時候,Filter都會執行
    • Filter配置攔截方式
      • 使用注解配置: 在@WebFilter中指定dispatcherTypes屬性,有DispatcherType.REQUEST(默認值,攔截請求), DispatcherType.FORWARD(攔截轉發), DispatcherType.INCLUDE(包含訪問資源,include控制權在第一個Servlet,forward控制權在第二個Servlet), DispatcherType.ERROR(錯誤跳轉), DispatcherType.ASYNC(異步訪問)。因爲dispatcherTypes屬性是一個數組,所以可以指定多個攔截方式
      • 使用web.xml配置:
        1
        2
        3
        4
        5
        6
        <filter-mapping>
        <filter-name>filter標簽中的Filter名稱</filter-name>
        <url-pattern>想要Filter生效的URL攔截路徑</url-pattern>
        <!-- 加入下面這一行 -->
        <dispatcher>攔截方式(比如REQUEST)</dispatcher>
        </filter-mapping>
    • FilterChain
      • 執行順序
        • 注解配置的Filter: 比較各個Filter的類名的字符串大小,小的先執行,比如Filter17Filter6先執行
        • web.xml配置的Filter: 按照文件中<filter-mapping>標簽的先後順序執行
    • 案例:登陸驗證
      • 登陸驗證是一種權限控制
      • 需求:登陸了就訪問,沒登陸跳轉登錄頁面
      • 邏輯:
        • 登錄相關的資源直接放行
        • 其他資源判斷是否登錄決定是否放行: 檢查Session中的登陸狀態即可
  12. Listener
    • JavaWeb三大組件之一
    • 事件監聽機制
      • 需要有事件,事件源,監聽器,注冊監聽(把事件,事件源和監聽器綁定在一起,黨事件源發生事件之後,執行監聽器代碼)
    • jakarta.servlet.ServletContextListener
      • 這個是一個接口,需要手動實現相關的類
      • 監聽ServletContext對象的創建和銷毀,服務器啓動,ServletContext自動創建,服務器關閉,ServletContext自動銷毀
      • default void contextInitialized(ServletContextEvent sce): 在ServletContext對象創建之後調用
      • default void contextDestroyed(ServletContextEvent sce): 在ServletContext對象被銷毀之後調用
    • 實現監聽器
      • 寫一個類實現ServletContextListener接口
      • 重寫contextInitialized(ServletContextEvent sce)方法和contextDestroyed(ServletContextEvent sce)方法
      • 配置監聽器
        • 使用web.xml配置
          1
          2
          3
          <listener>
          <listener-class>監聽器實現類的全類名</listener-class>
          </listener>
        • 使用注解配置: 加入@WebListener注解即可
    • 使用監聽器加載資源文件
      • web.xml中加入
        1
        2
        3
        4
        5
        6
        7
        8
        9
        <!-- 配置監聽器 -->
        <listener>
        <listener-class>com.example.demo.Listener</listener-class>
        </listener>
        <!-- 配置ServletContext的參數,用來在初始化ServletContext的時候解耦 -->
        <context-param>
        <param-name>Servlet參數名稱</param-name>
        <param-value>資源文件的路徑</param-value>
        </context-param>
      • 實現監聽器
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        @Override
        public void contextInitialized(ServletContextEvent sce) {
        // 加載資源文件
        //1. 獲取ServletContext對象
        ServletContext context = sce.getServletContext();
        //2. 加載資源文件
        String configurationLocation = context.getInitParameter("Servlet參數名稱");
        //3. 獲取真實路徑
        String path = context.getRealPath(configurationLocation);
        //4. 加載資源文件到内存中
        try {
        FileInputStream fileStream = new FileInputStream(path);
        } catch (FileNotFoundException e) {
        e.printStackTrace();
        }
        }
  13. JQuery
    • 定義:Jquery是一個快速,簡潔的JavaScript框架,簡化了JavaScript開發
    • JavaScript框架本質上就一個JavaScript文件,裏面封裝了JavaScript的原生代碼而已
    • 在html中使用JQuery
      • 下載JQuery文件: jquery-xxx.js有可閲讀性,jquery-xxx.min.js是压缩過的文件,文件體積小,加載速度快,生產環境用這個
      • 通過script導入jQuery文件
      • 一個簡單例子
        1
        2
        3
        4
        5
        6
        // 使用原生JavaScript
        let div1 = document.getElementById("div1");
        alert(div1.innerHTML);
        // 使用JQuery
        let div2 = $("#div2);
        alert(div2.html());
    • JQuery對象和JavaScript對象
      • jq對象在操作的時候更方便
      • jq對象和js對象的方法不通用,需要進行互相轉換
      • 把jq對象轉換成js對象:$(jq對象).get(索引)或者$(jq對象)[索引]
      • 把js對象轉換成jq對象:$(js對象)
    • jq的一些基本語法
      • 事件綁定:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        // 原生js的寫法
        let div1 = document.getElementById("div1")
        div1.onclick = () => {
        alert(div1.innerHTML);
        }
        // jq的寫法
        let div2 = $("#div2");
        div2.click(() => {
        alert(div2.html());
        })
      • 入口函數: 頁面加載完成之後執行,如果有多個window.onload只執行最後一個,如果有多個$()則按照次序執行
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        // 原生js的寫法
        window.onload = () => {
        let d1 = document.getElementById("div1")
        alert(d1.innerHTML);
        }
        //jq的寫法
        $(() => {
        let d2 = $("#div2");
        alert(d2.html());

        });
      • 樣式控制
        1
        2
        3
        4
        5
        6
        // 原生js的寫法
        let div1 = document.getElementById("div1");
        div1.style.backgroundColor = "red";
        // jq的寫法
        let div2 = $("#div2");
        div2.css("backgroundColor", "red");
    • jq的選擇器
      • 基本選擇器: $("html標簽"), $("#id屬性值"). $(".class屬性值")
      • 層級選擇器: $("A B")(後代選擇器: 選擇A元素内部的所有B元素), $(A > B)(選擇A元素内部的所有B子元素)
      • 屬性選擇器: $("對象名[屬性名]")(包含指定屬性的選擇器), $("對象名[屬性名='屬性值']")(包含指定屬性等於指定屬性值的選擇器), $("對象名[屬性名1='屬性值1'][屬性名2='屬性值2']")(包含多個屬性條件的選擇器
      • 過略選擇器: :fist, :last, :not(篩選條件), :even, :odd, :eq(索引), :gt(索引), :lt(索引), :header(篩選h1-h6)
      • 表達過濾選擇器: :enabled, :disabled, :checked, :selected
    • DOM操作
      • 内容操作: html()(獲取/設置元素的標簽躰内容), text()(獲取/設置元素的標簽躰的純文本内容), val()(獲取/設置元素的value屬性值)
      • 通用屬性操作:
        • attr()(獲取/設置元素的屬性), removeAttr()(刪除屬性), prop()(獲取/設置元素的屬性), removeProp()(刪除屬性)
        • 如果操作元素的固有屬性,用prop,如果操作元素的茲定於屬性,使用attr
        • 注意checked, selected, disabled等屬性只能使用prop,用attr結果是undefined
      • 對class屬性的操作: addClass()(添加class屬性), removeClass()(刪除class屬性), toggleClass()(切換class屬性, 如果存在class屬性就刪除,如果不存在class屬性就刪除)
      • CRUD相關操作
        • A.append(B): 元素A將元素B添加到元素A的最後一個子元素
        • A.prepend(B): 元素A將元素B添加到元素A的第一個子元素
        • A.appendTo(B):元素B將元素A添加到元素B的最後一個子元素
        • A.prependTo(B): 元素B將元素A添加到元素B的第一個子元素
        • A.after(B): 將元素B添加到元素A的後面
        • A.before(B): 將元素B添加到元素A的前面
        • A.insertAfter(B): 將元素A添加到元素B的後面
        • A.insertBefore(B): 將元素A添加到元素B的前面
        • A.remove(): 刪除元素A
        • A.empty(): 刪除元素A的所有子元素
    • jq的動畫效果: show(), hide(), toggle(), slideDown(), slideUp(), slideToggle(), fadeIn(), fadeOut(), fadeToggle()
    • 遍歷方式
      1
      2
      3
      4
      5
      6
      <ul id="city">
      <li id="1">東京</li>
      <li id="2">南京</li>
      <li id="3">西京</li>
      <li id="4">北京</li>
      </ul>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      let items = document.getElementById("city").getElementsByTagName("li");
      // 原生js的for循環遍歷
      for (let i = 0; i < items.length; i++) {
      alert(items[i].innerHTML);
      if (items[i].id == "3") {
      break;
      }
      }
      // 原生js的for-of循環遍歷
      for(let item of items) {
      alert(item.innerHTML);
      if (item.id == "3") {
      break;
      }
      }
      // jq的each遍歷方式
      let $items = $("#city li");
      $items.each((index, item) => {
      alert($(item).html());
      // alert(item.innerHTML);
      if (item.id == "3") {
      return false;
      }
      });

      //jq的$.each遍歷方式
      $.each(cities, (index, item) => {
      alert($(item).html());
      if (item.id == "3") {
      return false;
      }
      })

      // jq的for-of遍歷方式
      for (let city of cities) {
      alert($(city).html())
      if (city.id === "3") {
      break;
      }
      }
    • 綁定事件方法
      • 標準綁定方法: jq對象.事件名稱(回調函數)
      • on綁定事件/off解綁事件: jq對象.on(事件名稱, 回調函數), jq對象.off(事件名稱, 回調函數)
  14. AJAX
    • 異步:客戶端不需要等待服務器響應,在服務器處理請求的過程中,客戶端可以進行其他的操作
    • Ajax是一種無需重新加載整個網頁的基礎上,能個更新部分網頁的技術
    • 實現方式
      • 使用原生的js代碼, 這個過於複雜,基本沒人用
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script type="text/javascript">
        // 發送異步請求
        const func = () => {
        // 1.創建核心對象
        let xhttp;
        if (window.XMLHttpRequest) {
        xhttp = new XMLHttpRequest();
        } else {
        // code for IE6, IE5
        xhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        // 2. 建立連接
        // xhttp.open(methodTypeString, urlString, booleanAjax)
        // 發送一個異步請求到ajaxServlet?username=tom
        xhttp.open("GET", "ajaxServlet?username=tom", true);
        // 3. 發送請求
        // get請求的參數在open的url中,這裏不用傳參
        // post請求在這裏寫 xhttp.send("key=value")
        xhttp.send();
        // 4. 處理服務器響應結果
        // 黨請求狀態變化的時候觸發事件
        xhttp.onreadystatechange = function () {
        // 如果響應完畢,并且是響應成功的話
        if (this.readyState == 4 && this.status == 200) {
        document.getElementById("resp").innerHTML = xhttp.responseText;
        }
        };
        }
        </script>
        </head>
        <body>
        <input type="button" value="發送異步請求" onclick="func();">
        <input>
        <!-- 展示響應的結果 -->
        <div id="resp"></div>
        </body>
        </html>
      • 使用jq實現: $.ajax(), $.get(), $.post()
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        // 使用$.ajax()發送請求
        $.ajax({
        url: "ajaxServlet", // 請求路徑
        type: "GET", // 請求方式
        // 請求參數有兩種寫法
        // 第一種請求參數用字符串表示 data: "k1=v1&kn=vn"
        // 第二種請求參數用JSON表示
        data: {
        "username": "tom"
        },
        success: (resp) => {
        $("#resp").html(resp);
        }, // 請求成功的回調函數
        error: () => {
        alert("發生錯誤了");
        }
        })
      • 使用jq的$.get()發送請求: get()post()流程完全一致
        1
        2
        3
        4
        5
        6
        7
        8
        9
        $.get("ajaxServlet", {       // 本體裏面只寫請求url和可選的請求參數
        "username": "tom"
        }).done((resp) => { // done在jq3.0之後取代了success, 裏面寫請求成功的回調函數
        $("#resp").html(resp);
        }).fail(() => { // fail在jq3.0之後取代了error, 裏面寫請求失敗的回調函數
        alert("請求失敗了")
        }).always(() => { // always在jq3.0之後取代了complete, 裏面寫請求完畢的回調函數
        console.log("請求完畢")
        });
  15. JSON
    • JSON現在多用於存儲和交換信息的語法,類似XML,但是更小,更快,更容易解析
    • JSON的key可以不寫引號,可以用單引號,可以用雙引號
    • JSON基本語法
      • 獲取JSON的值: json.keyName, 或者json["keyName"]
      • 使用for-in循環遍歷JSON,注意這個循環中key是字符串,需要用json[key]來取值
    • JSON數據和Java對象的轉換
      • 服務器内部是Java對象,在服務器和客戶端之間傳輸是JSON字符串,客戶端是JSON Object(切記客戶端需要resp = JSON.parse(resp)把JSON字符串轉換成JSON Object)
      • JSON解析器: Jsonlib, Gson, fastjson, jackson
      • 使用Jackson把JSON轉換成Java對象
        • 步驟
          1. 導入jar包:jackson-annotations.jar, jackson-core.jar, jackson-databind.jar
          2. 創建Jackson核心對象ObjectMapper
          3. 調用ObjectMapper的方法把JSON字符串轉換成Java對象: om.readValue(json, Class<T>)把JSON字符串轉換成Java的T類型對象
      • 使用Jackson把Java對象轉換成JSON
        • 步驟
          1. 導入jar包:jackson-annotations.jar, jackson-core.jar, jackson-databind.jar
          2. 創建Jackson核心對象ObjectMapper
          3. 調用ObjectMapper的方法把Java對象轉換成JSON: om.writeValue(File/Writer/OutputStream, Java對象)把Java對象轉換成JSON字符串,然後寫到文件/字符流/字節流中,om.writeValueAsString(Java對象)把Java對象轉換成JSON字符串
        • 使用注解定制化轉換
          • @JsonIgnore: 忽略Java對象的某個屬性不進入轉換完的JSON字符串
          • @JsonFormat(pattern="定制化格式"):自定義Java對象的某個屬性轉換成JSON字符串的key的格式
  16. Redis
    • 概念: Redis是一款高性能的NoSQL系列的非關係型數據庫,它是一個key-value的數據庫,存儲在内存中,查詢速度快
    • Redis在windows下使用
      • 按照官网的指示,在WSL中安装使用
      • 纯粹的Windows版本: https://github.com/tporadowski/redis/releases下载即可使用
    • Redis的数据结构
      • Redis存储的是k-v格式的数据,其中key是字符串,value有5种不同的数据结构
        • Value数据類型:字符串string, 哈希類型hash(map), 列表類型list(linked list), 集合類型set, 有序集合類型sortedset
      • 字符串類型:set key val, get key, del key(刪除完之後nil)
      • 哈希類型:hset key field val, hget key field(獲取指定的值), hgetall key(獲取所有的key和value), hdel key field
      • 列表類型: lpush key val, rpush key val, lrange key start end(獲取指定範圍的所有元素,lrange key 0 -1獲取列表中所有的值), lpop key, rpop key
      • 集合類型: sadd key val, smembers key(獲取set中所有元素), srem key val
      • 有序集合: zadd key score(按照分數從小到大排序), zrange key start end(獲取有序集合中範圍内的元素), zrange key start end with scores(顯示有序集合中指定範圍的元素和對應的score), zrem key val
    • 其他常用語法: keys *: 查詢所有key, type key: 顯示對應value的類型, del key
    • 持久化機制
      • redis是一個内存數據庫,當redis服務器重啓,數據會丟失,可以將redis内存中的數據持久化保存到硬盤的文件中
      • 持久化機制
        • RDB: 默認持久化機制,不需要進行配置。在一定的時間間隔中,檢查key的變化,然後持久化數據
          • redis.windows.conf中有設置持久化要求,可以修改來自定義配置
            1
            2
            3
            4
            5
            6
            #   after 900 sec (15 min) if at least 1 key changed
            save 900 1
            # after 300 sec (5 min) if at least 10 keys changed
            save 300 10
            # after 60 sec if at least 10000 keys changed
            save 60 10000
          • 默認持久化到dump.rdb文件中,服務器啓動會讀取這個文件裏的數據
        • AOF: 使用日志記錄的方式,記錄每一條命令的操作。可以每一次操作後都持久化到硬盤
          • redis.windows.conf中把appendonly no(關閉AOF)改成appendonly yes(開啓AOF)
          • redis.windows.conf中有下面的配置
            1
            2
            3
            # appendfsync always       每個操作都持久化,默認關閉
            appendfsync everysec # 每秒持久化一次,默認開啓
            # appendfsync no 不持久化,默認關閉
    • Jedis
      • 定義: Jedis是一款Java操作redis數據庫的工具
      • 使用Jedis:導入jedis.jar, gson.jar(jedis依賴這個), commons-pool2.jar(jedis官方推薦使用連接池)
        1
        2
        3
        4
        5
        6
        7
        8
        // 不使用連接池,直接使用jedis
        Jedis jedis = new Jedis("url", port);
        jedis.set("key", "value");
        jeidis.close();
        //使用連接池
        JedisPooled jedis = new JedisPooled("url", port);
        jedis.set("key", "value");
        jedis.close();
      • Jedis操作redis的方法和redis原生方法的名字完全一致,還有一些其他方法: jedis.setex(key, sec, val)在經過sec秒之後,key將被刪除
    • 用Redis對數據庫進行緩存優化
      • 先查找redis,如果有數據則直接讀取内存中數據
      • 如果沒有數據,則查找數據庫數據,并且存儲到緩存中
      • 注意如果發生了對數據庫的增刪改操作,需要清空redis緩存
  17. Maven
    • Maven是一個項目管理工具,包含了一個項目對象模型(POM: Project Object Model,把一個項目看作一個POM), 一組標準集合,一個項目生命周期,一個依賴管理系統,和用來運行定義在生命周期階段中插件目標的邏輯
    • Maven解決的問題
      • 沒有Maven,需要手動導入jar包,Maven可以管理jar包
      • Maven可以管理項目的生命周期,例如:编译、打包、部署
    • Maven環境配置
      • 下載Maven,把maven路徑/bin配置到環境變量裏面從而可以直接使用mvn命令
      • 在命令行中輸入mvn -v,如果顯示maven版本信息,則表示Maven環境配置成功
      • 倉庫分類:本地倉庫,遠程倉庫,中央倉庫
      • 修改Maven的默認本地倉庫: 修改maven目錄/conf/settings.xml文件
        1
        2
        3
        <settings>
        <localRepository>路徑</localRepository>
        </settings>
      • 中國大陸訪問中央倉庫較慢,可以設置阿里雲的鏡像: 把下面内容添加到maven目錄/conf/settings.xml文件的<mirrors></mirrors>標簽中
        1
        2
        3
        4
        5
        6
        <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <name>阿里云公共仓库</name>
        <url>https://maven.aliyun.com/repository/public</url>
        </mirror>
      • 在Idea新建Maven項目的時候選擇正確的倉庫路徑和settings.xml文件路徑
    • Maven常用命令
      • Maven項目結構
        1. 項目根目錄
        2. src目錄
          1. main目錄
            1. java目錄
            2. resources目錄
            3. webapp目錄: web項目才有,java項目沒有(存放jsp/html/WEN-INFO目錄)
          2. test目錄
            1. java目錄
            2. resources目錄
        3. target目錄:編譯完的class文件
        4. pom.xml文件
      • Maven命令需要在項目根目錄下面執行
      • mvn compile:編譯項目目錄/src/main/java目錄下的所有java文件,得到的class文件存放在項目目錄/target/classes目錄下
      • mvn clean:刪除項目目錄/target目錄
      • mvn package:編譯java文件,并且把web項目打成war包,war包存放在項目目錄/target目錄下
      • mvn install:把當前項目編譯打包,并且加入到本地maven倉庫中
    • Maven項目的生命周期
      • Maven對項目構建分成三套相互獨立的生命周期
        • Clean Lifecycle:在進行真正的構建項目之前進行清理工作
        • Default Lifecycle:構建項目的核心部分,編譯,測試,打包,部署等
        • Site Lifecycle:生成項目報告,站點,發佈站點
      • 在同一個生命周期之中,執行後面的操作會自動執行前面的操作
    • Maven坐標
      • 作用:坐標用來唯一識別被maven管理的資源
      • 坐標構成: groupId, artifactId, version, packaging(jar適用於java項目/war適用於web項目/pom), scope
      • 導入jar包的依賴: 在pom.xml的<dependencies></dependencies>坐標内加入<dependency></dependency>標簽内部加入jar包的坐標
      • 導入編譯插件: 在pom.xml的<build><plugins></plugins></build>坐標内加入<build></build>標簽内部加入jar包的坐標
    • 依賴範圍
      • 依賴範圍的取值:
        • compile: 對於編譯環境(java目錄下面可以用),測試環境(test目錄下面可以用),運行環境(打包完的壓縮包裏面有)都有效
        • test:對於測試環境有效
        • provided:對於編譯環境,測試環境有效
        • runtime:對於測試環境和運行環境有效
        • system:對於編譯環境和測試員警有效