文章目錄
- 一、多線程
-
- 1. 緩存擊穿
- 2. 線程池
-
- (1)體系結構
- (2)Executors
- (3)ExecutorService
- 3. 掉單問題
- 二、資料庫相關
-
- 1. cast與decimal
- 2. 業務邏輯
-
- (1)DQL
- (2)DML
- 3. resultMap
- 4. Dao層方法起名字
- 5*. Datetime時區問題
- 6. 多表聯查查出的值的存放位置
- 7. \$()與$(document).ready()
- *8. 屬性和JS中均定義了某個事件
- 9. MyBatis逆向工程
- 10*. 資料庫樂觀鎖機制
- 11. 資料庫中的日期
- 三、JS相關
-
- 1. 使用正則
- 2*. $.each()中使用return
- 3*. $().text()
- 4. jQuery選取
- 5. 對象失去焦點
- 6. JQ中的md5加密
- 四、一些問題
-
- 1. dubbo逾時重試問題
- 2. 産品超賣問題
- 3. 訂單不過夜
- 五、一些操作
-
- 1. 随機
- 3. TODO
- 4. 脫敏操作
- 5. 取小數點後兩位
- 6. 日期格式與整數相加
-
- (1)使用Calendar
- (2)使用Commons-lang3工具包
- 7. 引入非(本地和中央)倉庫的jar包
- 8. 生成全局唯一單号
- 9. 時序圖
- 10. 背景跳轉頁面
- 六、一些功能
-
- 1. 短信驗證碼進行登入
- 2. 認證功能
- 3. 登入後傳回上次停留的頁面
- 4. 投資排行榜功能
- 5. 支付寶網頁支付功能
- 6. 生成簡單的圖檔驗證碼
- 7. 生成二維碼
- 七、一些約定俗稱
-
- 8. 登出
- 其它
-
- 1. 注解參數
-
- (1)@RequestParam
- 2. maven的繼承
- 5. jsp中對字元串處理
- 6. templates檔案夾
- 可變長參數
一、多線程
1. 緩存擊穿
緩存擊穿,是指查詢一個資料庫一定不存在的資料。正常的使用緩存流程大緻是,資料查詢先進行緩存查詢,如果key不存在或者key已經過期,再對資料庫進行查詢,并把查詢到的對象,放進緩存。如果資料庫查詢對象為空,則不放進緩存。在高并發下,當第一個線程将資料放入緩存中之前,會出現多個線程從資料庫中進行查詢的現象,即緩存穿透
解決方法:雙重判斷加同步代碼塊
固定文法:
if(第一次對值進行判空,為空時繼續){
synchronized (this){
對值進行更新
if(第二次對值進行判空,為空時繼續){
從資料庫中擷取值
}
}
}
一個小例子:
public Integer queryAllUserCount() {
BoundValueOperations<Object, Object> ops = redisTemplate.boundValueOps(Const.ALL_USER_COUNT);
Integer allUserCount = (Integer) ops.get();
if (!ObjectUtils.allNotNull(allUserCount)) {
synchronized (this) {
allUserCount = (Integer) ops.get();
if (!ObjectUtils.allNotNull(allUserCount)) {
allUserCount = userMapper.queryAllUserCount();
ops.set(allUserCount);
}
}
}
return allUserCount;
}
2. 線程池
線程池是提供一個線程隊列,隊列中儲存着所有等待狀态的線程。避免了建立與銷毀的額外開銷,提高了響應的速度。
(1)體系結構
線程池的體系結構:
java.util.concurrent.Executor 負責線程的使用和排程的根接口
|--ExecutorService 子接口: 線程池的主要接口
|--ThreadPoolExecutor 線程池的實作類
|--ScheduledExceutorService 子接口: 負責線程的排程
|--ScheduledThreadPoolExecutor : 繼承ThreadPoolExecutor,實作了ScheduledExecutorService
(2)Executors
Executors是一個工具類,用于建立線程池 :
-
:用來建立一個可以無限擴大的線程池,适用于伺服器負載較輕,執行很多短期異步任務。newCachedThreadPool
-
:建立一個固定大小的線程池,因為采用無界的阻塞隊列,是以實際線程數量永遠不會變化,适用于可以預測線程數量的業務中,或者伺服器負載較重,對目前線程數量進行限制。newFixedThreadPool
-
:建立一個單線程的線程池,适用于需要保證順序執行各個任務,并且在任意時間點,不會有多個線程是活動的場景。newSingleThreadExecutor
-
:可以延時啟動,定時啟動的線程池,适用于需要多個背景線程執行周期任務的場景。newScheduledThreadPool
-
:建立一個擁有多個任務隊列的線程池,可以減少連接配接數,建立目前可用cpu數量的線程來并行執行,适用于大耗時的操作,可以并行來執行newWorkStealingPool
(3)ExecutorService
ExecutorService 對象的一些方法:
-
:submit()
- 送出Callable對象,需要重寫
方法,傳回Futurecall()
- 送出Runnable對象,需要重寫
方法,傳回Future,run()
- 送出Callable對象,需要重寫
-
:shutdown()
- 關閉線程池
3. 掉單問題
簡單的一個處理:使用定時器去掃描掉單,然後再處理,使用鎖解決定時器和支付同時運作的情況(在支付時(建立了訂單但未支付完成),定時器同時掃描到了該條資料,使用鎖解決沖突)
二、資料庫相關
1. cast與decimal
- CAST:進行資料類型轉換,AS關鍵字分隔的源值和目标資料類型,前面是源值,後面是目标資料類型
-
DECIMAL:格式化數字(小數點前的資料表示最多顯示的總位數,小數點後的資料表示保留幾位小數)
例如:求出學生的平均年齡,
select cast(avg(age) as decimal(10,2)) as avgAge from student
2. 業務邏輯
(1)DQL
一個查詢語句就是一個完整的業務邏輯,目前台的響應要擷取多個不相關的資料值(不同表或者沒有關系的資料)的時候,在Controller層要多次調用相應的Service層,分别查出這些不相關的資料。
(2)DML
在Controller層,對于DML,要調用完整的業務,有時候是多個DML語言是一個業務。這時候給方法起的名字一般在控制層是業務名稱,在業務層再細化成各個操作的名字
3. resultMap
MyBatis逆向工程生成的 mapper.xml 中使用了
<mapper>
标簽,如果查詢對應的實體類,則在 标簽中使用 resultMap屬性指定與實體類的映射關系,因為可能存在表中的字段與實體類的字段不同的情況
4. Dao層方法起名字
Dao層方法的名字要細化,以便方法可以複用
5*. Datetime時區問題
java中使用Date對象往MySQL資料庫中存儲 Datetime 類型的日期,總是少加個時區。
原因:指定資料庫的url時,有個參數
serverTimezone
是用來指定時區的,而我寫的是UTC(世界标準時間),将UTC改為
CTT
(Asia&Shanghai)即可。
spring.datasource.url=jdbc:mysql://localhost:3306/p2p?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT
6. 多表聯查查出的值的存放位置
可以在含有外鍵的表對應的實體類中,添加外鍵對應字段,該字段是外鍵對應的表的實體類
例如:
學生表和班級表:學生表中存放有班級表的外鍵,當多表聯查時,可以在學生類中添加一個班級類的字段,在mapper.xml檔案中,使用
<resultMap>
标簽和
<association>
标簽來查出的值與實體類字段進行映射
<resultMap id="BaseResultMap" type="com.wkcto.springboot.model.Student">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
<association property="classRoom" javaType="com.wkcto.springboot.model.ClassRoom">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
</association>
</resultMap>
7. $()與$(document).ready()
這兩個函數的功能是等價的,都是在頁面加載完畢後再執行
$(function(){})
//這兩個方法是等價的,都是在頁面加載完畢後,然後執行
$(document).ready(function(){}) //在文檔加載後激活函數
*8. 屬性和JS中均定義了某個事件
當html标簽的屬性和JS中均定義了某個相同的事件,則隻會觸發屬性中定義的事件
9. MyBatis逆向工程
使用MyBatis逆向工程,推薦字段中分隔單詞使用下劃線"
_
",則逆向工程生成的實體類,會自動把下劃線去掉,并轉換為駝峰命名法,是以,要使用
<resultMap>
标簽來将表中的字段與實體類中的字段一一映射起來
10*. 資料庫樂觀鎖機制
實作:基于資料版本(Version)記錄機制實作
具體可通過給表加一個版本号或時間戳字段實作,當讀取資料時,将version字段的值一同讀出,資料每更新一次,對此version值加一。當我們送出更新的時候,判斷目前版本資訊與第一次取出來的版本值大小,如果資料庫表目前版本号與第一次取出來的version值相等,則予以更新,否則認為是過期資料,拒絕更新,讓使用者重新操作。
11. 資料庫中的日期
current_date:隻顯示日期,2020-01-01
current_time:隻顯示時間,23:11:49
current_timestamp:顯示日期加時間,2020-01-01 23:11:49
三、JS相關
1. 使用正則
常用的正規表達式:https://editor.csdn.net/md?articleId=103735776
在JS中使用正規表達式:
- 文法:
,如果比對則傳回true,否則傳回false/正規表達式/.test(字元串)
2*. $.each()中使用return
jquery 的 each 方法中如果使用了
return true
或者
return
相當于是
continue
,而
return false
相當于是
break
。并不會使方法結束
3*. $().text()
$().text()
會将$() 中所有取出的DOM元素中的文本内容進行拼接,而其它的方法如
$().html()
隻會對第一個DOM元素進行操作
4. jQuery選取
-
:選取所有class屬性以Err結尾的div标簽$("div[class$=Err]")
-
:選取所有class屬性以pro開頭的div标簽$("div[class^=pro]")
5. 對象失去焦點
- 為 id 為 input 的标簽綁定失去焦點事件:
- 觸發 id 為 input 的标簽的失去焦點事件:
6. JQ中的md5加密
四、一些問題
1. dubbo逾時重試問題
在IDEA中進行debug時,因為發生了逾時,dubbo自動進行重試,會多次發送請求,如果剛好是往資料庫中插入資料,而且還不是在Service層的事務中,那麼可能會發生往資料庫中重複插入多條相同的資料問題(主鍵自增)
2. 産品超賣問題
使用資料庫樂觀鎖解決
3. 訂單不過夜
生成訂單,如果不付款,會在一定時間内過期 ,比如說在15分鐘内過期,但是如果是在23點55分生成的訂單,則會在 0 點的時候過期,即時間并不夠15分鐘
五、一些操作
1. 随機
将要随機生成的元素存放在數組中,可以是任何元素,然後使用
Math.Random()*array.length
來随機生成數組的下标
注意:java中預設小數轉為整數是去掉小數點後的部分,使用
Math.round(num)
可以将小數四舍五入進行取整
3. TODO
在IDEA中使用TODO來記錄自己還沒有完成的功能
4. 脫敏操作
脫敏操作是指:在頁面中,将敏感的資料變得不敏感,比如金額,手機号等,使用
***
來對原資料進行一定修改,進行脫敏操作
5. 取小數點後兩位
- 各種語言通用的一種思路:将小數乘以100,再取整,再将整數除以100
-
java:使用DecimalFormat對象
0:代表一個數字,如果不存在顯示0
#:代表一個或多個數字,如果不存在則顯示為空
DecimalFormat decimalFormat = new DecimalFormat("#.00"); Double d = Double.valueOf(decimalFormat.format(13.13521));//
- js:toFixed(四舍五入保留的位數):
var num = new Number(12.3863); document.write(num.toFixed(2));//輸入出:12.39
6. 日期格式與整數相加
(1)使用Calendar
主要方法:
-
:add(field, amount)
- field:int類型,指定要加的整數的機關,是常量,Calendar.DATE表示天,
- amount:int類型,指定數量
public static Date getDateByAddDays(Date date, Integer count) {
//日期處理類對象
Calendar instance = Calendar.getInstance();
//設定日期處理類對象的日期值
instance.setTime(date);
//在指定日期上添加天數
instance.add(Calendar.DATE, count);
return instance.getTime();
}
(2)使用Commons-lang3工具包
直接使用 DateUtils 的相關函數即可,例如:
-
:在給定的日期上加上指定的天數DateUtils.addDays(Date date, int amount)
-
:在給定的上期上加上指定的月數DateUtils.addMonths(Date date, int amount)
7. 引入非(本地和中央)倉庫的jar包
- groupId/artifactId/version都可以随便填,但是不能有重複
-
标簽指定檔案的位置,一般為system<scope>
- 使用
标簽指定本地jar包的位置<systemPath>
-
是項目的根目錄${basedir}
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java20170324180803.jar</systemPath>
</dependency>
8. 生成全局唯一單号
- 可以使用時間戳加上 redis 全局唯一數字
String s1 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
Long l2 = redisTemplate.opsForValue().increment("onlyNum", 1L);
String result = s1 + l2;
- 使用電話号碼加上 redis 全局唯一數字(将上面的時間戳換成電話号即可)
9. 時序圖
要會看時序圖,會畫時序圖,對于複雜的業務邏輯,可以畫個時序圖,使用Rational工具
實線是請求,虛線是響應
ctrl + d 是删除元件
10. 背景跳轉頁面
從背景通路其它網站的頁面,比如說支付寶的頁面,可以使用請求轉發,但是請求轉發隻能是get請求方式,請求參數都在位址欄中,這時候可以采用如下的方式:
- 先跳轉到一個自己網站中的一個頁面
- 在該頁面中使用from表單内加隐藏域,并使用js在頁面加載完畢後自動送出的方式實作get轉post請求
例如:
<form method="post" action="http://localhost:9094/pay/api/alipay">
<input type="hidden" name="out_trade_no" th:value="${out_trade_no}">
<input type="hidden" name="total_amount" th:value="${total_amount}">
<input type="hidden" name="subject" th:value="${subject}">
</form>
<script>document.forms[0].submit()</script>
六、一些功能
1. 短信驗證碼進行登入
随機生成的驗證碼存放在redis中,鍵是手機号,值是驗證碼
主要的實作過程:https://blog.csdn.net/zyx1260168395/article/details/103747807
2. 認證功能
當隻更新某個字段時,實體類中隻需傳入需要更新的字段即可,即便是實體類中的字段的值和資料庫中的值相同,沒必要将其傳入,因為雖然值一樣,但還是更新了一遍資料庫,速度慢
比如認證時要更新使用者的姓名和身份證号
思路一:從可以用從Session中取出的user(含有許多資訊),加入身份證号和姓名,再傳入dao層進行更新,但是雖然隻用到了實體類中的id,姓名和身份證号,但是user表中其它的字段也更新了一遍,會造成速度緩慢,是以使用思路二
思路二:建立一個User,隻加入需要改的字段即可
3. 登入後傳回上次停留的頁面
思路:跳往登入頁時,在前端擷取目前頁的網址,當作請求參數傳遞到背景,然後跳到登入頁後,存在登入頁的一個隐藏域中,當登入成功時,擷取到這個隐藏域中的位址,再跳到這個位址
擷取目前頁的網址(Thymeleaf):
主要屬性:
-
:擷取目前頁面的URL#httpServletRequest.requestURL
-
:擷取目前頁面的請求參數,為null時省略#httpServletRequest.queryString
var redirectUrl = [[${#httpServletRequest.requestURL + (#httpServletRequest.queryString == null? "": "?" + #httpServletRequest.queryString)}]];
//這兩種方法均可
var redirectUrl = [[${#strings.replace(#httpServletRequest.requestURL + '?' + #httpServletRequest.queryString,"?null","")}]];
4. 投資排行榜功能
使用redis中的zset集合(zset可以用來解決各種排行榜問題),key為電話号碼,score為累計投資金額,使用者每投資一筆,就往score中加一筆。
使用到的方法:
incrementScore(K, V, delta)
:元素分數增加,delta是增量
rangeWithScores(K,start,end)
:鍵為K的集合,索引start<=index<=end的元素子集,傳回泛型接口(包括score和value),正序
reverseRangeWithScores(K,start,end)
:鍵為K的集合,索引start<=index<=end的元素子集,傳回泛型接口(包括score和value),倒序
ZSetOperations 操作解釋:https://www.cnblogs.com/pqy521/p/7009620.html
5. 支付寶網頁支付功能
要學會看API文檔,支付寶文檔:https://docs.open.alipay.com/catalog
6. 生成簡單的圖檔驗證碼
@RequestMapping(value = "/jcaptcha/captcha")
public void handleCaptchaRequest(HttpServletRequest request, HttpServletResponse response) {
//生成6位随機驗證碼,這裡就簡單的使用一個固定的字元串代替了
String captcha = "1Ag5Kw";
try {
//建立位元組數組輸出流
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
//建立圖檔緩存對象,BufferedImage.TYPE_INT_RGB : 表示一個圖像,該圖像具有整數像素的 8 位 RGB 顔色
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//擷取圖檔的畫布
Graphics graphics = bufferedImage.getGraphics();
//設定畫布背景色
graphics.setColor(Color.GREEN);
//設定畫布填充區域
graphics.fillRect(0, 0, WIDTH, HEIGHT);
//邊框區域
graphics.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
//設定字型顔色
graphics.setColor(Color.red);
//設定字型樣式
graphics.setFont(new Font("微軟雅黑", Font.ITALIC, 32));
//填充資料
graphics.drawString(captcha, 10, 38);
//将生成的驗證碼存放到session中
request.getSession().setAttribute(Constants.CAPTCHA, captcha);
ImageIO.write(bufferedImage, "jpeg", jpegOutputStream);
byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
//将驗證碼輸出到頁面
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0L);
response.setContentType("image/jpeg");
ServletOutputStream respOs = response.getOutputStream();
respOs.write(captchaChallengeAsJpeg);
respOs.flush();
respOs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
7. 生成二維碼
使用的是Google生成二維碼的依賴:
<!-- google生成二維碼依賴 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.0.0</version>
</dependency>
生成二維碼:
Map<EncodeHintType,Object> map = new HashMap<EncodeHintType, Object>();
//設定字元編碼
map.put(EncodeHintType.CHARACTER_SET, "UTF-8");
/*建立一個二維碼,
第一個參數是二維碼内容,
第二個參數是常量,二維碼的編碼規則,
第三、四個參數是二維碼的長和寬,
最後一個參數是設定的一些參數
*/
BitMatrix encode = new MultiFormatWriter().encode("https://blog.csdn.net/zyx1260168395", BarcodeFormat.QR_CODE, 200, 200, map);
//将二維碼轉換成圖檔,寫到指定的路徑上
Path path = FileSystems.getDefault().getPath("D://", "grcde.jpg");
MatrixToImageWriter.writeToPath(encode, "jpg", path);
//在浏覽器使用輸出流來将二維碼圖檔響應到前台
OutputStream out = response.getOutputStream();
MatrixToImageWriter.writeToStream(encode, "jpg", out);
七、一些約定俗稱
業務層:session;biz
jar包名字中有 source 的是源碼包,不帶 source 的是編譯好的jar包
用例:測試中用的功能點(用例圖,測試用例)
商戶系統:開發者的系統
管道、終端:
code:通信辨別,10000表示通信成功
請求/響應參數又叫請求/響應封包
timestamp:時間戳
sign:簽名
out_trade_no:商戶訂單号
8. 登出
@RequestMapping("/logout")
public Object logout(HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate();
return "redirect:/";
}
其它
1. 注解參數
當注解中的參數隻有一個 value 屬性時,可以省略value不寫,當注解中有多個參數時,每個參數都必須指定屬性名。
(1)@RequestParam
将請求參數綁定到你控制器的方法參數上(是springmvc中接收普通參數的注解)
屬性:
-
:參數名value
-
:是否包含該參數,預設為true,表示該請求路徑中必須包含該參數,如果不包含就報錯。required
-
:預設參數值,如果設定了該值,required=true将失效,自動為false,如果沒有傳該參數,就使用預設defaultValue
2. maven的繼承
父模版中的build标簽中的内容是不能被子子產品繼承的
5. jsp中對字元串處理
使用
fn
标簽:https://www.cnblogs.com/evolcq/p/3688443.html?utm_source=tuicool&utm_medium=referral
6. templates檔案夾
templates檔案夾中的檔案是受保護的,必須要通過背景才能進行通路
可變長參數
可變長參數可以傳遞多個值,也可以傳遞數組(類型要對)