紹語(yǔ)
本號(hào)主要是Java常用關(guān)鍵技術(shù)點(diǎn),通用工具類(lèi)的分享;以及springboot+springcloud+Mybatisplus+druid+mysql+redis+swagger+maven+docker等集成框架的技術(shù)分享;datax、kafka、flink等大數(shù)據(jù)處理框架的技術(shù)分享。文章會(huì)不斷更新,歡迎碼友關(guān)注點(diǎn)贊收藏轉(zhuǎn)發(fā)!
望各位碼友點(diǎn)擊關(guān)注,沖1000粉。后面會(huì)錄制一些視頻教程,圖文和視頻結(jié)合,比如:圖書(shū)介紹網(wǎng)站系統(tǒng)、搶購(gòu)系統(tǒng)、大數(shù)據(jù)中臺(tái)系統(tǒng)等。技術(shù)才是程序猿的最?lèi)?ài),碼友們沖啊
如果碼友覺(jué)得代碼太長(zhǎng),可以從頭到尾快速掃射一遍,了解大概即可。覺(jué)得有用后再轉(zhuǎn)發(fā)收藏,以備不時(shí)之需。
正文:
Apache的StringEscapeUtils轉(zhuǎn)義工具類(lèi),主要是針對(duì)html、json、xml、js等的轉(zhuǎn)義。具體使用場(chǎng)景在項(xiàng)目中并不是很多,所以可能了解的人也比較少。
import org.apache.commons.text.StringEscapeUtils;
import org.junit.Test;
public class StringEscapeUtilsTest {
@Test
public void test() {
// 轉(zhuǎn)義html腳本和反轉(zhuǎn)義html腳本
String inputText = "<input type=\"button\" value=\"點(diǎn)我\"/>";
String s1 = StringEscapeUtils.escapeHtml4(inputText);
System.out.println(s1);
String s2 = StringEscapeUtils.unescapeHtml4(s1);
System.out.println(s2);
// 轉(zhuǎn)義js腳本和反轉(zhuǎn)義js腳本
String s3 = StringEscapeUtils.escapeEcmaScript("<script>alert('點(diǎn)我')<script>");
System.out.println(s3);
String s4 = StringEscapeUtils.unescapeEcmaScript(s3);
System.out.println(s4);
// 把字符串轉(zhuǎn)義為unicode編碼和從把unicode編碼轉(zhuǎn)義為字符串
String s5 = StringEscapeUtils.escapeJava("abc不要點(diǎn)我了");
System.out.println(s5);
String s6 = StringEscapeUtils.unescapeJava(s5);
System.out.println(s6);
// 轉(zhuǎn)義XML和反轉(zhuǎn)義XML
String s7 = StringEscapeUtils.escapeXml11("<name>張三</name>");
System.out.println(s7);
String s8 = StringEscapeUtils.unescapeXml(s7);
System.out.println(s8);
}
}
其他方法:
工具類(lèi)源碼:
直接引入工具類(lèi)的jar包即可
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.1</version>
</dependency>
和本文使用的相同功能的工具類(lèi):
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.9</version>
<type>pom</type>
</dependency>
鄙人編碼十年多,在項(xiàng)目中也積累了一些工具類(lèi),很多工具類(lèi)在每個(gè)項(xiàng)目都有在用,很實(shí)用。大部分是鄙人封裝的,有些工具類(lèi)是同事封裝的,有些工具類(lèi)已經(jīng)不記得是ctrl+c的還是自己封裝的了,現(xiàn)在有空就會(huì)總結(jié)項(xiàng)目中大部分的工具類(lèi),分享給各位碼友。如果文章中涉及的代碼有侵權(quán)行為請(qǐng)通知鄙人處理。
計(jì)劃是先把工具類(lèi)整理出來(lái),正所謂工欲善其事,必先利其器。項(xiàng)目中不管是普通單體項(xiàng)目還是多模塊maven項(xiàng)目或是分布式微服務(wù),一部分功能模塊都是可以重用的,工具類(lèi)模塊就是其中之一。
在項(xiàng)目開(kāi)發(fā)過(guò)程中,有很多業(yè)務(wù)模塊的代碼是具有一定規(guī)律性的,例如controller控制器、service接口、service實(shí)現(xiàn)類(lèi)、mapper接口、model實(shí)體類(lèi)等等,這部分代碼可以使用代碼生成器生成,我們就可以將更多的時(shí)間放在業(yè)務(wù)邏輯上。
傳統(tǒng)的開(kāi)發(fā)步驟:
創(chuàng)建數(shù)據(jù)庫(kù)和表 根據(jù)表設(shè)計(jì)實(shí)體類(lèi) ? 編寫(xiě)mapper接口 ? 編寫(xiě)service接口和實(shí)現(xiàn)類(lèi) ? 編寫(xiě)controller控制器 ? 編寫(xiě)前端頁(yè)面 ? 前后端聯(lián)調(diào)
基于代碼生成器開(kāi)發(fā)步驟:
創(chuàng)建數(shù)據(jù)庫(kù)和表 ? 使用代碼生成器生成實(shí)體類(lèi)、mapper、service、controller、前端頁(yè)面 ? 將生成好的代碼拷貝到項(xiàng)目中并做調(diào)整 ? 前后端聯(lián)調(diào)
我們只需要知道數(shù)據(jù)庫(kù)和表相關(guān)信息,就可以結(jié)合模版生成各個(gè)模塊的代碼,減少了很多重復(fù)工作,也減少出錯(cuò)概率,提高效率。
(1)需要對(duì)數(shù)據(jù)庫(kù)表解析獲取到元數(shù)據(jù),包含表字段名稱(chēng)、字段類(lèi)型等等
(2)將通用的代碼編寫(xiě)成模版文件,部分?jǐn)?shù)據(jù)需使用占位符替換
(3)將元數(shù)據(jù)和模版文件結(jié)合,使用一些模版引擎工具(例如freemarker)即可生成源代碼文件
FreeMarker 是一款 模板引擎: 即一種基于模板和要改變的數(shù)據(jù), 并用來(lái)生成輸出文本(HTML網(wǎng)頁(yè),電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶(hù)的,而是一個(gè)Java類(lèi)庫(kù),是一款程序員可以嵌入他們所開(kāi)發(fā)產(chǎn)品的組件。
模板編寫(xiě)為FreeMarker Template Language (FTL)。它是簡(jiǎn)單的,專(zhuān)用的語(yǔ)言, 在模板中,你可以專(zhuān)注于如何展現(xiàn)數(shù)據(jù), 而在模板之外可以專(zhuān)注于要展示什么數(shù)據(jù)。
(1)動(dòng)態(tài)頁(yè)面
freemarker可以作為springmvc一種視圖格式,像jsp一樣被瀏覽器訪問(wèn)。
(2)頁(yè)面靜態(tài)化
對(duì)于一些內(nèi)容比較多,更新頻率很小,訪問(wèn)又很頻繁的頁(yè)面,可以使用freemarker靜態(tài)化,減少DB的壓力,提高頁(yè)面打開(kāi)速度。
(3)代碼生成器
根據(jù)配置生成頁(yè)面和代碼,減少重復(fù)工作,提高開(kāi)發(fā)效率。
(1)創(chuàng)建freemarker-demo模塊,并導(dǎo)入相關(guān)依賴(lài)
<?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)類(lèi)
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模型類(lèi)
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;//錢(qián)包
}
(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í)體類(lèi)相關(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>首頁(yè)</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è)試
瀏覽器訪問(wèn) http://localhost:8881/student/index
效果如下
(1)注釋?zhuān)?lt;#-- -->,介于其之間的內(nèi)容會(huì)被freemarker忽略
<#--我是一個(gè)freemarker注釋-->
(2)插值(Interpolation):即 ${..} 部分,freemarker會(huì)用真實(shí)的值代替${..}
Hello ${name}
(3)FTL指令:和HTML標(biāo)記類(lèi)似,名字前加#予以區(qū)分,F(xiàn)reemarker會(huì)解析標(biāo)簽中的表達(dá)式或邏輯。
<# >FTL指令</#>
(4)文本,僅文本信息,這些不是freemarker的注釋、插值、FTL指令的內(nèi)容會(huì)被freemarker忽略解析,直接輸出內(nèi)容。
<#--freemarker中的普通文本-->
我是一個(gè)普通的文本
if 指令即判斷指令,是常用的FTL指令,freemarker在解析時(shí)遇到if會(huì)進(jìn)行判斷,條件為真則輸出if中間的內(nèi)容,否則跳過(guò)內(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è)試
瀏覽器訪問(wèn)http://localhost:8881/student/index
效果如下
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>列表頁(yè)面</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>錢(qián)包</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è)試
瀏覽器訪問(wèn)http://localhost:8881/student/list
效果如下
include指令的作用類(lèi)似于JSP的包含指令,用于包含指定頁(yè),include指令的語(yǔ)法格式如下
<#include filename [options]></#include>
(1)filename:該參數(shù)指定被包含的模板文件 (2)options:該參數(shù)可以省略,指定包含時(shí)的選項(xiàng),包含encoding和parse兩個(gè)選項(xiàng),encoding 指定包含頁(yè)面時(shí)所使用的解碼集,而parse指定被包含是否作為FTL文件來(lái)解析。如果省略了parse選項(xiàng)值,則該選項(xiàng)值默認(rèn)是true
需求:"早上好,尊敬的 某某 用戶(hù)!" 這句話在很多頁(yè)面都有用到,請(qǐng)合理設(shè)計(jì)!
(1)在resources/templates目錄下創(chuàng)建00-head.ftl模版,內(nèi)容如下
早上好,尊敬的 ${name} 用戶(hù)!
(2)在resources/templates目錄下創(chuàng)建03-include.ftl模版,使用include引入00-head.ftl模版,內(nèi)容如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>詳情頁(yè)</title>
</head>
<body>
<#include "00-head.ftl" />
<br>
歡迎來(lái)到黑馬程序員。
</body>
</html>
(3)在StudentController中增加方法
@GetMapping("include")
public String include(Model model) throws ParseException {
model.addAttribute("name", "黑馬");
return "03-include";
}
(4)測(cè)試
瀏覽器訪問(wèn)http://localhost:8881/student/include
效果如下
它用于為該模板頁(yè)面創(chuàng)建或替換一個(gè)頂層變量
<#assign name = "zhangsan" />
(1)算數(shù)運(yùn)算符
FreeMarker表達(dá)式中完全支持算術(shù)運(yùn)算,FreeMarker支持的算術(shù)運(yùn)算符包括:
(2)比較運(yùn)算符
比較運(yùn)算符注意
(3)邏輯運(yùn)算符
邏輯運(yùn)算符只能作用于布爾值,否則將產(chǎn)生錯(cuò)誤 。
(1)缺失變量默認(rèn)值使用 “!”
(2)判斷某變量是否存在使用 “??”
用法為:variable??,如果該變量存在,返回true,否則返回false
例:為防止stus為空?qǐng)?bào)錯(cuò)可以加上判斷如下:
<#if stus??>
<#list stus as stu>
......
</#list>
</#if>
內(nèi)建函數(shù)語(yǔ)法格式: 變量+?+函數(shù)名稱(chēng)
(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 />
開(kāi)戶(hù)行:${data.bank} 賬號(hào):${data.account}
(5)常見(jiàn)內(nèi)建函數(shù)匯總
?html:html字符轉(zhuǎn)義
?cap_first: 字符串的第一個(gè)字母變?yōu)榇髮?xiě)形式
?lower_case :字符串的小寫(xiě)形式
?upper_case :字符串的大寫(xiě)形式
?trim:去掉字符串首尾的空格
?substring(from,to):截字符串 from是第一個(gè)字符的開(kāi)始索引,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模版頁(yè)面:
<!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>
沒(méi)有C函數(shù)顯示的數(shù)值:${point} <br>
有C函數(shù)顯示的數(shù)值:${point?c}
<hr>
<b>聲明變量assign</b><br>
<#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
<#assign data=text?eval />
開(kāi)戶(hù)行:${data.bank} 賬號(hào):${data.account}
<hr>
</body>
</html>
(3)測(cè)試
瀏覽器訪問(wèn)http://localhost:8881/student/innerFunc
效果如下
內(nèi)建函數(shù)測(cè)試demo2
需求:遍歷學(xué)生集合,顯示集合總條數(shù),id不要逗號(hào)隔開(kāi),顯示學(xué)生的生日(只顯示年月日),錢(qián)包顯示整數(shù)并顯示單位元,用戶(hù)姓名做脫敏處理(如果是兩個(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>列表頁(yè)面</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>錢(qián)包</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è)試
瀏覽器訪問(wèn)http://localhost:8881/student/list
效果如下
(1)springboot整合freemarker靜態(tài)化文件用法
編寫(xiě)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配置類(lèi)
@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)建配置類(lèi)
Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);
//設(shè)置模版加載路徑
//ClassTemplateLoader方式:需要將模版放在FreemarkerTest類(lèi)所在的包,加載模版時(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;
}
}
元數(shù)據(jù)(Metadata)是描述數(shù)據(jù)的數(shù)據(jù)。
數(shù)據(jù)庫(kù)元數(shù)據(jù)(DatabaseMetaData)就是指定義數(shù)據(jù)庫(kù)各類(lèi)對(duì)象結(jié)構(gòu)的數(shù)據(jù)。
在mysql中可以通過(guò)show關(guān)鍵字獲取相關(guān)的元數(shù)據(jù)
show status; 獲取數(shù)據(jù)庫(kù)的狀態(tài)
show databases; 列出所有數(shù)據(jù)庫(kù)
show tables; 列出所有表
show create database [數(shù)據(jù)庫(kù)名]; 獲取數(shù)據(jù)庫(kù)的定義
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ù)庫(kù)中的參數(shù)定義值
也可以從 information_schema庫(kù)中獲取元數(shù)據(jù),information_schema數(shù)據(jù)庫(kù)是MySQL自帶的信息數(shù)據(jù)庫(kù),它提供了訪問(wèn)數(shù)據(jù)庫(kù)元數(shù)據(jù)的方式。存著其他數(shù)據(jù)庫(kù)的信息。
select schema_name from information_schema.schemata; 列出所有的庫(kù)
select table_name FROM information_schema.tables; 列出所有的表
在代碼中可以由JDBC的Connection對(duì)象通過(guò)getMetaData方法獲取而來(lái),主要封裝了是對(duì)數(shù)據(jù)庫(kù)本身的一些整體綜合信息,例如數(shù)據(jù)庫(kù)的產(chǎn)品名稱(chēng),數(shù)據(jù)庫(kù)的版本號(hào),數(shù)據(jù)庫(kù)的URL,是否支持事務(wù)等等。
DatabaseMetaData的常用方法:
getDatabaseProductName:獲取數(shù)據(jù)庫(kù)的產(chǎn)品名稱(chēng)
getDatabaseProductName:獲取數(shù)據(jù)庫(kù)的版本號(hào)
getUserName:獲取數(shù)據(jù)庫(kù)的用戶(hù)名
getURL:獲取數(shù)據(jù)庫(kù)連接的URL
getDriverName:獲取數(shù)據(jù)庫(kù)的驅(qū)動(dòng)名稱(chēng)
driverVersion:獲取數(shù)據(jù)庫(kù)的驅(qū)動(dòng)版本號(hào)
isReadOnly:查看數(shù)據(jù)庫(kù)是否只允許讀操作
supportsTransactions:查看數(shù)據(jù)庫(kù)是否支持事務(wù)
(1)導(dǎo)入mysql依賴(lài)
<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);
}
}
(1)獲取數(shù)據(jù)庫(kù)元信息綜合信息
@Test
public void testDatabaseMetaData() throws SQLException {
//獲取數(shù)據(jù)庫(kù)元數(shù)據(jù)
DatabaseMetaData dbMetaData = conn.getMetaData();
//獲取數(shù)據(jù)庫(kù)產(chǎn)品名稱(chēng)
String productName = dbMetaData.getDatabaseProductName();
System.out.println(productName);
//獲取數(shù)據(jù)庫(kù)版本號(hào)
String productVersion = dbMetaData.getDatabaseProductVersion();
System.out.println(productVersion);
//獲取數(shù)據(jù)庫(kù)用戶(hù)名
String userName = dbMetaData.getUserName();
System.out.println(userName);
//獲取數(shù)據(jù)庫(kù)連接URL
String userUrl = dbMetaData.getURL();
System.out.println(userUrl);
//獲取數(shù)據(jù)庫(kù)驅(qū)動(dòng)
String driverName = dbMetaData.getDriverName();
System.out.println(driverName);
//獲取數(shù)據(jù)庫(kù)驅(qū)動(dòng)版本號(hào)
String driverVersion = dbMetaData.getDriverVersion();
System.out.println(driverVersion);
//查看數(shù)據(jù)庫(kù)是否允許讀操作
boolean isReadOnly = dbMetaData.isReadOnly();
System.out.println(isReadOnly);
//查看數(shù)據(jù)庫(kù)是否支持事務(wù)操作
boolean supportsTransactions = dbMetaData.supportsTransactions();
System.out.println(supportsTransactions);
}
(2)獲取數(shù)據(jù)庫(kù)列表
@Test
public void testFindAllCatalogs() throws Exception {
//獲取元數(shù)據(jù)
DatabaseMetaData metaData = conn.getMetaData();
//獲取數(shù)據(jù)庫(kù)列表
ResultSet rs = metaData.getCatalogs();
//遍歷獲取所有數(shù)據(jù)庫(kù)表
while (rs.next()) {
//打印數(shù)據(jù)庫(kù)名稱(chēng)
System.out.println(rs.getString(1));
}
//釋放資源
rs.close();
conn.close();
}
(3)獲取某數(shù)據(jù)庫(kù)中的所有表信息
@Test
public void testFindAllTable() throws Exception {
//獲取元數(shù)據(jù)
DatabaseMetaData metaData = conn.getMetaData();
//獲取所有的數(shù)據(jù)庫(kù)表信息
ResultSet rs = metaData.getTables("庫(kù)名", "%", "%", new String[]{"TABLE"});
//拼裝table
while (rs.next()) {
//所屬數(shù)據(jù)庫(kù)
System.out.println(rs.getString(1));
//所屬schema
System.out.println(rs.getString(2));
//表名
System.out.println(rs.getString(3));
//數(shù)據(jù)庫(kù)表類(lèi)型
System.out.println(rs.getString(4));
//數(shù)據(jù)庫(kù)表備注
System.out.println(rs.getString(5));
System.out.println("--------------");
}
}
(4)獲取某張表所有的列信息
@Test
public void testFindAllColumns() throws Exception {
//獲取元數(shù)據(jù)
DatabaseMetaData metaData = conn.getMetaData();
//獲取所有的數(shù)據(jù)庫(kù)某張表所有列信息
ResultSet rs = metaData.getColumns("庫(kù)名", "%", "表名","%");
while(rs.next()) {
//表名
System.out.println(rs.getString("TABLE_NAME"));
//列名
System.out.println(rs.getString("COLUMN_NAME"));
//類(lèi)型碼值
System.out.println(rs.getString("DATA_TYPE"));
//類(lèi)型名稱(chēng)
System.out.println(rs.getString("TYPE_NAME"));
//列的大小
System.out.println(rs.getString("COLUMN_SIZE"));
//小數(shù)部分位數(shù),不適用的類(lèi)型會(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 開(kāi)始
System.out.println(rs.getString("ORDINAL_POSITION"));
System.out.println("--------------");
}
}
參數(shù)元數(shù)據(jù)(ParameterMetaData):是由PreparedStatement對(duì)象通過(guò)getParameterMetaData方法獲取而 來(lái),主要是針對(duì)PreparedStatement對(duì)象和其預(yù)編譯的SQL命令語(yǔ)句提供一些信息,ParameterMetaData能提供占位符參數(shù)的個(gè)數(shù),獲取指定位置占位符的SQL類(lèi)型等等 以下有一些關(guān)于ParameterMetaData的常用方法:
getParameterCount:獲取預(yù)編譯SQL語(yǔ)句中占位符參數(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);
}
結(jié)果集元數(shù)據(jù)(ResultSetMetaData):是由ResultSet對(duì)象通過(guò)getMetaData方法獲取而來(lái),主要是針對(duì)由數(shù)據(jù)庫(kù)執(zhí)行的SQL腳本命令獲取的結(jié)果集對(duì)象ResultSet中提供的一些信息,比如結(jié)果集中的列數(shù)、指定列的名稱(chēng)、指 定列的SQL類(lèi)型等等,可以說(shuō)這個(gè)是對(duì)于框架來(lái)說(shuō)非常重要的一個(gè)對(duì)象。 以下有一些關(guān)于ResultSetMetaData的常用方法:
getColumnCount:獲取結(jié)果集中列項(xiàng)目的個(gè)數(shù)
getColumnType:獲取指定列的SQL類(lèi)型對(duì)應(yīng)于Java中Types類(lèi)的字段
getColumnTypeName:獲取指定列的SQL類(lèi)型
getClassName:獲取指定列SQL類(lèi)型對(duì)應(yīng)于Java中的類(lèi)型(包名加類(lèi)名
@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語(yǔ)句
ResultSet rs = pstmt.executeQuery();
//獲取ResultSetMetaData對(duì)象
ResultSetMetaData metaData = rs.getMetaData();
//獲取查詢(xún)字段數(shù)量
int columnCount = metaData.getColumnCount();
System.out.println("字段總數(shù)量:"+ columnCount);
for (int i = 1; i <= columnCount; i++) {
//獲取表名稱(chēng)
System.out.println(metaData.getColumnName(i));
//獲取java類(lèi)型
System.out.println(metaData.getColumnClassName(i));
//獲取sql類(lèi)型
System.out.println(metaData.getColumnTypeName(i));
System.out.println("----------");
}
}
創(chuàng)建maven工程并導(dǎo)入以下依賴(lài)
<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)如下
文主要講述:文件名工具類(lèi)Apache之org.apache.commons.lang.StringEscapeUtils。
java中轉(zhuǎn)義是指:將某些特殊字符轉(zhuǎn)換為它在特定場(chǎng)景中原來(lái)的意義。
轉(zhuǎn)義字符是指無(wú)法直接顯示的字符。
常見(jiàn)的場(chǎng)景是:
1、同一字符在不同編碼中表達(dá)形式不一樣。
2、某些特定的字符在不同環(huán)境、不同語(yǔ)言中表達(dá)形式不一樣。
下面演示StringEscapeUtils的用法:
//轉(zhuǎn)義為Unicode編碼
String escape = StringEscapeUtils.escapeJava("古德貓寧");
輸出:\u53E4\u5FB7\u732B\u5B81
//反轉(zhuǎn)義Unicode編碼
String unescape = StringEscapeUtils.unescapeJava("\u53E4\u5FB7\u732B\u5B81");
輸出:古德貓寧
sql注入是網(wǎng)絡(luò)攻擊方式之一,而防止SQL注入的唯一方法是SQL參數(shù)化。
escapeSql就提供了sql轉(zhuǎn)義功能,從而實(shí)現(xiàn)防止sql注入。
String userId = "000001";
String sql = "select user_name from tab_emp where user_id = " + StringEscapeUtils.escapeSql("'" + userId + "'");
輸出:select user_name from tab_emp where user_id = ''000001''
//轉(zhuǎn)義為html腳本
String escapeHtml = StringEscapeUtils.escapeHtml("<div>Hello World!</div>");
輸出:<div>Hello World!</div>
//反轉(zhuǎn)義html腳本
String unescapeHtml = StringEscapeUtils.unescapeHtml("<div>Hello World!</div>");
輸出:<div>Hello World!</div>
//轉(zhuǎn)義為JS腳本
String escapeJS = StringEscapeUtils.escapeJavaScript("<script type=\"text/javascript\">alert('哈哈')<script>");
輸出:<script type=\"text\/javascript\">alert(\'\u54C8\u54C8\')<script>
//反轉(zhuǎn)義JS腳本
String unescapeJS = StringEscapeUtils.unescapeJavaScript("<script>alert(\'哈哈\')<script>");
輸出:<script>alert('哈哈')<script>
String escapeCsv = StringEscapeUtils.escapeCsv("He said \"'I love you'\"");
輸出:"He said ""'I love you'"""
String unescapeCsv = StringEscapeUtils.unescapeCsv("\"He said \"\"'I love you'\"\"\"");
輸出:He said "'I love you'"
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。