复习资料整合
1、設(shè)計(jì)模式
代理模式--Spring中的AOP
工廠模式--BeanFactory所有的對象都是通過Factory
模板方法設(shè)計(jì)模式--RestTemplate --
策略設(shè)計(jì)模式---算法--策略--輪詢等--LoadBalancerClient--還有Ribbon
單例設(shè)計(jì)模式--Singleton--餓漢式:小對象,頻繁加載
懶漢式:用的時候加載對象,加鎖
門面設(shè)計(jì)模式--Slf4j 日志的查看
適配器模式--XxxAdapter 適配器接口繼承某個適配器,就不用實(shí)現(xiàn)全部的方法了,用哪個實(shí)現(xiàn)哪個就可以了
責(zé)任鏈模式--Interceptor 比如攔截器鏈等等
雙重校驗(yàn)的單例設(shè)計(jì)--很多地方都用到了
享元設(shè)計(jì)模式---所有的池都是,通過池減少對象的創(chuàng)建次數(shù)
建造者模式,過濾器模式,
2、常見的工廠模式
1、簡單工廠模式:每增加一款汽車都需要修改工廠類。違背開閉原則
2、工廠方法模式:只負(fù)責(zé)生產(chǎn)單一產(chǎn)品。避免簡單工廠模式的缺點(diǎn)。新增一款汽車只需要新建一家工廠即可。符合開閉原則
3、抽象工廠模式:工廠負(fù)責(zé)生產(chǎn)旗下多個產(chǎn)品(產(chǎn)品族)打比方:一個超級工廠里面分小工廠,工廠1生產(chǎn)輪胎、工廠2生產(chǎn)方向盤、工廠3生產(chǎn):底盤等等。新增汽車工廠實(shí)現(xiàn)此接口即可
抽象工廠模式會使用上:需要生產(chǎn)公司旗下的一系列產(chǎn)品(產(chǎn)品族)。同時其它公司旗下也會生產(chǎn)同樣一系列產(chǎn)品(可以理解成是共同抽象出來的部分)。只是兩個工廠生產(chǎn)出來品牌不一樣。
抽象工廠模式常見應(yīng)用:oracle和mysql數(shù)據(jù)庫應(yīng)用時,系統(tǒng)可以會替換數(shù)據(jù)庫,這時候抽象工廠模式就派上用場了。?
2、數(shù)據(jù)結(jié)構(gòu)
1、數(shù)組;是有序的元素序列,數(shù)組是在內(nèi)存中開辟一段連續(xù)的空間,并在此空間存放元素。查找快,增刪慢。
就像是一列火車,從1號車廂到最后一節(jié)車廂,每節(jié)車廂都有自己的固定編號,乘坐火車的人可以通過車廂號快速找到自己的位置。
2、鏈表,是一系列Node節(jié)點(diǎn)組成,每個節(jié)點(diǎn)包括兩部分(存儲數(shù)據(jù)元素的數(shù)據(jù)域,存儲下一個節(jié)點(diǎn)地址的指針域),多個節(jié)點(diǎn)之間通過地址進(jìn)行連接
一種遞歸的數(shù)據(jù)結(jié)構(gòu);單向鏈表和雙向鏈表兩種;不需要初始化容量、可以添加任意元素;
插入和刪除的時候只需要更新引用。
8、哈希表Hash Table:也叫散列表,通過(key-value)數(shù)據(jù)結(jié)構(gòu),可以快速實(shí)現(xiàn)查找、插入和刪除。
3、堆:可以被看做是一棵樹的數(shù)組對象
- 堆中某個節(jié)點(diǎn)的值總是不大于或不小于其父節(jié)點(diǎn)的值;
- 堆總是一棵完全二叉樹。
3、棧Stack,它是運(yùn)算受限的線性表,其限制是僅允許在標(biāo)的一端進(jìn)行插入和刪除操作,不允許在其他任何位置進(jìn)行添加、查找、刪除等操作。
按照“先進(jìn)后出”的原則來存儲數(shù)據(jù);像一個水桶 都是線性表
壓棧:就是存元素 彈棧:就是取元素。
4、隊(duì)列queue;先進(jìn)先出,也是一種運(yùn)算受限的線性表,其限制是僅允許在表的一端進(jìn)行插入,而在表的另一端進(jìn)行刪除。像一個水管,隊(duì)頭只允許刪除操作(出隊(duì)),隊(duì)尾只允許插入操作(入隊(duì))。
5、樹,是由 n(n>0)個有限節(jié)點(diǎn)組成的一個具有層次關(guān)系的集合;
分為:無序樹,二叉樹,滿二叉樹,二叉樹查找樹,紅黑樹
二叉樹BinaryTree:每個節(jié)點(diǎn)不超過2的有序樹(tree)
我們可以簡單理解成生活的樹的結(jié)構(gòu),只不過每個節(jié)點(diǎn)上都最多只能有兩個子節(jié)點(diǎn)。
頂上的叫根節(jié)點(diǎn),兩邊被稱作“左子樹”和“右子樹”。
紅黑樹:紅黑樹是最常見的平衡二叉樹(左右要實(shí)現(xiàn)“平衡,兩邊的子樹相等”)本身就是一顆二叉查找樹,平衡二叉樹的難點(diǎn)在于,當(dāng)刪除或者增加節(jié)點(diǎn)的情況下,如何通過左旋或者右旋的方式來保持左右平衡。
樹的鍵值仍然是有序的。紅黑樹的速度特別快,趨近平衡樹,查找葉子元素最少和最多次數(shù)不多于二倍
紅黑樹的特點(diǎn):
- 節(jié)點(diǎn)可以是紅色的或者黑色的
- 根節(jié)點(diǎn)是黑色的
- 葉子節(jié)點(diǎn)(特指空節(jié)點(diǎn))是黑色的
- 如果一個節(jié)點(diǎn)是紅色的,則它兩個子節(jié)點(diǎn)都是黑色的。也就是說在一條路徑上不能出現(xiàn)相鄰的兩個紅色節(jié)點(diǎn)。
- 從任一節(jié)點(diǎn)到其每個葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。
7、圖;圖是一種復(fù)雜的非線性結(jié)構(gòu),有頂點(diǎn)和有窮非空集合和頂點(diǎn)之間的集合組成,通常表示為G(V,E) ,其中G表示一個圖,V是圖G中 頂點(diǎn)的集合,E是圖G中 邊的集合
3、代理
1.1代理對象
JDK動態(tài)代理
特點(diǎn):
1.要求被代理者必須實(shí)現(xiàn)(有)接口.
2.JDK代理是jdk默認(rèn)提供的.
默認(rèn)情況下采用jdk實(shí)現(xiàn)aop,也可以強(qiáng)制使用CGlib
CGLIB動態(tài)代理
特點(diǎn):
1.不管被代理者是否有接口,都可以為其創(chuàng)建代理對象. 代理對象是目標(biāo)對象的子類.
2.cglib需要手動導(dǎo)入jar包
3.spring為了創(chuàng)建代理對象方便,自身自動添加cglib依賴項(xiàng)
CGLIB的大部分類是直接對Java字節(jié)碼進(jìn)行操作,這樣生成的類會在Java的永久堆中。如果動態(tài)代理操作過多,容易造成永久堆滿,觸發(fā)OutOfMemory內(nèi)存溢出異常
spring默認(rèn)使用jdk動態(tài)代理,如果類沒有接口,則使用cglib。Spring會自動在JDK動態(tài)代理和cglib之間轉(zhuǎn)換
1.2代理機(jī)制
反向代理---nginx
nginx是 一個高性能的HTTP 和 反向代理web服務(wù)器
ngnix有wendows版本,也有l(wèi)inux版本
在高連接并發(fā)的情況下,Nginx是Apache服務(wù)器不錯的替代品。
正向代理--路由vpn
方法的重寫
語法規(guī)則:兩同 兩小 一大
兩同:方法名相同,參數(shù)列表相同
一大:(子類方法的修飾符范圍>=父類的修飾符范圍--指的是訪問控制符)
兩小:子類方法的返回值類型<=父類方法的返回值類型(是引用類型的返回值是繼承關(guān)系的大小)(void和基本數(shù)據(jù)類型,返回類型必須保持一致)
子類方法拋出的異常類型<=父類的異常類型
重寫和重載的區(qū)別
方法的重載和重寫都是實(shí)現(xiàn)多態(tài)的方式,區(qū)別在于前者實(shí)現(xiàn)的是編譯時的多態(tài)性,而后者實(shí)現(xiàn)的是運(yùn)行時的多態(tài)性。
重載發(fā)生在一個類中,同名的方法如果有不同的參數(shù)列表(參數(shù)類型不同、參數(shù)個數(shù)不同或者二者都不同)則視為重載;
重寫發(fā)生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的參數(shù)列表,有兼容的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求,不能根據(jù)返回類型進(jìn)行區(qū)分。
?
IO流
序列化:ObjectOutputStream
將對象轉(zhuǎn)化為字節(jié)/Json字符串 保存在磁盤文件中
反序列化:ObjectInputStream
反序列化流對象來讀取恢復(fù)對象
將字節(jié)/Json字符串從磁盤文件中取出,重新恢復(fù)成對象
*每次反序列化時,拿之前序列化生成的UID作比較,一致時才能反序列成功
*解決方案1:一次序列化對應(yīng)一次反序列化操作
*解決方案2:將UID寫死,無論怎么修改Student類,UID都不變*/
private static final long serialVersionUID=1L;
4.1輸入流、輸出流
字節(jié)流:按8位傳輸以字節(jié) 為單位輸入輸出數(shù)據(jù)
字節(jié)輸入流:FileInputStream文件字節(jié)輸入流 BufferedInputStream高效字節(jié)輸入流
字節(jié)輸出流:FileOuputStream 、BufferOutputStream
字符流按照16位傳輸以字符為單位 輸入輸出數(shù)據(jù)
字符輸入流:FileReader文件字符輸入流、BufferedReader高效字符輸入流
字符輸出流:FileWriter、BufferWriter
Buffered為什么高效:Buffered實(shí)質(zhì)是通過內(nèi)部一個緩存數(shù)組來實(shí)現(xiàn)“緩沖區(qū)”的功能。BufferedInputStream是緩沖輸入流,它繼承于FilterInputStream。
BufferedInputStream的read()里面就寫好了類似于byte[] buf = new byte[1024];的方法,以至于它可以一下子讀到的字節(jié)比FileInputStream一下子讀到的字節(jié)多
BufferedInputStream 的好處之一就是能夠提高效率,是因?yàn)榇嬖诰彺婵臻g,就比如一個中轉(zhuǎn)倉庫,當(dāng)倉庫滿了的時候就把倉庫的物品一次搬移。緩存空間數(shù)據(jù)滿了之后就讀取一次把數(shù)據(jù)取走。
FileInputStream是一直讀取流中數(shù)據(jù),這樣相比于BufferedInputStream更加耗費(fèi)CPU,效率也就下降了
編碼轉(zhuǎn)換流:
輸出流 outputStreamWriter
輸入流 InputStreamReader
使用字符流:完全和輸入的字符保持一致
字節(jié)流:結(jié)果是二進(jìn)制文件:a是00 00 00 05,int是四個字節(jié) b是01,布爾是一個字節(jié)
c是00 47,char是兩個字節(jié)
JVM
java虛擬機(jī)
- Java虛擬機(jī)JVM 管理的內(nèi)存包括5個運(yùn)行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器,其中方法區(qū)和堆是由線程共享的數(shù)據(jù)區(qū),其他幾個是線程隔離的數(shù)據(jù)區(qū)
- JVM垃圾回收,年青代(Young)(分為伊甸園區(qū),存活區(qū))、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法(復(fù)制算法、標(biāo)記清除算法、標(biāo)記整理算法)。
內(nèi)存溢出和內(nèi)存泄露
內(nèi)存泄漏 Memory Leak 是指程序申請內(nèi)存后,無法釋放已申請的資源,一次沒關(guān)系,但是內(nèi)存泄漏次數(shù)多了就會導(dǎo)致內(nèi)存溢出OutOfMemory。new了對象,但是不歸還delete,產(chǎn)生堆積;
未清空對象的引用,或關(guān)流
②:垃圾回收
③:死循環(huán)問題,不結(jié)束
④:線程等待問題,線程溢出,無限遞歸調(diào)用
堆溢出:堆和棧的設(shè)置過小,過程中不停的創(chuàng)建對象,都會引起堆溢出
棧溢出:遞歸的調(diào)用過深
1.內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);
2.集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;
3.代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;
4.使用的第三方軟件中的BUG;
5.啟動參數(shù)內(nèi)存值設(shè)定的過小
6、本質(zhì)原因是創(chuàng)建了太多的線程,而能創(chuàng)建的線程數(shù)是有限制的,導(dǎo)致了這種異常的發(fā)生
7、并行或者并發(fā)回收器在GC回收時間過長
解決方法
異常
FileNotFoundException-------文件找不到異常--編譯異常,需要在編寫時就聲明
classNotFoundException------類無法加載異常 、類文件未找到異常
java.lang.Stack Over flowError --棧溢出異常---遞歸的內(nèi)存泄漏異常
Null Pointer Exception-----------空指針異常
ClassCastException--------------類轉(zhuǎn)換異常 (強(qiáng)轉(zhuǎn)的時候)
Index OutOf BoundsException---下標(biāo)越界異常
Arithmetic Exception-------------算數(shù)異常
SQLException-------操作數(shù)據(jù)庫異常
IOException--------輸入輸出異常,是失敗或中斷的I/O操作生成的異常的通用類。
NumberFormatException--------字符串格式轉(zhuǎn)換異常
SecurityException----------------由安全管理器拋出的異常,指示存在安全侵犯。
JDK JRE JVM 三者之間的關(guān)系
JDK java開發(fā)工具包--包含JRE+開發(fā)工具
開發(fā)java程序最小的環(huán)境為JDK,所以JDK是JAVA語言的核心,
JRE java運(yùn)行環(huán)境--包含JVM+運(yùn)行java程序必須的環(huán)境
運(yùn)行java程序最小的環(huán)境為JRE
JVM java虛擬機(jī)--負(fù)責(zé)加載class并運(yùn)行.class文件
將JAVA代碼轉(zhuǎn)換為對應(yīng)的操作系統(tǒng)可以理解的指令,可以運(yùn)行字節(jié)碼文件,可以跨平臺的核心部分
ajax請求的參數(shù)
url:發(fā)送請求的地址
type:請求方式post/get。默認(rèn)是get
contentType:文本類型
data:
dataType:傳輸?shù)臄?shù)據(jù)類
success:請求成功要執(zhí)行的代碼
error:
timeout:設(shè)置請求超時時間
async:默認(rèn)true--所有的請求都是異步,如果要同步請求,false,加鎖鎖住瀏覽器
cache:會不會從瀏覽器緩存中加載請求信息
VUE 生命周期函數(shù)
概念: 生命周期函數(shù),是VUE針對與用戶提供的擴(kuò)展的功能.如果編輯了生命周期函數(shù),則vue對象自動執(zhí)行,無需手動調(diào)用.
生命周期函數(shù)種類:
1. 初始化階段 beforeCreate創(chuàng)建前 created實(shí)例創(chuàng)建完成后直接調(diào)用
beforeMount掛載前 mounted--VUE對象真正的實(shí)例化,開始干活了
2. 使用:Vue對象的修改 beforeUpdate, updated
3. 對象銷毀 beforeDestroy destroyed
2.2生命周期函數(shù)難點(diǎn)講解(了解)
beforeCreate
官網(wǎng)說明: 在實(shí)例初始化之后,數(shù)據(jù)觀測 (data observer) 和 event/watcher 事件配置之前被調(diào)用。
解析: VUE對象被JS剛解析之后,實(shí)例化成功. 內(nèi)部的屬性暫時都為null.
created
官方說明: 在實(shí)例創(chuàng)建完成后被立即調(diào)用
解析: VUE對象開始加載其中的屬性和屬性的值,當(dāng)加載完成,對象實(shí)例化成功!!! 僅限于創(chuàng)建不執(zhí)行業(yè)務(wù)操作.
beforeMount
官方說明: 在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用。
解析: vue對象中 el: “#app”, 通過app指定的ID,將指定的區(qū)域交給Vue對象進(jìn)行管理.
mounted
官方說明: 實(shí)例被掛載后調(diào)用,這時 el 被新創(chuàng)建的 vm.$el 替換了。
解析: 當(dāng)對象創(chuàng)建完成之后,并且指定區(qū)域開始 “渲染”,將區(qū)域中的標(biāo)簽/表達(dá)式進(jìn)行解析加載. 當(dāng)數(shù)據(jù)加載成功之后,這時mounted執(zhí)行完成.這時用戶可以看到解析后的頁面.
事務(wù)
事務(wù)就是將一堆的SQL語句(通常是增刪改操作)綁定在一起執(zhí)行,要么都執(zhí)行成功,要么都執(zhí)行失敗
事務(wù)4個特性ACID
原子性(Atomicity,或稱不可分割性)綁定到一起,要么全成功,要么全失敗。
一致性(Consistency)多個系統(tǒng)中,保證數(shù)據(jù)是一致的
隔離性(Isolation,又稱獨(dú)立性)保證性能的同時(高并發(fā)),隔離用戶操作
持久性(Durability)持久影響的,對數(shù)據(jù)的修改是永久的,系統(tǒng)故障也不會丟失
· 開啟事務(wù):start transaction;BEGIN;
· 結(jié)束事務(wù):commit;(提交事務(wù))或rollback(回滾事務(wù)操作之前)。
隔離級別
1、讀未提交:Read uncommitted 效率高,安全性最差,可能發(fā)生并發(fā)數(shù)據(jù)問題,可以讀到比人未提交的數(shù)據(jù)
2、讀提交(read committed) 犧牲了效率提高了安全性,Oracle默認(rèn)的隔離級別
3、可重復(fù)讀(repeatable read)MySQL默認(rèn)的隔離級別,安全性較好,性能一般,產(chǎn)生緩存,不能看到你后開啟的事務(wù)內(nèi)容--多次讀同一數(shù)據(jù),讀到的數(shù)據(jù)是相同的。防止臟讀、不可重復(fù)讀,容易產(chǎn)生幻讀
當(dāng)隔離級別設(shè)置為Repeatable read 時,可以避免不可重復(fù)讀。當(dāng)singo拿著工資卡去消費(fèi)時,一旦系統(tǒng)開始讀取工資卡信息(即事務(wù)開始),singo的老婆就不可能對該記錄進(jìn)行修改,也就是singo的老婆不能在此時轉(zhuǎn)賬。
4、串行化(Serializable) 表級鎖,讀寫都加鎖,效率低下,安全性高,不能并發(fā)--解決幻讀
mandatory
never--從不創(chuàng)建實(shí)物
not-supported
supports--
required--創(chuàng)建新事物
required -new
nested---
事務(wù)注解@Transactional的參數(shù)
BeanFactory和FactoryBean的區(qū)別
區(qū)別:BeanFactory是個Factory,也就是IOC容器或?qū)ο蠊S, 它負(fù)責(zé)生產(chǎn)和管理bean的一個工廠
FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進(jìn)行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產(chǎn)或者修飾對象生成的 工廠Bean,它的實(shí)現(xiàn)與設(shè)計(jì)模式中的工廠模式和修飾器模式類似
Servlet
tomcat(Web服務(wù)連接器) 里面有多個servlet模塊,servlet里面有xml文件中servlet name 然后,有個class name,通過反射實(shí)例化
httpservlet是可以重寫,但是我們以后寫的所有的類都要繼承這個類,里面有HttpServletRequse 和 HttpServletResponse
public String findUserByIds(HttpServletRequest request)
{String id = request.getParameter("id");
手動JDBC
String url= "jdbc:mysql://localhost:3306/cgb2107?characterEncoding=utf8&serverTimezone=Asia/Shanghai";//解決中文亂碼
jdbc:傳輸協(xié)議//本機(jī)的IP地址localhost:數(shù)據(jù)庫的端口號/數(shù)據(jù)庫的名稱
注冊Driver驅(qū)動class.forName--通過反射
獲取鏈接對象DriverManager.getConnection,訪問的url和用戶名、密碼傳進(jìn)來
得到prepare Statement傳輸器,將sql骨架語句放進(jìn)來---用sql骨架,用問好代替參數(shù)
執(zhí)行sql,更新;executeUpdate()
處理結(jié)果集——查才處理,
關(guān)閉流close
描述AOP
①:AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來建立一種對象層次結(jié)構(gòu)。面向?qū)ο蠼鉀Q了層次的問題,比如下級繼承上級來增強(qiáng)上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點(diǎn)是通過代理方式對目標(biāo)類在運(yùn)行期間織入功能
面向切面的編程(AOP)實(shí)現(xiàn)了橫切關(guān)注的模塊化 避免代碼纏繞 削除代碼分散
②:AOP的三要素:切面、通知、切點(diǎn)
@Aspect切面:包括了切入點(diǎn)和advice---基本上是個類
JoinPoint連接點(diǎn):程序中的一個點(diǎn),例如方法的調(diào)用或者排除異常,每一個方法都是一個連接點(diǎn),
@pointcut 切點(diǎn):xml文件或者注解,選擇一個或者多個連接點(diǎn)表達(dá)式---在什么時候執(zhí)行
Advice(通知):在選擇的每個連接點(diǎn)執(zhí)行的代碼--基本是個方法
編織:將切面與主要代碼進(jìn)行結(jié)合的技術(shù),spring就提供了,就是把所有整合在了一起,將指定位置注入
③:通知的用法:
日志與跟蹤 事務(wù)管理 安全 緩存 錯誤處理 性能監(jiān)測 自定義業(yè)務(wù)規(guī)則
前置Advice
@Before如果advice拋出異常,則目標(biāo)方法不被執(zhí)行---關(guān)心方法訪問權(quán)限限制
后置Advice
@After--無論怎么,在方法之后都調(diào)用Advice
@AfterReturning--當(dāng)方法有返回值的時候才執(zhí)行Advice,
如果目標(biāo)方法出現(xiàn)異常--不是返回失敗,則advice不被調(diào)用
@AfterThrows--當(dāng)目標(biāo)方法拋出正確異常的時候才調(diào)用Advice,
必須是指定異常,但是不會停止異常傳播(沒辦法try/cratch)(調(diào)用有異常的方法本身也有了異常,方法之間的調(diào)用)
環(huán)繞Advice--阻止異常傳播
@Around注解和proceedingJoinPoint 添加的proceed()
1.spring bean是什么?
Bean是Spring框架中最核心的兩個概念之一(另一個是面向切面編程AOP,bean是一個由Spring IoC容器實(shí)例化、組裝和管理的對象。我們的應(yīng)用程序由一個個bean構(gòu)成
- 概念1:Bean容器,或稱spring ioc容器,主要用來管理對象和依賴,以及依賴的注入。
- 概念2:bean是一個Java對象,根據(jù)bean規(guī)范編寫出來的類,并由bean容器生成的對象就是一個bean。
- 概念3:bean規(guī)范。
2.bean的生命周期?
最常用的作用域--五個
一般用@Scope來標(biāo)識作用域
單例(長命對象):singleton作用域:Spring 只會為每一個bean創(chuàng)建一個實(shí)例,并保持bean的引用,在spring Ioc容器中僅存在一個bean實(shí)例,Bean以單利方式存在---默認(rèn)值
原型(短命對象):prototype作用域:一個bean定義對應(yīng)多個對象實(shí)例。每次從容器中調(diào)用Bean是,都返回一個新的實(shí)例,即每次調(diào)用getBean()時,相當(dāng)于new操作
request作用域:每次Http request請求都會創(chuàng)建一個新的bean,該作用域僅適用于WebApplicationContext環(huán)境
seesion作用域:同一個Http Session共享一個bean,不同session使用不同bean,僅WebApplicationContext環(huán)境
globalSeesion作用域:一般用于Porlet應(yīng)用環(huán)境,也僅適用于web環(huán)境
注解
一、spring
Spring是一個輕量級的開源框架,是為解決企業(yè)級應(yīng)用開發(fā)的復(fù)雜性而創(chuàng)建的,通過核心的Bean factory實(shí)現(xiàn)了底層的類的實(shí)例化和生命周期的管理。Spring的最根本使命是:簡化java開發(fā),整合第三方框架
三大核心組件Bean、Context、Core
Spring框架兩大核心:IoC和DI
Spring框架重點(diǎn)提供的IOC DI AOP
IOC:
控制反轉(zhuǎn): 將對象創(chuàng)建的權(quán)利交給Spring容器管理,由Spring容器管理對象的生命周期
DI: 依賴注入
創(chuàng)建對象時,如果該對象中有需要依賴的屬性,Spring負(fù)責(zé)為屬性賦值.
------------------------------------------------------------------------------------------------------
導(dǎo)入的包是org.springframework.beans.factory.annotation
@Autowired 基于注解的依賴注入,可以被使用再屬性域,方法,構(gòu)造函數(shù)上--先根據(jù)type判斷,在判斷name
@Qualifier @Autowired注解判斷多個bean類型相同時,就需要使用 @Qualifier("xxBean") 來指定依賴的bean的id:
@Resource 也是依賴的注入,先根據(jù)name判斷注入哪個對象, 屬性域和方法上 ---這個是javax包
@Scope bean的創(chuàng)建模式--單例和原型模式 singleton--只有一個實(shí)例 prototype--每一次都new
@Component@Controller @Service 當(dāng)使用基于注解的配置和類路徑掃描的時候,這些類就會被實(shí)例化,把創(chuàng)建的對象交給spring容器管理
@Configuration將配置類交給spring管理 添加在配置類config上面
---------------------------------------------------不熟悉的注解-----------------------------------------------------
@PropertySource 加載指定的配置文件 將pro文件交給spring容器管理 @PropertySource(value = "classpath:/mysql.properties",encoding = "UTF-8"),
@Value("${mysql.username}") springel表達(dá)式,簡稱spel,從spring容器內(nèi)獲取key,動態(tài)為屬性賦值
@Repository
@Repository和@Controller、@Service、@Component的作用差不多,都是把對象交給spring管理。@Repository用在持久層的接口上,這個注解是將接口的一個實(shí)現(xiàn)類交給spring管理。
不使用@Repository注解,idea會報警告,提示找不到這個bean,直接忽略即可
@Async 在方法上,這個注解描述的方法,底層會異步執(zhí)行--日志表數(shù)據(jù)量太大,不由web服務(wù)線程執(zhí)行,而是交給spring自帶的線程池中的線程去執(zhí)行;優(yōu)點(diǎn):不會長時間阻塞web服務(wù)(例如tomcat)線程
@EnableAsync 需要在啟動類上啟動異步執(zhí)行
@Cacheable
@EnableCaching
@CachePut
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm") 修改日期的格式
---------------------------------------------------AOP---------------------------------------------------------
@Aspect切面聲明,標(biāo)注在類、接口(包括注解類型)或枚舉上。
@Pointcut 切入點(diǎn)聲明,即切入到哪些目標(biāo)類的目標(biāo)方法。
@Before 前置通知, 在目標(biāo)方法(切入點(diǎn))執(zhí)行之前執(zhí)行
@After 后置通知, 在目標(biāo)方法(切入點(diǎn))執(zhí)行之后執(zhí)行,不管是否拋出異常都執(zhí)行
@AfterReturning 返回通知, 在目標(biāo)方法(切入點(diǎn))返回結(jié)果之后執(zhí)行,在 @After 的后面執(zhí)行
@AfterThrowing 異常通知, 在方法拋出異常之后執(zhí)行, 意味著跳過返回通知
@Around 環(huán)繞通知:目標(biāo)方法執(zhí)行前后分別執(zhí)行一些代碼,發(fā)生異常的時候執(zhí)行另外一些代碼
AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來建立一種對象層次結(jié)構(gòu)。面向?qū)ο蠼鉀Q了層次的問題,比如下級繼承上級來增強(qiáng)上級等等。AOP解決了左右級問題,比如分別給一下不同的對象的某些方法加入不同的額外功能等等。特點(diǎn)是通過代理方式對目標(biāo)類在 運(yùn)行期間 織入功能 面向切面的編程(AOP)實(shí)現(xiàn)了橫切關(guān)注的模塊化 避免代碼纏繞 削除代碼分散
②:AOP的三要素:切面、通知、切點(diǎn)
@Aspect切面:包括了切入點(diǎn)和advice---基本上是個類
JoinPoint連接點(diǎn):程序中的一個點(diǎn),例如方法的調(diào)用或者排除異常,每一個方法都是一個連接點(diǎn),
@pointcut 切點(diǎn):xml文件或者注解,選擇一個或者多個連接點(diǎn)表達(dá)式---在什么時候執(zhí)行
Advice(通知):在選擇的每個連接點(diǎn)執(zhí)行的代碼--基本是個方法
編織:將切面與主要代碼進(jìn)行結(jié)合的技術(shù),spring就提供了,就是把所有整合在了一起,將指定位置注入
③:通知的用法:
日志與跟蹤 事務(wù)管理 安全 緩存 錯誤處理 性能監(jiān)測 自定義業(yè)務(wù)規(guī)則
------------------------------------------事務(wù)----------------------------------------
@Transactional ---spring管理事務(wù)
1.默認(rèn)條件下,只攔截運(yùn)行時異常 ,執(zhí)行成功則提交事務(wù),不成功則回滾
* 2.rollbackFor: 指定異常的類型回滾 rollbackFor = RuntimeException.class
* 3.noRollbackFor: 指定異常不回滾 noRollbackFor = RuntimeException.class
添加的位置最好在增刪改方法上,不要直接添加在類上
二、springBoot
解決了spring的配置地獄的問題,內(nèi)嵌Servlet容器(tomcat),降低了對環(huán)境的要求; 提供一系列的starter pom啟動項(xiàng)簡化Maven配置,主要應(yīng)用了開箱即用的思想。
1、簡化了maven的操作(用什么jar包,就要添加依賴(坐標(biāo)))
2、內(nèi)嵌了Tomcat,可以訪問服務(wù)器里的程序
3、springboot整合了Spring+SpringMVC+Mybatis--基于ORM的思想以對象的方式操作數(shù)據(jù)庫
@RestControllerAdvice捕獲全局異常,都是對Controller進(jìn)行增強(qiáng)的,可以全局捕獲spring mvc拋的異常,指定遇到某種異常實(shí)現(xiàn)AOP處理.,返回值都是JSON串,通知是AOP中的技術(shù),解決特定問題。
@ExceptionHandler(value = Exception.class)或者是捕獲({RuntimeException.class})運(yùn)行時異常,用來捕獲指定的異常。
@SpringBootTest 單元測試類的注解
@SpringBootApplication--主啟動類注解
spring boot starter 這是啟動項(xiàng),
spring boot starter-web是整合了mvc,開箱即用的思想,有啟動項(xiàng)starter 就可以直接使用其中的功能
常用的啟動項(xiàng)/依賴
springboot的加載機(jī)制:開箱即用
pom文件只是添加了jar包,需要被調(diào)用才能生效,被主啟動類上的注解SpringBootApplication調(diào)用
springboot程序啟動,就是注解開始工作,
注解的結(jié)構(gòu)
1、元注解:@Inherited
2、配置類:@SpringBootConfiguration
3、自動配置:@EnableAutoConfiguration
4、包掃描:@ComponentScan
SpringbootApplication注解的說明
@Target 作用位置類/方法
@Retention 生命周期
@Documented 是否動態(tài)生成文檔
@Inherited 是否允許繼承
@SpringBootConfiguration 加載其他小的配置文件
>@Configration 標(biāo)識我是一個配置類
@EnableAutoConfiguration 開啟自動化配置
>@AutoConfigurationPackage 動態(tài)獲取主啟動類的包路徑
>@Import({AutoConfigurationImportSelector.class}) 開箱即用選擇器
@ComponentScan(excludeFilters排除某些類,如:影響項(xiàng)目的過濾器)
springboot開箱即用也可以說是工作流程:選擇器加載web,選擇器加載jdbc,選擇器加載MQ資源很多選擇器。pom文件中有1.web啟動項(xiàng)2.jdbc啟動項(xiàng);
web選擇器去掃描pom文件,發(fā)現(xiàn)web資源,線程開始工作,整個web項(xiàng)目中的包開始生效和加載,加載完成后,springMVC就有效了。程序返回繼續(xù)執(zhí)行下一個jdbc選擇器,找到j(luò)dbc的配置項(xiàng),開始整合JDBC,此時獲取資源從配置文件中。
所以啟動項(xiàng)里面寫的東西,都要在配置文件中配好,否則就會啟動失敗
選擇器沒找到啟動項(xiàng),就繼續(xù)向下執(zhí)行,直到所有的選擇器執(zhí)行完
總結(jié):springboot在內(nèi)部有N個選擇器,當(dāng)springBoot程序啟動時,依次加載選擇器,如果選擇器匹配pom文件中的啟動項(xiàng),則自動配置程序開始運(yùn)行
三、springMvc
概念:是spring框架的一個非常有名的產(chǎn)品,用來接收瀏覽器發(fā)來的請求,并返回?cái)?shù)據(jù),給瀏覽器做出響應(yīng)。遵循了MVC思想:主要是想要松耦合,實(shí)現(xiàn)代碼間的高內(nèi)聚,提高代碼的可維護(hù)性
M:是model模型,用來封裝數(shù)據(jù)
V:是view,視圖層,用來展示數(shù)據(jù)
C:是controller,控制層,作用是用來,接收請求和給出相應(yīng)
導(dǎo)入的包是org.springframework.web.bind.annotation
@RestController只能用在類上,讓瀏覽器訪問,是@ResponseBody 把數(shù)據(jù)轉(zhuǎn)化成Json串和@Controller合并起來的
@RequestMapping規(guī)定瀏覽器怎么訪問方法(類)
@GetMapping是一個組合注解,等價于@RequestMapping(method = RequestMethod.Get ),用于簡化開發(fā),@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
@PathVariable 綁定到操作方法的入?yún)⒅?/p>
@ResponseBody 返回對象利用jackson工具類轉(zhuǎn)換為json字符串--web提供就是mvc提供的
@RequestParam 參數(shù)名和請求參數(shù)名稱不同時使用,可以設(shè)置默認(rèn)值
@CrossOrigin//放行js的訪問請求,解決跨域問題---在controller中添加類注解--Ajax引擎--局部刷新,ajax是不支持跨域的
五個核心的組件工作原理:
1、前端控制器DispatcherServlet:接收請求,并分發(fā)請求
2、處理器映射器HandlerMapping:根據(jù)請求,找到具體能處理請求的類名,方法名
3、處理器適配器HandlerAdapter:正式開始調(diào)用當(dāng)方法處理請求,并返回結(jié)果
4、視圖解析器ViewResolver:把頁面找到,并把數(shù)據(jù)進(jìn)行解析
5、視圖渲染View:具體展示數(shù)據(jù)并返回給瀏覽器
四、Mybatis
MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射(ORM)。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。(mybatis在內(nèi)部將JDBC封裝).
MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄。
1、持久化 : 計(jì)算機(jī)在計(jì)算時,數(shù)據(jù)都在內(nèi)存中.如果斷電則數(shù)據(jù)清空,所以要求將內(nèi)存數(shù)據(jù)保存到磁盤中.--目標(biāo)--概念
2、持久層: 程序通過Dao/Mapper 與數(shù)據(jù)庫進(jìn)行交互的層級代碼 (Controller層 Service層 Dao/Mapper層與數(shù)據(jù)庫交互)--流程--具體操作
小結(jié): Mybatis是一個優(yōu)秀的持久層框架,基于ORM設(shè)計(jì)思想,實(shí)現(xiàn)了以對象的方式操作數(shù)據(jù)庫.
Mybatis的ORM并不完全,只完成了結(jié)果集映射,但是Sql需要自己手寫.所以也稱之為半自動化的ORM映射框架.
前身是apache的一個開源項(xiàng)目IBatis
@Param 在mapper接口中,如果方法傳遞的參數(shù)有多個,則可以將多個參數(shù)使用注解@Param("sex") String sex 封裝為Map
@Mapper注解
@MapperScan
#{}獲取數(shù)據(jù)時候
①:默認(rèn)存在預(yù)編譯效果,用?代替,防止sql注入攻擊
②:默認(rèn)為數(shù)據(jù)添加一對兒“”號
③:高效的
${}獲取數(shù)據(jù)
①:當(dāng)以字段名稱直接作為參數(shù)時候,使用${}
②:這樣的sql慎用,會有sql注入攻擊
所以能用#就不要用$
標(biāo)簽
<select>
<update>
<insert>
<delect>
<foreach> mybatis為了參數(shù)取值方便,特意封裝了遍歷的標(biāo)簽<foreach>,屬性:collection
1.如果傳遞的參數(shù)是數(shù)組, 則collection="array"
2.如果傳遞的參數(shù)是list集合, 則collection="list"
3.如果傳遞的參數(shù)是Map集合, 則collection="map中的key",item,open,close,index,separator
--------------------------------------------優(yōu)化
<typeAliases>別名包-有順序的,在<environment>上面配置<typeAliases>標(biāo)簽
<sql>簡化sql語句,提取sql語句中的共性
---------------------------------------------動態(tài)sql
<where>-<if>動態(tài)Sql要求根據(jù)對象中不為null的屬性,充當(dāng)where條件 實(shí)現(xiàn)動態(tài)查詢
<set>-<if>根據(jù)對象中不為null的屬性當(dāng)做set條件,去除set條件中多余的 ,號,多個條件并列的時候
<choose>-<when>/<otherwise>如果不想將全部的條件當(dāng)做if的判斷.則mybatis提供了分支結(jié)構(gòu) switch
<choose>:代表分支結(jié)構(gòu),只有一個條件有效.
<when test=" ">: 指定判斷的條件 和if類似.
<otherwise>: 如果上述的條件都不滿足時,該行代碼有效.
<resultMap>當(dāng)結(jié)果集中的字段名稱,與對象中的屬性不一致時,可以使用resultMap實(shí)現(xiàn)自定義的封裝.,resultType是<select>中的一個屬性,當(dāng)結(jié)果集中的字段名稱,如果與對象中的屬性的名稱一致時,才會實(shí)現(xiàn)自動的數(shù)據(jù)封裝
屬性:id---sql語句中返回結(jié)果集resultMap中的名稱
屬性:type---返回結(jié)果要封裝成的對象,應(yīng)該是全路徑,但是我們定義了別名包
<!--自定義映射關(guān)系
1.<id>標(biāo)簽代表主鍵 (每張表中都會有一個主鍵)
標(biāo)簽屬性:column: 代表結(jié)果集(數(shù)據(jù)庫表)中的字段.--相當(dāng)于查詢的時候給列起的別名
標(biāo)簽屬性:property: 對象中的屬性
2.<result>標(biāo)簽 除了主鍵之外的配置信息
-->
<association>標(biāo)簽---關(guān)聯(lián)封裝,如果需要封裝單個的對象-----子查詢,一對一
property 屬性名 封裝的是emp中的屬性dept
javaType 屬性dept的類型 參數(shù)是類型的pojo全路徑,但是已經(jīng)別名包,所以直接寫Dept
column=“子查詢的字段信息”
select=“sql的Id” 作用:根據(jù)column中的數(shù)據(jù) 實(shí)現(xiàn)子查詢
<collection>標(biāo)簽: 封裝集合的固定寫法. ----一對多
property: 指定屬性 ,要封裝的dept中集合對象的屬性名
ofType: 封裝List集合的泛型中的對象
<cache>:應(yīng)用二級緩存,在xml配置文件中
前端常見錯誤編號
2**--成功
3**--重定向,轉(zhuǎn)移
301--對象已永久移走,即永久重定向
302--對象已臨時移動
4**--客戶端錯誤--前端瀏覽器
400--錯誤的請求:url中的參數(shù)類型不匹配 和服務(wù)需要的參數(shù)類型不同
403--禁止訪問
404--未找到,url路徑不存在
401是認(rèn)證失敗--沒有攜帶令牌
403是沒有訪問權(quán)限--某種請求方式?jīng)]有在數(shù)據(jù)庫中設(shè)置權(quán)限
5**--服務(wù)器錯誤--后臺
500--內(nèi)部服務(wù)器錯誤,看后臺報錯
502--Web服務(wù)器用作網(wǎng)關(guān)或代理服務(wù)器時收到了無效響應(yīng)。
504--網(wǎng)關(guān)超時
505--HTTP版本不支持
500:請求的url未綁定參數(shù)--一定看后臺--IDEA中拋出了IllegalStateException的異常
Mybatis中的緩存機(jī)制
解決是用戶的只是查詢,不能提高入庫的速度,緩存可以有效降低用戶訪問物理設(shè)備的頻次.提高用戶響應(yīng)速度.
1.mybatis自身緩存 一級緩存/二級緩存
2.Redis緩存 讀取10萬次/秒, 寫 8.6萬次/秒
1-4-1一級緩存
概念說明: Mybatis默認(rèn)開啟一級緩存, 一級緩存可以在同一個SqlSession對象中執(zhí)行相同的SQL查詢時,第一次去查詢數(shù)據(jù)庫,并寫在緩存中。第二次會直接從緩存中取。但是當(dāng)執(zhí)行兩次查詢中間發(fā)生了增刪改的操作,則sqlSeesion緩存會被清空,每次查詢會先去緩存中找,如果找不到就去查數(shù)據(jù)庫,并把數(shù)據(jù)寫在緩存中。
Mybatis內(nèi)部緩存使用HashMap,key是sql語句+hashcode+statementId;
value是查詢出來的結(jié)果集映射成的java對象
1-4-2二級緩存
說明: 二級緩存mybatis中默認(rèn)也是開啟的.但是需要手動標(biāo)識.
同一個SqlSessionFactory內(nèi)部有效. 也就是不同的SqlSession都可以用,只要是同一個工廠
全局配置:默認(rèn)是開的
sqlSession查詢數(shù)據(jù)之后,會將緩存信息保存到一級緩存中.但是不會立即將 緩存交給二級緩存保管.如果需要使用二級緩存,則必須將sqlSession業(yè)務(wù)邏輯執(zhí)行成功之后關(guān)閉.sqlSession.close
局部配置:直接在xml配置文件中加<cache>標(biāo)簽就可以了
數(shù)據(jù)庫與緩存同步
高并發(fā)(熱點(diǎn)數(shù)據(jù))的項(xiàng)目,實(shí)現(xiàn)讀寫一致性--緩存與數(shù)據(jù)庫的一致性
當(dāng)我們在做數(shù)據(jù)庫與緩存數(shù)據(jù)同步時,究竟更新緩存,還是刪除緩存,究竟是先操作數(shù)據(jù)庫,還是先操作緩存?
其實(shí)如果業(yè)務(wù)簡單,只是去數(shù)據(jù)庫拿一個值,寫入緩存,那么更新緩存也是可以的。但是,淘汰緩存(刪除緩存)操作簡單,并且?guī)淼母弊饔弥皇窃黾恿艘淮蝐ache miss,建議作為通用的處理方式。
先操作緩存,還是先操作數(shù)據(jù)庫?
那么問題就來了,我們是先刪除緩存,然后再更新數(shù)據(jù)庫,還是先更新數(shù)據(jù)庫,再刪緩存呢?
先更新數(shù)據(jù)庫,再刪緩存依然會有問題,不過,問題出現(xiàn)的可能性會因?yàn)樯厦嬲f的原因,變得比較低!數(shù)據(jù)庫的讀操作的速度遠(yuǎn)快于寫操作的因此產(chǎn)生臟數(shù)據(jù)的可能性會很小。(不然做讀寫分離干嘛,做讀寫分離的意義就是因?yàn)樽x操作比較快,耗資源少)
Mybatis-plus
以對象的方式操作數(shù)據(jù)庫
Mybatis是半自動化的ORM映射框架.半自動: Sql是自己手寫的,但是結(jié)果集映射是自動的.
MybatisPlus: 全自動的ORM 映射框架,對Mybatis的擴(kuò)展.在UserMapper上繼承BaseMapper<User>接口,baseMapper中的新增方法1個,修改方法是:2個?
1、Mybatis操作的實(shí)質(zhì):sql把公共的部分提取出來
調(diào)用步驟:
1.用戶執(zhí)行userMapper.insert(user);
2.根據(jù)繼承的關(guān)系 BaseMapper.insert(user);
3.MP在內(nèi)部生成sql之后交給Mybatis調(diào)用 最終實(shí)現(xiàn)數(shù)據(jù)操作
2、MP映射注解
@TableName("demo_user") //對象與表名映射,如果相同可以不寫,添加在類上
@TableId(type = IdType.AUTO)//主鍵自增,添加在屬性上
@TableField("age") //實(shí)現(xiàn)屬性與字段映射
@TableField(exist = false)//排除不必要的字段
3、QueryWrapper條件構(gòu)造器,動態(tài)拼接where條件,默認(rèn)的關(guān)系連接符 and
* 1. eq =
* 2. gt >
* 3. lt >
* 4. ge >=
* 5. le <=
* 6. ne <>
queryWrapper.gt("age",18).eq("sex","男");//利用條件構(gòu)造器自己構(gòu)建條件
* 7.模糊查詢
queryWrapper.like("name","君") //"%君%"
queryWrapper.likeLeft("name","君") //"%君"
* 8.in--多個相同參數(shù)--包裝類型
Integer[] ids = {1,3,4,5};
queryWrapper.in("id",ids).orderByDesc("age");//desc排序
*9.動態(tài)sql
動態(tài)sql實(shí)現(xiàn),根據(jù)不為null的屬性當(dāng)做where條件
Spring提供的API StringUtils.hasLength(sex);---判斷字符串API
對于字符串來說,要判斷是否第null和空串代替了:/boolean flag = sex !=null && "".equals(sex);
對于數(shù)字來說,就直接判斷是否為空就可
boolean flag = StringUtils.hasLength(sex);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt(age!=null, "age",age )//三個屬性,添加了判斷條件,條件成立則拼接
.eq(flag,"sex",sex);
4、MybatisPlus API介紹
selectById---返回一個對象
selectList--返回多個對象集合
selectObjs--只想獲取第一列數(shù)據(jù)--做關(guān)聯(lián)查詢時可以使用
五、lombok
@Data //動態(tài)生成get/set/toString/equals等方法
@Accessors(chain = true) //開啟鏈?zhǔn)郊虞d,換名就方便了,不用一直寫對象.方法 重寫set
@NoArgsConstructor //無參構(gòu)造
@AllArgsConstructor //有參構(gòu)造
六、Servlet--控制器?
socket 服務(wù)器
springmvc底層就是servlet
Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務(wù)程序或服務(wù)連接器,主要功能在于交互式地瀏覽和生成數(shù)據(jù),生成動態(tài)Web內(nèi)容。
狹義的Servlet是指Java語言實(shí)現(xiàn)的一個接口,廣義的Servlet是指任何實(shí)現(xiàn)了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運(yùn)行于支持Java的應(yīng)用服務(wù)器中。從原理上講,Servlet可以響應(yīng)任何類型的請求,但絕大多數(shù)情況下Servlet只用來擴(kuò)展基于HTTP協(xié)議的Web服務(wù)器。
servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監(jiān)聽端口,請求過來后,根據(jù)URL信息,確定要將請求交給哪個servlet去處理,然后調(diào)用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。
概括: Servlet是java后臺程序與用戶交互的機(jī)制(媒介).
核心方法:request.getParameter--parameter是參數(shù)的意思
規(guī)則:
參數(shù)是取的 而不是傳的;請求的流程: 一個request對象返回response
public String findUserByIds(HttpServletRequest request)
{String id = request.getParameter("id");
參數(shù)名稱必須相同、弊端無論什么樣的數(shù)據(jù),都是String數(shù)據(jù)類型,需要手動的轉(zhuǎn)化
SpringMVC: 在內(nèi)部封裝了Servlet機(jī)制.并且可以根據(jù)用戶的參數(shù)類型,實(shí)現(xiàn)自動的數(shù)據(jù)類型的轉(zhuǎn)化
第一階段
ASCII碼表
‘0’對應(yīng)48‘A’對應(yīng)65 ‘a(chǎn)’對應(yīng)的97單引號對應(yīng)數(shù)值,雙引號是字符串
Idea快捷鍵
ctrl+alt+L 代碼對齊
Shift +alt+↑向上移動
Ctrl+d 復(fù)制當(dāng)前行
Ctrl+y 刪除
//特征 屬性 字段 成員變量是同一個東西
//行為 功能 方法 成員方法 是同一個東西
Math常用的方法
Math.round()返回最接近參數(shù)的int,它表示"四舍五入"
Math.rint()返回最接近參數(shù)并等于某一整數(shù)的 double 值,如果有2個數(shù)同樣接近,則返回偶數(shù)的那個
Math.floor()返回最小的(最接近正無窮大)double 值,該值小于等于參數(shù),并等于某個整數(shù)
Math.ceil()返回最大的(最接近負(fù)無窮大)double 值,該值大于等于參數(shù),并等于某個整數(shù)
Math.cbrt()返回 double 值的立方根
Math.sqrt()返回正確舍入的double 值的正平方根
Math.pow()返回第一個參數(shù)的第二個參數(shù)次冪的值
Math.max()返回兩個 double 值中較大的一個
Math.min()返回兩個 double 值中較小的一個
關(guān)鍵字
String不是關(guān)鍵字
Assert--------用來查找內(nèi)部程序錯誤、
Enum---------枚舉類型
Final----------修飾一個常量 finally----try塊中總會執(zhí)行的部分
Instanceof-----測試一個對象是否是某個類的實(shí)例
Native---------由宿主系統(tǒng)實(shí)現(xiàn)的一個方法
被native關(guān)鍵字修飾的方法叫做本地方法,本地方法和其它方法不一樣,本地方法意味著和平臺有關(guān),因此使用了native的程序可移植性都不太高。另外native方法在JVM中運(yùn)行時數(shù)據(jù)區(qū)也和其它方法不一樣,它有專門的本地方法棧。native方法主要用于加載文件和動態(tài)鏈接庫,由于Java語言無法訪問操作系統(tǒng)底層信息(比如:底層硬件設(shè)備等),這時候就需要借助C語言來完成了。被native修飾的方法可以被C語言重寫。
Java程序中聲明native修飾的方法,類似于abstract修飾的方法,只有方法簽名,沒有方法實(shí)現(xiàn)。編譯該java文件,會產(chǎn)生一個.class文件。
Strictfp---------對浮點(diǎn)數(shù)計(jì)算使用嚴(yán)格的規(guī)則
Throw----------拋出一個異常throws-------一個方法可能拋出的異常
Transient-------標(biāo)志非永久性的數(shù)據(jù)
Volatile---------標(biāo)記字段可能會被多個線程同時訪問(異步),而不做同步
標(biāo)記其他線程可見,可見性和原子性,禁止指令重排序
枚舉enum
Java是一門面向?qū)ο蟮恼Z言,當(dāng)我們創(chuàng)建好一個類以后,可以創(chuàng)建這個類的多個對象
但是一個類究竟創(chuàng)建多少個對象,并且對象代表的值我們是無法限制的
所以,如果開發(fā)中需要一組值,的數(shù)據(jù)是明確的,就可以使用枚舉
如果當(dāng)我們需要定義一組常量表示不同的狀態(tài)時,就建議使用枚舉類,枚舉類的對象個數(shù)是有限且明確的
JDK5以前是需要自定義枚舉類的,JDK5以后可以使用關(guān)鍵字enum來定義枚舉類,普通類定義class 類名,枚舉類eunm 類名
枚舉類的構(gòu)造方法必須私有化,防止外界隨意創(chuàng)建本類對象,本類特有的私有屬性要加上final防止被篡改
- 定義枚舉對象:不能像自定義枚舉類對象時new,要按照下面的語法枚舉名1(值1,值2),枚舉名2(值1,值2),枚舉名3(值1,值2);
- 不需要生成toString,枚舉類繼承了java.lang.Enum * 在Enum中重寫了繼承自O(shè)bject的toString(),直接打印的就是枚舉名
- 實(shí)現(xiàn)接口的枚舉類,在枚舉類每個枚舉對象后分別實(shí)現(xiàn)接口中的抽象方法
- JDK5中擴(kuò)展了switch語句,除了可以接收byte short char int ,還可以接收枚舉類型
1、面向?qū)ο蟮奶卣饔心男?/h2>
1.封裝:
通過private修飾符來封裝屬性與方法,封裝后需要外界提供公共的操作方式get/set。封裝是把過程和數(shù)據(jù)包裹起來,外界對于數(shù)據(jù)的操作僅限于我們提供的方式。
2.繼承:
繼承是一種聯(lián)結(jié)類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。我們可以從現(xiàn)有的類中派生出一個新的類,這個過程稱為類繼承。新類繼承了原始類的特性,被稱為派生類(子類),而原始類稱為基類(父類)。派生類可以從它的基類那里繼承方法和實(shí)例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。
3.多態(tài):多態(tài)性是指可以盡量屏蔽不同類的差異性,提供通用的解決方案。多態(tài)性語言具有靈活、抽象、行為共享、代碼共享的優(yōu)勢,很好的解決了應(yīng)用程序函數(shù)同名問題。我們所使用的有:
向上造型【將子類對象統(tǒng)一看作父類型Parent p=new Child();,比如花木蘭替父從軍】
向下造型【將之前看作父類型的子類對象再重新恢復(fù)成子類對象,比如花木蘭打仗結(jié)束回家化妝】
4.抽象:抽象就是忽略一個主題中與當(dāng)前目標(biāo)無關(guān)的那些方面,以便更充分地注意與當(dāng)前目標(biāo)有關(guān)的方面。抽象并不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細(xì)節(jié)。抽象包括兩個方面,一是過程抽象,二是數(shù)據(jù)抽象。
(3) 構(gòu)造方法,與類同名,但是沒有返回值類型(連void都沒有);
一個類會默認(rèn)存在無參構(gòu)造方法(函數(shù));格式 public 名稱(){}
2、抽象類與接口的異同:
抽象類:
== 抽象類和普通類的主要有三點(diǎn)區(qū)別:==
1)抽象方法必須為public或者protected(因?yàn)槿绻麨閜rivate,則不能被子類繼承,子類便無法實(shí)現(xiàn)該方法),缺省情況下默認(rèn)為public。抽象類的存在就是為了被繼承,如果是private就沒有存在的意義了。
2)抽象類不能用來創(chuàng)建對象;--有構(gòu)造方法,為了子類
3)如果一個類繼承于一個抽象類,則子類必須實(shí)現(xiàn)父類的抽象方法。如果子類沒有實(shí)現(xiàn)父類的抽象方法,則必須將子類也定義為為abstract類。
接口:
接口中的變量默認(rèn)拼接public static final,接口中的方法必須都是抽象方法。
抽象類和接口的區(qū)別:
語法層面上的區(qū)別
1)一個類只能繼承一個抽象類,而一個類卻可以實(shí)現(xiàn)多個接口
設(shè)計(jì)層面上的區(qū)別
1)抽象類是對一種事物的抽象,即對類抽象,而接口是對行為的抽象。所以抽象類是后天構(gòu)建的結(jié)果,接口是先天設(shè)計(jì)的
2)設(shè)計(jì)層面不同,抽象類作為很多子類的父類,它是一種模板式設(shè)計(jì)。而接口是一種行為規(guī)范,它是一種輻射式設(shè)計(jì)。
2、抽象類中有構(gòu)造方法 ,為了給子類創(chuàng)建對象時調(diào)用
接口中沒有構(gòu)造方法,子類調(diào)用的是父類的(默認(rèn)object)的構(gòu)造方法
3、抽象類 單繼承
接口可以多繼承
4、抽象類可以定義普通的成員變量,抽象類中可以有普通的方法
接口只能定義靜態(tài)常量(static和final),只能有抽象方法
5、相同:抽象類和接口均不可以實(shí)例化/創(chuàng)建對象
6、抽象類是后天構(gòu)建的結(jié)果,接口是先天設(shè)計(jì)的
3.、線程有幾種狀態(tài)?它們是怎么切換的?
線程生命周期,主要有五種狀態(tài):
1、新建狀態(tài)(New) : 當(dāng)線程對象創(chuàng)建后就進(jìn)入了新建狀態(tài).如:Thread t = new MyThread();
2、就緒狀態(tài)(Runnable):當(dāng)調(diào)用線程對象的start()方法,線程即為進(jìn)入就緒狀態(tài).
處于就緒(可運(yùn)行)狀態(tài)的線程,只是說明線程已經(jīng)做好準(zhǔn)備,隨時等待CPU調(diào)度執(zhí)行,并不是執(zhí)行了t.start()此線程立即就會執(zhí)行
3、運(yùn)行狀態(tài)(Running):當(dāng)CPU調(diào)度了處于就緒狀態(tài)的線程時,此線程才是真正的執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)
就緒狀態(tài)是進(jìn)入運(yùn)行狀態(tài)的唯一入口,也就是線程想要進(jìn)入運(yùn)行狀態(tài)狀態(tài)執(zhí)行,先得處于就緒狀態(tài)
4、阻塞狀態(tài)(Blocked):處于運(yùn)狀態(tài)中的線程由于某種原因,暫時放棄對CPU的使用權(quán),停止執(zhí)行,此時進(jìn)入阻塞狀態(tài),直到其進(jìn)入就緒狀態(tài)才有機(jī)會被CPU選中再次執(zhí)行.
根據(jù)阻塞狀態(tài)產(chǎn)生的原因不同,阻塞狀態(tài)又可以細(xì)分成三種:
等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait()方法,本線程進(jìn)入到等待阻塞狀態(tài)
同步阻塞:線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其他線程占用),它會進(jìn)入同步阻塞狀態(tài)
其他阻塞:調(diào)用線程的sleep()或者join()或發(fā)出了I/O請求時,線程會進(jìn)入到阻塞狀態(tài).當(dāng)sleep()狀態(tài)超時.join()等待線程終止或者超時或者I/O處理完畢時線程重新轉(zhuǎn)入就緒狀態(tài)
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期
就緒 → 執(zhí)行:為就緒線程分配CPU 即可變?yōu)閳?zhí)行狀態(tài)"
執(zhí)行 → 就緒:正在執(zhí)行的線程由于時間片用完被剝奪CPU暫停執(zhí)行,就變?yōu)榫途w狀態(tài)
執(zhí)行 → 阻塞:由于發(fā)生某事件,使正在執(zhí)行的線程受阻,無法執(zhí)行,則由執(zhí)行變?yōu)樽枞?/p>
(例如線程正在訪問臨界資源,而資源正在被其他線程訪問)
反之,如果獲得了之前需要的資源,則由阻塞變?yōu)榫途w狀態(tài),等待分配CPU再次執(zhí)行
開啟線程:繼承thread類,重寫run方法;2.實(shí)現(xiàn)runnable接口,實(shí)現(xiàn)run方法,3.實(shí)現(xiàn)callable接口,實(shí)現(xiàn)call方法(有返回值),通過FutureTask創(chuàng)建一個線程,4.線程池
4、什么是線程池?線程池有什么優(yōu)點(diǎn)?
我們使用線程的時候就去創(chuàng)建一個線程,如果并發(fā)的線程數(shù)量很多,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時間。
線程池:其實(shí)就是一個容納多個線程的容器,其中的線程可以反復(fù)使用,省去了頻繁創(chuàng)建線程對象的操作,無需反復(fù)創(chuàng)建線程而消耗過多資源。
好處:
- 降低資源消耗,減少創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用
- 提高響應(yīng)速度,當(dāng)任務(wù)到達(dá)時,任務(wù)不需要等待線程的創(chuàng)建 立即執(zhí)行
- 提高線程的可管理性,根據(jù)系統(tǒng)的承受能力調(diào)整線程池中的數(shù)目,防止消耗過多內(nèi)存--內(nèi)存溢出
5、創(chuàng)建線程池有哪幾種方式?
兩類:通過Excutors創(chuàng)建、另一類:通過ThreadPoolExecutor創(chuàng)建--最原始的創(chuàng)建線程池方式
①. new Fixed ThreadPool(int nThreads)創(chuàng)建一個固定長度的線程池,每當(dāng)提交一個任務(wù)就創(chuàng)建一個線程,直到達(dá)到線程池的最大數(shù)量,這時線程規(guī)模將不再變化,當(dāng)線程發(fā)生未預(yù)期的錯誤而結(jié)束時,線程池會補(bǔ)充一個新的線程。
②. new Cached ThreadPool()創(chuàng)建一個可緩存的線程池,如果線程池的規(guī)模超過了處理需求,將自動回收空閑線程,而當(dāng)需求增加時,則可以自動添加新線程,線程池的規(guī)模不存在任何限制。
③. new Single ThreadExe cutor()這是一個單線程的Executor,它創(chuàng)建單個工作線程來執(zhí)行任務(wù),如果這個線程異常結(jié)束,會創(chuàng)建一個新的來替代它;它的特點(diǎn)是能確保依照任務(wù)在隊(duì)列中的順序來串行執(zhí)行。
④. new Sche duled ThreadPool(int corePoolSize)創(chuàng)建了一個固定長度的線程池,而且以延遲或定時的方式來執(zhí)行任務(wù),類似于Timer。
⑤:new SingleThread ScheduledExecutor:創(chuàng)建一個單線程的可以執(zhí)行延遲任務(wù)的線程池;
⑥:new WorkStealing Pool:創(chuàng)建一個搶占式執(zhí)行的線程池(任務(wù)執(zhí)行順序不確定)JDK 1.8 添加
⑦:ThreadPoolExecutor:最原始的創(chuàng)建線程池的方式
46.線程池的參數(shù):
core Pool Size 線程池核心線程大小 一個最小的線程數(shù)量,
maxi mum PoolSize 線程池最大線程數(shù)量 一個最大線程數(shù)量的限制
workQueue 工作隊(duì)列 新任務(wù)被提交后,會先進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時再從隊(duì)列中取出任務(wù)
46. 線程池中 submit()和 execute()方法有什么區(qū)別?
接收的參數(shù)不一樣submit有返回值,而execute沒有,submit方便Exception處理
47.怎么保證線程安全:
加鎖:jvm提供的synchoronized關(guān)鍵字鎖
jdk提供的各種鎖Lock
5、volatile關(guān)鍵字和Synchronized關(guān)鍵字
Synchronized,用來枷鎖
volatile只是保持變量的線程可見性,通常適用于一個線程寫,多個線程讀
volatile不能保證原子性,只能保證線程可見性
多個線程的時候,多個副本線程從主線程里面拿取數(shù)據(jù),副本線程的更改,只有當(dāng)提交到主線程后,才能被其他福線程感知到。volatile就是實(shí)現(xiàn)復(fù)線程更改后 馬上感知到這個變化
volatile防止指令重排序
6、Java虛擬機(jī)管理的內(nèi)存分配--5個
JVM運(yùn)行時數(shù)據(jù)區(qū)
Java虛擬機(jī)JVM 管理的內(nèi)存包括5個運(yùn)行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器,其中方法區(qū)和堆是由線程共享的數(shù)據(jù)區(qū),其他幾個是線程隔離的數(shù)據(jù)區(qū)
1、程序計(jì)數(shù)器ProgramConute
程序計(jì)數(shù)器是一塊較小的內(nèi)存,他可以看做是當(dāng)前線程所執(zhí)行的行號指示器。每一個線程都有自己的寄存器,PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的地址
分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計(jì)數(shù)器來完成。
如果線程執(zhí)行的是Java方法,這個計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;
如果正在執(zhí)行的是Native方法,這個計(jì)數(shù)器則為空
此內(nèi)存區(qū)域是唯一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemotyError(內(nèi)存溢出)情況的區(qū)域
2、Java虛擬機(jī)棧
虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于儲存局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每個方法從調(diào)用直至完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧中入棧到出棧的過程。
棧內(nèi)存就是虛擬機(jī)棧,或者說是虛擬機(jī)棧中局部變量表的部分
Java虛擬機(jī)規(guī)范對這個區(qū)域規(guī)定了兩種異常狀況:
- 如果線程請求的棧深度大于虛擬機(jī)所允許的深度,拋出StackOverflowError棧溢出異常
- 如果虛擬機(jī)擴(kuò)展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError內(nèi)存溢出異常
3、本地方法棧
本地方法棧和虛擬機(jī)棧發(fā)揮的作用是非常類似的,他們的區(qū)別是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)
本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryErroy異常
4、Java堆(Heap)
堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,是被所有線程共享的一塊內(nèi)存區(qū)域,此內(nèi)存區(qū)域的唯一目的是存放對象實(shí)例,幾乎所有的對象實(shí)例和數(shù)組都在這里分配內(nèi)存,堆中的對象內(nèi)存需要等待GC進(jìn)行回收。
Java堆細(xì)分為新生代和老年代
5、方法區(qū)(Method Area)
方法區(qū)它用于儲存已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)
跟Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外,還可以不實(shí)現(xiàn)垃圾收集。這個區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載
當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryErroy異常
6、JVM垃圾回收
Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對不同生命周期的對象使用不同的算法。(基于對對象生命周期分析)
GC的基本原理:將內(nèi)存中不再被使用的對象進(jìn)行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,Java在對對象的生命周期特征進(jìn)行分析后,按照新生代、舊生代的方式來對對象進(jìn)行收集,以盡可能的縮短GC對應(yīng)用造成的暫停
- Young(年輕代)
三個區(qū)。一個Eden區(qū),兩個Survivor區(qū)。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時,還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個),當(dāng)這個Survivor區(qū)滿時,此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū),當(dāng)這個Survivor去也滿了的時候,從第一個Survivor區(qū)復(fù)制過來的并且此時還存活的對象,將被復(fù)制年老區(qū)(Tenured)
- Tenured(年老代)
年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。
- Perm(持久代)
用于存放靜態(tài)文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時候需要設(shè)置一個比較大的持久代空間來存放這些運(yùn)行過程中新增的類。持久代大小通過-XX:Max PermSize=進(jìn)行設(shè)置。
自動GC,由jvm系統(tǒng)內(nèi)存不足的時候,自動釋放
判斷策略:1引用計(jì)數(shù)法 2可達(dá)性分析
算法:
1、復(fù)制算法:伊甸園區(qū)(判斷)→存活區(qū)(伊甸園就空了)分成S0和S1
S0滿了用復(fù)制算法→S1(二者總有一個是空的)
S1滿了用復(fù)制算法→S0(默認(rèn)是15輪,后仍然存在→老生帶區(qū))
所以新生代區(qū)和老生代區(qū)GC的頻率是不同的,內(nèi)存縮小代價高,代碼存活率太高
2、標(biāo)記清除算法:老生帶的對象,標(biāo)記后,直接刪除不復(fù)制了,但是此時區(qū)域變成了碎片(內(nèi)存碎片)》優(yōu)化算法
3、標(biāo)記整理算法,解決內(nèi)存碎片問題
7、垃圾回收機(jī)制GC
基于特定的算法,釋放垃圾對象縮占用的內(nèi)存空間,是進(jìn)軍大規(guī)模應(yīng)用開發(fā)的前提
手動GC,手動內(nèi)存分配與釋放,如果忘記釋放對應(yīng)的內(nèi)存不被使用(容易內(nèi)存泄露)System.gc
自動GC,由jvm系統(tǒng)內(nèi)存不足的時候,自動釋放
判斷策略:1計(jì)數(shù) 2可達(dá)性
算法:
1、復(fù)制算法:伊甸園區(qū)(判斷)→存活區(qū)(伊甸園就空了)分成S0和S1
S0滿了用復(fù)制算法→S1(二者總有一個是空的)
S1滿了用復(fù)制算法→S0(默認(rèn)是15輪,后仍然存在→老生帶區(qū))
所以新生代區(qū)和老生代區(qū)GC的頻率是不同的,內(nèi)存縮小代價高,代碼存活率太高
2、標(biāo)記清除算法:老生帶的對象,標(biāo)記后,直接刪除不復(fù)制了,但是此時區(qū)域變成了碎片(內(nèi)存碎片)》優(yōu)化算法
3、標(biāo)記整理算法,解決內(nèi)存碎片問題
6、談?wù)勀銓tatic的了解
static是java中的一個關(guān)鍵字,可以用來修飾方法、變量、代碼塊、內(nèi)部類,還可以使用靜態(tài)導(dǎo)包
- static方法:不依賴于對象就可以直接訪問。不能與this合用--因?yàn)樗灰栏接谌魏螌ο?#xff0c;沒有對象就談不上this了。靜態(tài)方法中只能訪問靜態(tài)變量和靜態(tài)方法。非靜態(tài)方法中可以訪問靜態(tài)方法
- static變量:靜態(tài)變量被所有對象共享,在內(nèi)存中只有一個副本,類加載時候初始化
- static代碼塊:靜態(tài)代碼塊優(yōu)化程序性能,只加載一次,靜態(tài)代碼塊》構(gòu)造代碼塊》構(gòu)造函數(shù)
- 靜態(tài)內(nèi)部類:在定義內(nèi)部類的時候加上static權(quán)限修飾符,靜態(tài)內(nèi)部類又稱為嵌套類,此時不需要外部類,就能創(chuàng)建內(nèi)部類的對象。不能從內(nèi)部類訪問外部類的非靜態(tài)資源。
*修飾方法一般寫在權(quán)限修飾符public之后
*可以修飾變量、方法、代碼塊、內(nèi)部類
*靜態(tài)資源優(yōu)先于對象進(jìn)行加載,他隨著類的加載而加載的,比對象先加載入內(nèi)存,所以在沒創(chuàng)建對象的時候,靜態(tài)資源可以通過類名直接調(diào)用。類名.變量/方法
*由于靜態(tài)比對象先加載,所以static不能與this/super共用。因?yàn)檫€沒有對象
*靜態(tài)資源被全局所有對象(多個對象)共享,屬于類資源,值只有一份
*普可調(diào)普、普可調(diào)靜、靜可調(diào)靜、靜不可調(diào)普(只能調(diào)靜)
*靜態(tài)代碼塊:static{},類里方法外;靜態(tài)代碼塊也屬于靜態(tài)資源,優(yōu)先于對象加載(創(chuàng)建對象的時候先執(zhí)行靜態(tài)代碼塊→構(gòu)造代碼塊→構(gòu)造方法),并且只加載一次;作用:用于加載需要第一時間就加載,并且只加載一次(初始化)
7、談?wù)勀銓Ψ椒ǖ睦斫?#xff1a;
方法是具有一定功能的代碼塊,可以把重復(fù)多次使用的功能提取成一個方法,減少代碼的冗余
- 格式:權(quán)限修飾符 返回值類型 方法名(參數(shù)列表){ 方法體 }
方法簽名:方法名(參數(shù)列表)
- 方法的重載和重寫:
重載:方法名相同,但是參數(shù)列表不同
重寫:兩同(方法名,參數(shù)列表相同)兩小(返回值,異常)一大(子的訪問權(quán)限控制符>=父類的)
- 方法的遞歸:在方法中調(diào)用自己本身,遞歸次數(shù)過多時,會出現(xiàn)棧溢出異常,求數(shù)字階乘--遞歸
8、內(nèi)部類有哪些種類?又有哪些使用場景?
1、成員內(nèi)部類--在類的內(nèi)部
成員內(nèi)部類可以無條件的訪問外部類的成員屬性和成員方法(包括 private 和 static 類型的成員)
使用場景:當(dāng)類 A 需要使用類 B ,同時 B 需要訪問 A 的成員/方法時,可以將 B 作為 A 的成員內(nèi)部類。同時我們可以利用 private 內(nèi)部類禁止其他類訪問該內(nèi)部類,從而做到將具體的實(shí)現(xiàn)細(xì)節(jié)完全隱藏。
2、靜態(tài)內(nèi)部類--成員內(nèi)部類被靜態(tài)修飾
不持有外部類的引用,想在靜態(tài)內(nèi)部類中訪問外部類的成員只能 new 一個外部類的對象,否則只能訪問外部類的靜態(tài)屬性和靜態(tài)方法。
使用場景:當(dāng)類 A 需要使用類 B,而 B 不需要直接訪問外部類 A 的成員變量和方法時,可以將 B 作為 A 的靜態(tài)內(nèi)部類。
3、局部內(nèi)部類--在代碼塊或方法中創(chuàng)建的類
局部內(nèi)部類的作用域只能在其所在的代碼塊或者方法內(nèi),在其它地方無法創(chuàng)建該類的對象。
我們可以把局部內(nèi)部類理解為作用域很小的成員內(nèi)部類。
使用場景:局部內(nèi)部類只用于當(dāng)前方法或者代碼塊中創(chuàng)建、使用,屬于一次性產(chǎn)品,使用場景少。
4、匿名內(nèi)部類--
1.必須繼承一個父類或?qū)崿F(xiàn)一個接口,并不需要增加額外的方法,只是對繼承方法的實(shí)現(xiàn)或是覆蓋。
2.只是為了獲得一個對象實(shí)例,并不需要知道其實(shí)際的類型。
3.匿名內(nèi)部類的匿名指的是沒有類名,而不是沒有引用指向它。
4.匿名內(nèi)部類不能有構(gòu)造方法,只能有初始化代碼塊。因?yàn)榫幾g器會幫我們生成一個構(gòu)造方法然后調(diào)用。
5.匿名內(nèi)部類中使用到的參數(shù)是需要聲明為 final 的,否則編譯器會報錯。
使用場景: 當(dāng)我們需要實(shí)現(xiàn)一個接口,但不需要持有它的引用,僅需要使用一次它的某一個資源時使用
9、談?wù)劥?、并行、并發(fā)、分布式?
串行---A和B兩個任務(wù)運(yùn)行在單核【1個CPU】線程上
并行---A和B兩個任務(wù)在同一時刻發(fā)生在多核【多個CPU】線程上
并發(fā)---多個線程在宏觀時間上看似同時執(zhí)行,但實(shí)際上是輪流穿插執(zhí)行的效果。實(shí)際上是一個cpu在若干程序間的多路復(fù)用。有串行并發(fā)、并行并發(fā)兩種
分布式---在并行處理的基礎(chǔ)上,強(qiáng)調(diào)正在執(zhí)行的物理設(shè)備處理器、內(nèi)存等 是分開的,并行是計(jì)算機(jī)上的計(jì)算,不是物理上的分開。
10、String中常用的api
s.charAt(0) //a,獲取指定下標(biāo)處的字符(元素)
s.concat("dfg") //用于拼接字符串,不會改變原串
s.indexOf("b") //判斷指定元素第一次出現(xiàn)的索引(下標(biāo))
s.lastIndexOf("b")//判斷指定元素最后一次出現(xiàn)的索引
s.substring(3)//從指定下標(biāo)處截取子串,到結(jié)束
s.substring(1,5)//截取指定范圍的子串[) 含頭不含尾
s.length()//獲取字符串的長度是字符串的方法,但length是數(shù)組的屬性無括號
s.split(" ")//regex是正則表達(dá)式(你定義的規(guī)則)按照正則表達(dá)式拆分字符串
s.toUpperCase()//全大寫
s.toLowerCase()//全小寫
s.startsWith("a")//判斷字符串是否以指定元素開頭
s.endsWith("c") //判斷字符串是否以指定元素結(jié)尾
s.trim()//去除字符串兩端多余的空格
String.valueof()//通過類型調(diào)用,不用名字,把輸入的類型轉(zhuǎn)換成String
System.out.println(String.valueOf(80)+10);//8010 因?yàn)榍懊娴?0已經(jīng)成字符串了,兩者是拼接的關(guān)系
long t1=System.currentTimeMillis();獲取系統(tǒng)當(dāng)前時間,使用的類:System
11、Regex
定義一個string類型的字符串---規(guī)則
要判斷的.matches(string正則表達(dá)式)---返回值是布爾類型
12、包裝類常用API
Integer.valueof()---byte范圍內(nèi)高效的創(chuàng)建包裝類,也可以把string轉(zhuǎn)換成Integer
Integer.parseInt()----把string類型的數(shù)據(jù)轉(zhuǎn)成 String---int
泛型
< ? >的部分,它就是泛型
1、引入泛型
泛型想要模擬數(shù)組的數(shù)據(jù)類型檢查,因?yàn)榉盒徒?jīng)常與集合一起使用,不檢查就啥都能傳進(jìn)去,引入泛型--主要目的檢查約束集合中元素的類型,可以把報錯的時機(jī)提前,在編譯器就報錯。是一顆語法糖,編譯后刪除,不會出現(xiàn)在.class源碼中。
List list=new ArrayList();//原來的集合對象,任何數(shù)據(jù)都可以
List<String> list2=new ArrayList();//引入泛型,約束成String類型
<引用類型>中必須使用基本數(shù)據(jù)類型的包裝類(引用類型)
13、多個集合(帶all)
c.addAll(c2)//把c2集合中的所有元素添加到c中,c2本身沒有改變
c.containsAll(c2)//true判斷c集合是否包含c2的所有元素
c.removeAll(c2)//刪除c中屬于c2集合的所有元素,若有重復(fù),都刪
c.retainAll(c2)//取c和c2的交集,c2不受影響,刪c
迭代(遍歷)集合
*迭代步驟:1.獲取工具--集合的迭代器c.iterator()
Iterator<Integer> it = c2.iterator();//c2定義的泛型是Integer類型
* 2.判斷集合中是否有下一個元素可以迭代 it.hasNext(),用在while循環(huán)中
* 3.獲取當(dāng)前迭代器迭代到的元素it.next()
①:System.out.println(it.next());//直接打印本次迭代到的元素
②:Integer num = it.next();//來接一下元素,在打印
System.out.println("本次跌倒到的元素"+num);
14、List接口
特點(diǎn):元素都有下標(biāo),有序的,允許存放重復(fù)的元素
list接口中獨(dú)有的方法
list.add(1,"蛇精");//在指定索引位置增加元素,并且可以存放重復(fù)的元素
System.out.println(list.indexOf("小蝴蝶"));//3 元素第一次出現(xiàn)的位置
System.out.println(list.lastIndexOf("小蝴蝶"))//8 最后出現(xiàn)的位置
System.out.println(list.remove(5))//根據(jù)下標(biāo)刪除,并打印刪除的元素
通過remove()直接刪除元素300
list.remove(Integer.valueOf(300));//把int自動封裝成Integer
System.out.println(list.get(3));//小蝴蝶 獲取指定位置的元素
System.out.println(list.set(6,"蝎子精"));
//重置,把6位置的葫蘆娃 換成了蝎子精 會打印出來6位置的元素
List<E> subList(int fromIndex,int toIndex)截取子集合,含頭不含尾
15、集合的四種迭代方式:
* 1.for循環(huán)--因?yàn)閘ist集合是有序的,元素有下邊,所以可以根據(jù)下邊來遍歷
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));//根據(jù)循環(huán)次數(shù) 獲取對應(yīng)的集合元素
}
* 2.高效for循環(huán)
for(String s:list){
System.out.println(s);//:后面是要遍歷的對象,前面是類型
}
* 3.iterator
Iterator<String> it = list.iterator();//獲取迭代器
while(it.hasNext()){//判斷集合是否仍然由下一個元素可以迭代
System.out.println(it.next());//可以直接打印
}
* 4.listIterator
listIterator屬于list接口特有的迭代器,Iterator<E>--父接口,ListIterator<E>--子接口(不常用,可以逆序迭代)
ListIterator<String> it2 = list.listIterator();
while(it2.hasNext()){
System.out.println(it2.next());
}
16、map集合的迭代
1、map的迭代(沒有迭代器,只能去找Set接口)
方式一:KeySet()
遍歷map中的數(shù)據(jù),Set<Key>:把map中所有的key取出來放入到當(dāng)前的set集合中,Key的類型放入到<>中。
①:轉(zhuǎn)Set集合Set<Integer> keySet = map.keySet();
②:獲取迭代器Iterator<Integer> it = keySet.iterator();
while(it.hasNext()){//3是否有下一個元素
//4獲取當(dāng)前循環(huán)迭代到的元素 ,每輪獲取的是map中的key
Integer key = it.next();
//5根據(jù)剛剛獲取到的key從map中獲取對應(yīng)的value map.get();
String value = map.get(key);
//6拼接打印key與value
System.out.println("{"+key+"="+value+"}");
}
方式二:entrySet()
此方案是把map中的每一對鍵值對看做是一個個的Entry<k,v> 放入set集合中
最后在獲取key和value
①:轉(zhuǎn)entry
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
②:獲取迭代器
Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();
while(it2.hasNext()){
//獲取當(dāng)前迭代的元素,這里是一個entry------
Map.Entry<Integer, String> entry = it2.next();//是一個entry
//通過entry.getKey();通過entry方法獲取
System.out.println("["+entry.getKey()+"="+entry.getValue()+"]");
}
17、HashMap
HashMap數(shù)組和鏈表的結(jié)合,數(shù)組的初始容量是16,默認(rèn)的加載因子是0.75,我們用key的hash值%初始容量,存在對應(yīng)的位置上。當(dāng)計(jì)算出來的數(shù)重復(fù),則形成鏈的結(jié)構(gòu)(>8是轉(zhuǎn)成紅黑樹,<6時在轉(zhuǎn)回鏈)。所以Map是無序的。
JDK1.8 以后在解決哈希沖突時有了較大的變化,當(dāng)鏈表長度大于閾值(或者紅黑樹的邊界值,默認(rèn)為 8)并且當(dāng)前數(shù)組的長度大于64時,此時此索引位置上的所有數(shù)據(jù)改為使用紅黑樹存儲。
HashMap的特點(diǎn):
1.存取無序的
2.鍵和值位置都可以是null,但是鍵位置只能是一個null
3.鍵位置是唯一的,底層的數(shù)據(jù)結(jié)構(gòu)控制鍵的
4.jdk1.8前數(shù)據(jù)結(jié)構(gòu)是:鏈表 + 數(shù)組 jdk1.8之后是 : 鏈表 + 數(shù)組 + 紅黑樹
5.閾值(邊界值) > 8 并且數(shù)組長度大于64,才將鏈表轉(zhuǎn)換為紅黑樹,變?yōu)榧t黑樹的目的是為了高效的查詢。
?
18、ConcurrentHashMap
--線程安全的-效率更高--采用的分段鎖
hashTable在所有的上面都加了synchronize--全局鎖--保證線程安全的
ConcurrentHashMap:如何實(shí)現(xiàn)線程安全
在JDK1.7中----分段鎖
ConcurrentHashMap采用了ReentrantLock重的+Segment+HahEntry。一個Segment中包含一個HashEntry數(shù)組,每個HashEntry又是一個鏈表結(jié)構(gòu)。--數(shù)組+鏈表的結(jié)構(gòu),外層加了Segment分段鎖。
Segment分段鎖 繼承了ReentrantLock ,key對應(yīng)的是某個Segment,value對應(yīng)的是某段,這樣就是只鎖住某一段。不影響其他段的操作。并發(fā)度:就是segment有多少個,可以同時支持多少個線程來操作--可以通過構(gòu)造函數(shù)來設(shè)計(jì)。
數(shù)組擴(kuò)容不影響其他的segment
元素的查詢:兩次hash,第一次hash是定位到哪個Segment中包含了數(shù)組,第二次hash定位到元素所在的鏈表的頭部
get方式無需加鎖,不會產(chǎn)生臟讀,因?yàn)樵乇旧硗ㄟ^volatile 保證的--一個線程寫,多個線程讀。標(biāo)記這個可能會被異步訪問。
在JDK1.8中---synchronize+CAS(樂觀鎖)+Node+紅黑樹,
查找、替換、賦值都使用CAS---保證不了線程安全 擴(kuò)容等等,所以才加的synchronize
鎖:是鏈表的head節(jié)點(diǎn),鎖的細(xì)粒度更高,擴(kuò)容時阻塞所有的讀寫操作,讀操作無鎖。
取消了Segment分段鎖的數(shù)據(jù)結(jié)構(gòu),取而代之的是Node,Node的value和next都是由volatile關(guān)鍵字進(jìn)行修飾,可以保證可見性。采用CAS+Synchronized替代Segment分段鎖。
18、反射
1、獲取字節(jié)碼對象
Class.forName(“全路徑”);//全路徑--包名.類名 可能會拋出異常
類名.class 不能alt+回車獲取返回值,這是一個屬性
New對象.getClass();
Class<?> student1 = Class.forName("cn.tedu.reflection.Student");
Class<?> student2 = Student.class;
Class<?> student3 = new Student().getClass();
通過反射我們可以獲取到//class 類的全路徑//字節(jié)碼對象全路徑名:包名.類名//獲取對應(yīng)的字節(jié)碼的類名
獲取構(gòu)造方法/獲取成員方法//獲取成員變量
暴力反射:
通過反射的方式創(chuàng)建對象
Object obj = clazz.newInstance();
//4.2!!!暴力反射!!需要設(shè)置權(quán)限私有可見
field.setAccessible(true);
19、設(shè)計(jì)單例模式
1、單例設(shè)計(jì)方式一:餓漢式,自己吧對象都創(chuàng)建好了
//0.創(chuàng)建自己的單例程序(類)
class MySingle{
//1.構(gòu)造方法私有化,不讓外界隨意調(diào)用本方法創(chuàng)建對象(實(shí)例化)
private MySingle(){}
//2.創(chuàng)建本類對象,并私有化
private static MySingle sin=new MySingle();
//3給外界提供一個公共的全局訪問點(diǎn)(類似get和set方法)。把創(chuàng)建好的對象返回,返回值類型是MySingle
public static MySingle getSin(){
return sin;}//4.將本類中剛創(chuàng)建好的對象通過return返回到調(diào)用位置
// 5.get方法設(shè)置為靜態(tài),直接通過類名調(diào)用,不用創(chuàng)建對象;靜態(tài)方法只能調(diào)靜態(tài),所以創(chuàng)建出來的對象new MySingle()也需要改成靜態(tài)的
MySingle sin1 = MySingle.getSin();
MySingle sin2 = MySingle.getSin();
//都只是執(zhí)行了類中自己創(chuàng)建的那一個對象(單例),地址值是一樣的
sin1==sin2
二、單例設(shè)計(jì)方式二:懶漢式
先不創(chuàng)建對象,等你需要才創(chuàng)建--延遲加載
//1.構(gòu)造方法私有化
//2.在類的內(nèi)部創(chuàng)建引用類型變量成員變量(延遲加載的思想)---注意私有化
private static MySingle2(引用類型類名) sin2;
//3.創(chuàng)建一個公共的方法供外界調(diào)用,用來獲取本類唯一的方法
public static MySingle2 getSin2(){
//3.1在返回對象之前,需要判斷sin2是否保存有地址值
if (sin2==null){//如果是默認(rèn)值null,說明之前從來沒有創(chuàng)建過本類對象
//3.2創(chuàng)建一個對象并賦給sin2
sin2=new MySingle2();
}//3.3如果sin2不為空(之前創(chuàng)建過對象)就跳過if,直接返回sin2
return sin2;
//4.1方法設(shè)置成靜態(tài)
//4.2成員變量設(shè)置成靜態(tài)
//5.獲取本類對象
MySingle2 s1 = MySingle2.getSin2(); 類名.方法名;
20、JDK自帶注解(5個)
@Override:標(biāo)識重寫方法
@SuppressWarnings(“deprecation”) 忽略警告
·元注解(5個),用來定義其他注解 的注解
@Target 注解用在哪里:類上、方法上、屬性上等等,可以使用的位置
@Retention 注解的生命周期:源文件中、字節(jié)碼文件中、運(yùn)行中
@interface注解名--自定義注解之前要用上兩個元注解表示作用位置和生命周期
第二階段
1、表的操作
創(chuàng)建表:
先使用數(shù)據(jù)庫 use 數(shù)據(jù)庫名
create table 表名(字段1名字 字段類型(字段最大長度),字段2,字段3)
id int primary key auto_increment, 可以寫id int (5),
door_name varchar(100), 下劃線分隔
tel varchar(50)
);
查看表的結(jié)構(gòu):desc 表名;--展示表的字段名和數(shù)據(jù)類型
添加表的列(字段):alter table 表名 add column money numeric(7,2);
2、SQL語句
結(jié)構(gòu)化查詢語言(Structured Query Language),專門用來操作數(shù)據(jù)庫的語言
- 對數(shù)據(jù)(記錄)的操作
查詢表中的數(shù)據(jù):select*from表名;*代表通配符,表示查所有
添加表中的數(shù)據(jù):insert into 表名values(null,'varchar的數(shù)據(jù)',666);
修改表中的數(shù)據(jù):update 表名 set 字段名= 555 where id=1;
刪除表中的數(shù)據(jù):Delete from 表名 where id=2;
- 基礎(chǔ)函數(shù)
Lower、Upper、Length、Substr截取、Concat拼接、Replace替換、Ifnull、Round/ceil/floor對小數(shù)的特殊處理、now、Year/Month/day
- 條件查詢
distinct(去重)、where、and/or/in 、like、null/is not null、between and、limit、order by desc降序asc升序
limit 1,3; 從下標(biāo)1(第二行)開始記錄3行
select * from emp limit 2; 從頭開始展示2行
orderby和limit搭配使用--可以用來查詢最大,最小,第幾大
若不用order by來排序
where salaries.salary=( select max(s2.salary) from salaries s2
where s2.salary< (SELECT max(salary) from salaries));兩次max()
- 聚合函數(shù)
max(),min(),sum(),avg(),count(),count(1),group By
count(列名)--只統(tǒng)計(jì)非空的數(shù)列,非聚合列和聚合列不能一起查,只能分組。
Having跟where 一樣,但是用在group by之后。不能再從表中讀取數(shù)據(jù)了,數(shù)據(jù)的查詢都是在from 表名之前進(jìn)行的。所以having后面跟的條件一定是之前select過的。
Group by 非聚合列 having 過濾條件;
Where 用在分組之前更高效,但是where不能用在聚合函數(shù)之中。
3、字段約束
主鍵約束“primary key”、唯一約束“unique”、非空約束“not null”、外鍵約束“foreign key”、默認(rèn)值約束“Default”,檢查約束(年齡大于0,小于200)
4、創(chuàng)建索引index
索引是一種排好序的快速查找的數(shù)據(jù)結(jié)構(gòu),它幫助數(shù)據(jù)庫高效的進(jìn)行數(shù)據(jù)的檢索。設(shè)計(jì)索引通常都是背后操作。但是索引本身就是龐大的表--采用BTree樹方式構(gòu)建。
分類: 單值索引:一個索引只包含一個列(字段),
唯一索引:索引列的值必須唯一,允許有空值;主鍵會自動創(chuàng)建唯一索引
復(fù)合索引:一個索引包含多個列
創(chuàng)建索引:create index 索引名字 on 表名(字段名)
創(chuàng)建唯一索引:create unique index 索引名 on 表名(字段名);
查看索引 show index from 表名;
刪除索引:alter table 表名 drop index 索引名;
創(chuàng)建復(fù)合索引:最左特性(應(yīng)用索引時候的查詢條件必須按照從左到右的順序),否則索引失效
模糊查詢導(dǎo)致索引失效:
5、多表聯(lián)查
1、笛卡爾積Cartesian product又稱直積
SELECT * FROM dept,emp where dept . deptno=emp.deptno;直接先查兩個表--慎用
2、連接查詢
· 內(nèi)連接 inner join 兩個的交集。
· 左(外)連接 left join 左表所有的和右表滿足條件的,不滿足的是null,以左邊表為基礎(chǔ),
· 右(外)連接 right join 右表的所有和左表滿足的,左表不滿足的是null,
3、子查詢subquery
也叫嵌套查詢(兩個select)效率非常低,把上次查詢的結(jié)果當(dāng)做第二次查詢的條件
6、sql優(yōu)化
1、查詢SQL盡量不要使用select *,而是具體字段,盡量避免返回大量數(shù)據(jù)
2、在where子句中不使用or來連接條件 盡量用and
使用varchar代替char
不要使用in和not in,用between和exists代替
3、盡量使用數(shù)值替代字符串類型 --0表示女 1 表示男 數(shù)字內(nèi)存小,還有讀寫性能
4、給常用來查詢條件的字段設(shè)計(jì)索引,但是索引控制在5個以內(nèi),不適合建在有大量重復(fù)數(shù)據(jù)的字段上
##使用explain分析你SQL執(zhí)行計(jì)劃,分析性能是否使用了索引
創(chuàng)建name字段的索引 where name=””; name索引才生效
5、優(yōu)化like語句,盡量避免%號在前面,但是like很可能讓你的索引失效--從開始位置比較高效
6、where限定查詢的數(shù)據(jù),避免在where子句中使用!=或<>操作符,在等號左側(cè)進(jìn)行表達(dá)式、函數(shù)操作
7、INSERT INTO dept (deptno,dname) VALUES(4,"abcs");#給指定的列插入值,
INSERT INTO student (id,NAME) VALUES(4,'齊雷'),(5,'劉昱江');批量提交
8、偽刪除設(shè)計(jì)
商品狀態(tài)(state):1-上架、2-下架、3-刪除
通過where state=1或者where state=2過濾掉數(shù)據(jù),這樣偽刪除的數(shù)據(jù)用戶就看不到了,從而不影響用戶的使用,操作速度快,特別數(shù)據(jù)量很大情況下
9、 count 聚合函數(shù)
沒有主建: 只有一列count(*)多列count(1)
有主鍵 count(主鍵)
所以實(shí)際業(yè)務(wù)中一般用count(1)比較普遍,但是如果需要聚合多個列,則用count(列名)比較合適。
7、HTML--超文本標(biāo)記語言
1、列表標(biāo)簽<ul type=”circle”> <li> 有序列表 ol -li(自動編號) /無序列表 ul -li
2、<input type="radio"/>男 單選框
<input type="number" /> 數(shù)字值
<input type="week" /> 日歷
<input type="checkbox" />楊冪 復(fù)選框
3、表格標(biāo)簽 table <table>表里 <tr> 行 里包含 <td> 列
參數(shù)border=邊框 cellspacing=間距 bgcolor="顏色" width="寬度" align="center"
5、下拉框:<select> <option>aaa</option></select>
6、文字框:<textarea >請輸入描述信息</textarea>
7、添加音頻<audio controls="controls"> 添加音頻的控制屬性
<source src="位置"></source></audio>
8、CSS-使用標(biāo)簽<style>--層疊樣式表
CSS語法:元素的選擇器{ 修飾的具體樣式 屬性名:屬性值;}
CSS使用的位置: <style> CSS代碼 </style>標(biāo)簽
- 行內(nèi)CSS(給正在用的標(biāo)簽,添加style的屬性) <div style="text-align: center;">大家好</div>
- 內(nèi)部CSS(使用html提供的<style>標(biāo)簽,把css代碼包裹起來) <head>里加<style> 選擇器
- 外部CSS(在網(wǎng)頁里,引入一個外部CSS文件)
選擇器:簡單選擇器(標(biāo)簽名,id,類class選擇器)分組選擇器 屬性選擇器--選擇好了,可以設(shè)置我們特殊的樣式了
CSS選擇器
1、簡單選擇器
- 標(biāo)簽名選擇器 自己給標(biāo)簽起個名字,選擇的太多
- 類Class選擇器 ①:加class屬性, 精確的選擇 ②:點(diǎn)class的值{ } <span class="a b"> 如果a 和b中有相同的設(shè)置,b會覆蓋a的
- Id選擇器:①:按照id屬性的值(唯一) ②:#id的值 ---更高效 唯一
1、分組選擇器
將多個選擇器選中的元素組合在一起,用,號隔開 div,span{
2、屬性選擇器
通過標(biāo)簽的屬性選中元素input [ type='text' ]{ } //a [href="!"]{} //a[href] 不寫值也可
3、引用外部的CSS,在同一級目錄
<link href="yonghuzuce.css" rel="stylesheet"/> 可以直接寫名字 stylesheet:樣式表
9、JS--使用標(biāo)簽<script>
1、JS概述
JavaScript 是 web 前端開發(fā)者必學(xué)的三種語言之一:
· HTML 定義網(wǎng)頁的內(nèi)容 H5
· CSS 規(guī)定網(wǎng)頁的布局 CSS3
· JavaScript 實(shí)現(xiàn)網(wǎng)站的交互--人機(jī)互動--動態(tài) ES6
JS是一門 基于對象 和 事件驅(qū)動 的 腳本語言 ,通常用來提高網(wǎng)頁與用戶的交互性。
2、名詞解釋
基于對象:它不僅可以創(chuàng)建對象,也能使用現(xiàn)有的對象。JS沒有類的概念,也沒有編譯的過程。是一邊解釋一邊執(zhí)行。
事件驅(qū)動:在JS中,大部分情況下都是通過事件觸發(fā)驅(qū)動函數(shù)執(zhí)行的,從而實(shí)現(xiàn)特定的功能。(比如點(diǎn)擊div將內(nèi)容替換為時間、當(dāng)鼠標(biāo)滑過元素,元素就有翻轉(zhuǎn)的動態(tài)。)
腳本語言:在網(wǎng)絡(luò)前端開發(fā)環(huán)境下,用于嵌入在客戶端瀏覽器中的一段小程序。
3、特點(diǎn)和優(yōu)勢
特點(diǎn):
(1)JS是一門直譯式的語言,直接執(zhí)行的就是源代碼.
是一邊解釋一邊執(zhí)行,沒有編譯的過程(不像Java需要提前編譯為class文件再運(yùn)行).
(2) JS是一門弱類型的語言,沒有嚴(yán)格的數(shù)據(jù)類型.
優(yōu)勢:
(1)良好的交互性
(2)一定的安全性(JS被強(qiáng)制的要求,不能訪問瀏覽器以外的東西,只能訪問瀏覽器和瀏覽器內(nèi)部的資源)
(3)跨平臺性(Java語言具有跨平臺性,是因?yàn)橛刑摂M機(jī))
只要有瀏覽器的地方都能執(zhí)行JS
4、引入JS代碼
1、行內(nèi)JS
<div οnclick="alert(100)">我是div</div> onclick點(diǎn)擊事件 alert 是彈出窗口的
<div οndblclick=--雙擊
<div οnmοuseenter=--鼠標(biāo)移入
2、內(nèi)部JS 在<head>里 <script>alert('嘟嘟嘟嘟~~~')</script> //執(zhí)行代碼
3、外部js <script src="1.js"></script>//引入外部的js文件
不要同時通過一個script標(biāo)簽引入JS代碼和JS文件,會導(dǎo)致代碼不會執(zhí)行
5、js的數(shù)據(jù)類型
數(shù)據(jù)類型包括:number / string / boolean / null / undefined
數(shù)字類型運(yùn)算自動轉(zhuǎn)換類型 alert(2.4+3.6) 直接輸出6不是6.0
字符串類型中用單引號和雙引號均可
6、js的變量--常量
var 定義變量,沒有嚴(yán)格的區(qū)分變量類型
alter(a==b);//比較的是值
alter(a===b);//比較值和類型
const常量:const定義常量且必須初始化
typeof運(yùn)算符: 用于返回變量或者表達(dá)式 的數(shù)據(jù)類型
JS數(shù)組:單個的變量中存儲多個值(個容器)。例如:數(shù)值、字符串、布爾值、undefined、null、對象、函數(shù)等
JS函數(shù):類似java中的方法
方式一:function 聲明函數(shù)
聲明:function 函數(shù)名稱( 參數(shù)列表 ){ 函數(shù)體 } 沒有用變量來接受創(chuàng)建好的函數(shù)
調(diào)用: 函數(shù)名稱( 參數(shù)列表 );
function add(a , b){//定義含參函數(shù) alert(x+y); }//不用寫變量類型
Add(1,2);//調(diào)用add方法
方式二:var 函數(shù)名稱 = function (x,y)-----這種注意不能先調(diào)用。因?yàn)楹竺娼o名字
JS對象:window對象可以提供alert/confrm/prompt輸入框/document
string對象
number對象
自定義對象:
1、方式一;
聲明對象:function Person(){}-----P要大寫
------------------用創(chuàng)建出的p對象 更改Person對象---------------------
創(chuàng)建對象:var p1 = new Person();
設(shè)置屬性:p1.name = "張飛"; p1.age = 18;-----創(chuàng)建出來的對象 直接添加值
設(shè)置方法:p1.run = function( ){ }-----在Person中添加run函數(shù)
訪問p1對象:
它的屬性可以邊寫邊創(chuàng)建
2、方式二:
定義對象的名稱 var p2={
“變量名”:values, "pname":"張三",
"page":20,
綁定函數(shù) "psay":function(){
console.log(this.pname+this.page);
}}
打印對象 console.log(p2);----{pname: '張三', page: 20, psay: ?}
調(diào)用對象的方法 p2.psay();
10、document對象
1、獲取方式window.document;先寫一句這個
2、調(diào)用方法 document..getElementById("id屬性的值")--返回1個元素,根據(jù)id找
Document..getElementsByName("name屬性的值")--返回多個元素(用數(shù)組)
getElementsByClassName("class屬性的值")--返回多個元素(用數(shù)組)
11、JSON--JavaScript 對象表示法
JavaScript Object Notation
1、JSON數(shù)據(jù):本身是字符串
Var a=’”fristname”:”josn”’ ---外層有引號 內(nèi)層的key和values都要有引號
不同于創(chuàng)建對象時候key的引號可以省略
2、JSON對象---多個屬性
var a = '{ "firstName":"John" , "lastName":"Doe" }'
3、JSON 數(shù)組:
var a = '[ { "firstName":"Bill" , "lastName":"Gates" },{ "firstName":"George" , "lastName":"Bush" } ] ';
給服務(wù)器發(fā)送數(shù)據(jù): 將JS對象轉(zhuǎn)成JSON字符串 JSON.stringify(Js對象)
接受服務(wù)器的數(shù)據(jù): JSON字符串轉(zhuǎn)成JS對象 JSON.parse("json字符串")
轉(zhuǎn)換成js對象后,直接 .name 屬性就能獲取name的值
var js=JSON.parse(a); console.log(js.name);
轉(zhuǎn)成字符串后 就可以直接對字符串操作,拼接、求長度等
12、vue
· 一個輕量級的mvvm框架,雙向綁定,數(shù)據(jù)動態(tài)更新,gzip后大小只有20k+
· 是一個漸進(jìn)式框架,其核心思想是數(shù)據(jù)驅(qū)動、組件化的前端開發(fā)
· 原生html頁面是通過js 操作的是dom,而vue.js操作的是數(shù)據(jù)。
3、使用vue準(zhǔn)備數(shù)據(jù) <script>標(biāo)簽里面創(chuàng)建vue對象
<script>new Vue({ vue的兩個屬性,用{}包起來,里面寫的是js對象})</script>
el 屬性 掛載點(diǎn) element的簡稱,即將把準(zhǔn)備好的數(shù)據(jù) 渲染到指定區(qū)域
data屬性 準(zhǔn)備要顯示的數(shù)據(jù),傳遞數(shù)據(jù)
第三階段
一、linux命令--在Mobaxter
1.1 cd命令集
ifconfig/ip addr 檢查IP地址
pwd 檢查當(dāng)前的位置
tab鍵 自動補(bǔ)齊(注意唯一性)
cd命令是linux中最基本的命令語句,必須熟練掌握
cd / 返回根目錄
cd ~ 用戶主目錄
cd . 當(dāng)前目錄
cd ..返回到上一級目錄
cd /usr/ 進(jìn)入到usr目錄
cd – 返回上一個目錄
cd 直接回家
1.2 ls目錄和文件
ls –l 詳細(xì)格式,文件權(quán)限,時間
ll 和ls –l作用相同
ls *.txt 查看所有的txt類型文檔
1.3 目錄操作
mkdir 創(chuàng)建目錄
mkdir a 創(chuàng)建 a目錄
mkdir a/aa/aa 創(chuàng)建多級目錄
mkdir -p a/b 創(chuàng)建 a目錄,并在a目錄里創(chuàng)建b目錄
mkdir -m 777 c 創(chuàng)建一個權(quán)限為777的C目錄
rmdir 刪除目錄(如果目錄里有文件,則不能用此命令)
1.4 Vi/vim創(chuàng)建/查看/編輯文件
命令行:Esc切換到命令行模式。
編輯模式:先按一下切換模式
按i,在光標(biāo)前開始編輯
按a,在光標(biāo)后開始編輯
按o,在當(dāng)前行的下一行開始編輯
按u, 撤銷之前的操作---基本上是撤銷一行
底行模式:按 shift+:冒號。
:q! 不保存退出
:wq 保存退出
:/world 從當(dāng)前光標(biāo)處,向上查找world關(guān)鍵字
:?world 從當(dāng)前光標(biāo)處,向后查找world關(guān)鍵字
1.5 刪除文件
rm 刪除文件
rm n.txt 提示y刪除n放棄
rm –f n.txt 不提示
rm –rf dirname 不提示遞歸刪除目錄下所以內(nèi)容
rm –rf * 刪除所有文件
rm –rf /* 刪除所有子目錄所有和文件
1.6 復(fù)制和移動文件
cp復(fù)制文件
cp nginx.conf n.txt
cp –R tomcat1 tomcat2 #復(fù)制整個目錄
mv 修改文件名,移動文件
mv n.txt m.txt 修改文件名稱
1.7瀏覽文件
cat 輸出文件所有的內(nèi)容
more 輸出文檔所有的內(nèi)容,分頁輸出,空格瀏覽下一屏,q退出
less 用法和more相同,只是通過PgUp、PgOn鍵來控制
tail 用于顯示文件后幾號,使用頻繁
tail -10 nginx.conf 查看nginx.conf的最后10行
tail –f nginx.conf 動態(tài)查看日志,方便查看日志新增的信息
ctrl+c 結(jié)束查看
1.8 打包命令
tar命令位于/bin目錄下,它能夠?qū)⒂脩羲付ǖ奈募蚰夸洿虬梢粋€文件,但不做壓縮。一般Linux上常用的壓縮方式是選用tar將許多文件打包成一個文件,再以gzip壓縮命令壓縮成name.tar.gz的文件。
-c 創(chuàng)建一個新的tar文件
-v 顯示運(yùn)行過程的信息
-f 指定文件名
-z 調(diào)用gzip壓縮命令進(jìn)行壓縮
-t 查看壓縮文件的內(nèi)容
-x 解開tar文件
tar –cvf n.tar ./* 壓縮當(dāng)前目錄下的所有文件和目錄,文件名為n.tar
tar –xvf n.tar 解壓壓縮包中的文件到當(dāng)前目錄(如果長時間未解壓成功 Ctrl+C推出)
tar –cvzf m.tar.gz ./* 壓縮文件
tar -zxvf m.tar.gz 解壓m.tar文件到當(dāng)前目錄
1.9grep命令
grep root /etc/passwd 在文件中查找關(guān)鍵字root
grep root /etc/passwd –-color 高亮顯示
grep root /etc/passwd –A5 –B5 高亮顯示,A后5行,B前5行
grep -n root /etc/passwd 查找并顯示行數(shù)
grep -v root /etc/passwd 取反,查出不含root的數(shù)據(jù)
第四階段-----微服務(wù)
知識基礎(chǔ):
1、RestTemplate--客戶端
Spring RestTemplate 是 Spring 提供的用于訪問 Rest 服務(wù)的客戶端,RestTemplate 提供了多種便捷訪問遠(yuǎn)程Http服務(wù)的方法,第三方服務(wù)商都是使用 RestTemplate 請求 restful 服務(wù)。
@Autowired//從spring容器獲取一個RestTemplate對象,基于此對象實(shí)現(xiàn)遠(yuǎn)端服務(wù)
private RestTemplate restTemplate;
public String doRestEcho1(){
//1、定義要調(diào)用的遠(yuǎn)端服務(wù)的url
String url="http://localhost:8081/provider/echo/"+server;
//2、基于restTemplate對象中的相關(guān)方法進(jìn)行服務(wù)調(diào)用
return restTemplate.getForObject(url,String.class );
}
服務(wù)消費(fèi)方是如何調(diào)用服務(wù)提供方的服務(wù)的?(RestTemplate)--客戶端
服務(wù)之間進(jìn)行服務(wù)調(diào)用時,使用了什么API?(RestTemplate)
2、Socket/ServerSocket對象
* 1.網(wǎng)絡(luò)服務(wù)端(ServerSocket) ---用tomcat實(shí)現(xiàn)
* 2.網(wǎng)絡(luò)客戶端(Socket)--用Browser
//網(wǎng)絡(luò)中計(jì)算機(jī)的唯一標(biāo)識是:ip
//計(jì)算機(jī)中應(yīng)用程序的唯一標(biāo)識:端口port
3、讀寫鎖--ReadWriteLock
寫鎖--悲觀寫,自能自己寫,別人不能寫
讀鎖--樂觀讀,拿讀鎖后還能被別人讀
創(chuàng)建讀寫鎖ReentrantReadWriteLock
4、過濾器Filter
過濾器鏈:FilterChain
一般和servlet控制器 (請求控制邏輯) 組合使用,控制請求的分發(fā)等等
5、Servlet控制器
Servlet是java后臺程序與用戶交互的機(jī)制(媒介).
Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務(wù)程序或服務(wù)連接器,主要功能在于交互式地瀏覽和生成數(shù)據(jù),生成動態(tài)Web內(nèi)容。
狹義的Servlet是指Java語言實(shí)現(xiàn)的一個接口,廣義的Servlet是指任何實(shí)現(xiàn)了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。Servlet運(yùn)行于支持Java的應(yīng)用服務(wù)器中。從原理上講,Servlet可以響應(yīng)任何類型的請求,但絕大多數(shù)情況下Servlet只用來擴(kuò)展基于HTTP協(xié)議的Web服務(wù)器。
servlet不會直接和客戶端打交道!那請求怎么來到servlet呢?答案是servlet容器,比如我們最常用的Tomcat,Tomcat才是與客戶直接打交道的家伙,它監(jiān)聽端口,請求過來后,根據(jù)URL信息,確定要將請求交給哪個servlet去處理,然后調(diào)用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個response返回給客戶端。
核心方法:request.getParameter--parameter是參數(shù)的意思,參數(shù)是自己取的,根據(jù)request對象取值
public String findUserByIds(HttpServletRequest request)
{String id = request.getParameter("id"); //自己從request對象中取出信息
參數(shù)名稱必須相同、弊端無論什么樣的數(shù)據(jù),都是String數(shù)據(jù)類型,需要手動的轉(zhuǎn)化
SpringMVC: 在內(nèi)部封裝了Servlet機(jī)制.并且可以根據(jù)用戶的參數(shù)類型,實(shí)現(xiàn)自動的數(shù)據(jù)類型的轉(zhuǎn)化
6、tomcat處理請求流程
tomcat 處理客戶端的請求時的一個簡易流程分析
關(guān)鍵組件:
服務(wù)注冊 與發(fā)現(xiàn)---Nacos 服務(wù)的配置--配置中心
負(fù)載均衡-Ribbon
遠(yuǎn)程服務(wù)調(diào)用-Feign
限流降級--Sentinel
訪問入口管理--網(wǎng)關(guān)GateWay
分布式事務(wù)管理
一、Nacos注冊中心
1.1應(yīng)用背景、配置
不停機(jī)就可以動態(tài)刷新服務(wù)內(nèi)部的配置項(xiàng)--調(diào)整日志級別
例如,在生產(chǎn)環(huán)境上日志級別error ,系統(tǒng)出問題對它 debug 的時候,日志級別調(diào)整為 debug 級別。
服務(wù)注冊與發(fā)現(xiàn),一般需要添加的依賴有兩個spring-cloud-starter-alibaba-nacos-discovery/config--這是配置中心的
- 實(shí)現(xiàn)Nacos服務(wù)注冊需要添加什么依賴?(兩個spring-boot-starter-web,spring-cloud-starter-alibaba-nacos-discovery)
- 實(shí)現(xiàn)Nacos服務(wù)注冊時,必須做哪些配置?(服務(wù)名,假如是本機(jī)服務(wù)注冊可以省略服務(wù)地址)
- Nacos如何檢查服務(wù)狀態(tài)?(通過心跳包實(shí)現(xiàn))
- 服務(wù)之間進(jìn)行服務(wù)調(diào)用時,使用了什么API?(RestTemplate)
要想下載的nacos能夠管理對應(yīng)的數(shù)據(jù)庫表--找到nacos文件的conf(配置)中的application.properties,更改里面的表名,數(shù)據(jù)庫連接密碼等。
啟動Nacos:在bin目錄下打開cmd輸入:startup.cmd -m standalone
訪問Nacos:運(yùn)行成功后會顯示訪問端口號和路徑http://localhost:8848/nacos
- 什么是配置中心?(存儲項(xiàng)目配置信息的一個服務(wù))
- 為什么要使用配置中心?(集中管理配置信息,動態(tài)發(fā)布配置信息)
- 配置中心一般都會配置什么內(nèi)容?(可能會經(jīng)常變化的配置信息,例如連接池,日志、線程池、限流熔斷規(guī)則)
- 什么信息一般不會寫到配置中心?(服務(wù)端口,服務(wù)名,服務(wù)的注冊地址,配置中心)
- 市場上有哪些主流的配置中心?(Apollo,nacos,Spring Cloud全家桶……)
1.2nacos動態(tài)刷新
我們系統(tǒng)在瀏覽器中能看到日志級別的變化
* 配置中心的level--日志級別 發(fā)生了變化,但屬性的值不變:屬性的值是對象初始化的時候讀取的 *
@RefreshScope--在controller類上添加注解--創(chuàng)建監(jiān)聽器--監(jiān)聽配置中心,只要配置中心發(fā)生變化,就重新創(chuàng)建對象
日志級別:error>warn>info>debug>trace
當(dāng)我們后臺設(shè)計(jì)的日志級別是info,trace/debug 等級別小的能在后臺打印,warn/error比info大的不打印
- 你項(xiàng)目中使用的日志規(guī)范是什么?(SLF4J)
- 項(xiàng)目中使用的日志API是什么:(org:slf4j.Logger)
- Nacos配置中心宕機(jī)了,我們的服務(wù)還可以讀取到配置信息嗎?(可以從內(nèi)存,客戶端獲取了配置中心的配置信息以后,會將配置信息在本地內(nèi)存中存儲一份.)
- 微服務(wù)應(yīng)用中我們的客戶端如何獲取配置中心的信息?(我們的服務(wù)一般首先會從內(nèi)存讀取配置信息,同時我們的微服務(wù)還可以定時向nacos配置中心發(fā)請求拉取(pull)更新的配置信息)
- 微服務(wù)應(yīng)用中客戶端如何感知配置中心數(shù)據(jù)變化?(當(dāng)數(shù)據(jù)發(fā)生變化時,nacos找到它維護(hù)的客戶端,然后通知客戶端去獲取更新的數(shù)據(jù),客戶端獲取數(shù)據(jù)以后更新本地內(nèi)存,并在下次訪問資源時,刷新@Value注解描述的屬性值,但是需要借助@RefreshScope注解對屬性所在的類進(jìn)行描述)(nacos客戶端會基于長輪詢機(jī)制,從nacos獲取配置信息)
- 長輪詢:如果沒有配置更新時 就在nacos服務(wù)端的對列 等待 短輪詢:如果沒有就離開
1.3定時任務(wù)Timer/ScheduledExecuteService
- 基于Timer對象實(shí)現(xiàn)定時任務(wù)調(diào)度(單線程有順序的任務(wù)調(diào)度)---最大缺陷是多個任務(wù)不能并發(fā)執(zhí)行
//Timer對象創(chuàng)建時會創(chuàng)建一個線程(單線程用Timer),并未線程分配一個對列(多任務(wù)排隊(duì))
Timer timer=new Timer();
//構(gòu)建任務(wù)對象 TimerTask 是接口類型
TimerTask task1=new TimerTask()
//3.定時執(zhí)行任務(wù)
timer.schedule(task1,1000,1000);
- 基于ScheduledExecuteService實(shí)現(xiàn)多線程任務(wù)調(diào)度(Nacos中定時心跳,配置長輪詢都基于此對象)
//構(gòu)建一個負(fù)責(zé)任務(wù)調(diào)度的線程池對象,池中最多有5個核心線程
ScheduledExecutorService ses=Executors.newScheduledThreadPool(5);
//構(gòu)建任務(wù)對象
Runnable task=new Runnable() {//重寫里面的方法--要執(zhí)行的代碼
//執(zhí)行任務(wù)對象(定時任務(wù)調(diào)度)
ses.scheduleWithFixedDelay(task, initialDelay:1,//初始延遲 delay:1, //每隔一秒執(zhí)行一次(與任務(wù)是否結(jié)束無關(guān))TimeUnit.SECONDS);//每秒刷新一次
2、基于雙重校驗(yàn)機(jī)制,實(shí)現(xiàn)類的單實(shí)例設(shè)計(jì)--懶漢式
1.4多線程從數(shù)據(jù)庫獲取數(shù)據(jù)
問題1:數(shù)據(jù)緩存問題
問題2:多線程安全問題(不能取null值),和并發(fā)問題
安全問題:
list集合用 CopyOnWriteArrayList<>()
Vector<>是悲觀鎖,別人不允許更新,值允許一個人更新
CopyOnWriteArrayList 是一個線程安全的list集合,允許多個線程并發(fā)更新,但是只能有一個更新成功
并發(fā)問題:
雙重校驗(yàn)+加鎖
1.5共享配置文件--點(diǎn)單登錄
在同一個namespace的工作空間中,提取相同的配置,存儲到指定配置文件
單點(diǎn)登錄:商城中不同的模塊,不需要多次登錄,相當(dāng)于登錄的通行證(加密的)--需要校驗(yàn)--秘鑰
應(yīng)用jwt.io技術(shù)網(wǎng)站,生產(chǎn)令牌,包含用戶信息--后期需要解密
日志的級別、秘鑰token、可以當(dāng)做共享配置
- 項(xiàng)目中為什么使用org.slf4j.Logger對象記錄日志?此對象是日志規(guī)范,對外面的門面,可以更好降低耦合
- @Slf4j注解的作用是什么?(此注解屬于lombok,用于描述類,在類中創(chuàng)建org.slf4j.Logger對象)
- Nacos配置中心依賴對項(xiàng)目中配置文件的名字有要求嗎?(bootstrap.yml)
- Nacos中@RefreshScope注解的應(yīng)用場景以及作用.:動態(tài)創(chuàng)建對象,當(dāng)數(shù)據(jù)更新時就創(chuàng)建一次對象
二、負(fù)載均衡--Ribbon
2.1LoadBalancerClient應(yīng)用
client--客戶端、用戶端、客戶、委托人
LoadBalancerClient對象可以從nacos中基于服務(wù)名獲取服務(wù)實(shí)例,在工程中基于算法實(shí)現(xiàn)負(fù)載均衡方式的調(diào)用。spring幫我們創(chuàng)建好了LoadBalancerClient對象,直接注入就可以了,
loadBalancerClient.choose("sca-provider");//choose綁定負(fù)載均衡地址 serviceId--服務(wù)的id
2.2Ribbon負(fù)載均衡--優(yōu)化
Netfilx公司進(jìn)行維護(hù)的
在啟動類中創(chuàng)建RestTemplate對象,使用@loadBalanced注解描述RestTemplate對象時,假如發(fā)起遠(yuǎn)程服務(wù)調(diào)用,底層會對這個請求進(jìn)行攔截,攔截到此請求后會基于LoadBalancerClient對象獲取服務(wù)實(shí)例,然后進(jìn)行負(fù)載均衡方式的調(diào)用。簡化了loadBalancerClient對象獲取實(shí)例信息的過程。
為RestTemplate對象注入攔截器,在底層攔截器中實(shí)現(xiàn)服務(wù)實(shí)例的獲取
- 何為服務(wù)發(fā)現(xiàn)?(從nacos獲取服務(wù)實(shí)例)
- LoadBalancerClient的作用?(從nacos獲取服務(wù)實(shí)例列表,然后本地基于負(fù)載均衡算法 獲取服務(wù)實(shí)例)
- @Loadbalanced作用?(描述RestTemplate對象,讓系統(tǒng)底層為RestTemplate對象賦能--簡化)
三、遠(yuǎn)程服務(wù)調(diào)用--Feign
3.1feign的注解
Feign 是一種聲明式Web服務(wù)客戶端,底層封裝了對Rest技術(shù)的應(yīng)用,通過Feign可以簡化服務(wù)消費(fèi)方對遠(yuǎn)程服務(wù)提供方法的調(diào)用實(shí)現(xiàn)。(不用自己拼url了)
1、添加依賴
<!--feign 中的api封裝了遠(yuǎn)程服務(wù)調(diào)用方式以及錯誤機(jī)制--> 可以不用 直接用RestTemplate
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、@EnableFeignClients--啟動類上添加注解
使用@EnableFeignClients注解配置啟動類時,主要用于告訴spring框架,要對使用@FeignClient的接口創(chuàng)建實(shí)現(xiàn)類以及對象
實(shí)現(xiàn)Feign的時候 注意有代理對象,然后實(shí)現(xiàn)的http:url的訪問遠(yuǎn)程服務(wù)
3、@FeignClient--接口上,定義遠(yuǎn)程調(diào)用規(guī)范
@FeignClient(name="sca-provider")--name屬性是遠(yuǎn)端服務(wù)名,同時也會將這個名字作為接口實(shí)現(xiàn)類的 Bean對象名字
4、@GetMapping等注解--在接口中的方法上添加
Feign接口是基于方法上的@GetMapping等注解中的value(默認(rèn)不寫)值,調(diào)用遠(yuǎn)端服務(wù)的某個具體方法
@GetMapping("/provider/echo/{msg}")--調(diào)用sca-provider中的provider類中的echo方法
String echoMsg(@PathVariable("msg") String msg);//jdk版本中只有參數(shù)arg0,arg1,所以要特定指定參數(shù)
5、創(chuàng)建controller類,定義方法,基于怎么樣的額方式調(diào)用遠(yuǎn)程服務(wù)
外界輸入的url:http://localhost:8090/consumer/echo4/aaa先訪問consumer中的方法,然后跳轉(zhuǎn)到feign接口中
3.2Feign配置進(jìn)階
防止同一個服務(wù)提供了很多服務(wù)調(diào)用方法,并添加容錯方案
1、添加唯一標(biāo)識屬性屬性
為遠(yuǎn)程調(diào)用服務(wù)接口指定一個contextId屬性,作為遠(yuǎn)程調(diào)用服務(wù)的唯一標(biāo)識即可
@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider服務(wù)提供名稱
interface RemoteProviderService{
@GetMapping("/provider/echo/{string}")//前提是遠(yuǎn)端需要有這個服務(wù)
public String echoMessage(@PathVariable("string") String string);
}
2、實(shí)現(xiàn)類做相關(guān)處理
創(chuàng)建實(shí)現(xiàn)類--調(diào)用feign.hystrix提供的FallbackFactory接口
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService前面的接口> {
/**此方法會在RemoteProviderService接口服務(wù)調(diào)用時,出現(xiàn)了異常后執(zhí)行.*/
@Override
public RemoteProviderService create(Throwable throwable) {//自動重寫的方法
//JDK8中l(wèi)ambda表達(dá)式--匿名函數(shù)--用箭頭操作符--左側(cè) : Lambda 表達(dá)式的參數(shù)列表
--右側(cè) : Lambda 表達(dá)式中所需執(zhí)行的功能, 即 Lambda 體
return (msg) -> {
return "服務(wù)維護(hù)中,請稍等";
};
}
3、在@FeignClient注解的接口中,添加fallbackFactory屬性=實(shí)現(xiàn)類的名字;在Feign訪問接口中應(yīng)用FallbackFactory對象
4、服務(wù)中斷處理機(jī)制
3.3調(diào)用流程
1)通過 @EnableFeignCleints 注解告訴springcloud,啟動 Feign Starter 組件。
2) Feign Starter 在項(xiàng)目啟動過程中注冊全局配置,掃描包下所由@FeignClient注解描述的接口,然后由系統(tǒng)底層創(chuàng)建接口實(shí)現(xiàn)類(JDK代理類),并構(gòu)建類的對象,然后交給spring管理(注冊 IOC 容器)。
3) 接口被調(diào)用時被動態(tài)代理類邏輯攔截, @FeignClient 請求信息通過編碼器生成 Request請求對象,基于此對象進(jìn)行遠(yuǎn)程過程調(diào)用。
4) 請求對象經(jīng)Ribbon進(jìn)行負(fù)載均衡,挑選出一個健康的 Server 實(shí)例(instance)
5) 通過 Client(客戶端) 攜帶 Request 調(diào)用遠(yuǎn)端服務(wù)返回請求響應(yīng)。
6) 通過解碼器生成 Response 返回客戶端,將信息流解析成為接口返回?cái)?shù)據(jù)。
- Feign是什么?(Spring Cloud微服務(wù)規(guī)范中的一組遠(yuǎn)程調(diào)用API)
- 為什么使用Feign?(優(yōu)化服務(wù)調(diào)用結(jié)構(gòu))
- 如何使用Feign實(shí)現(xiàn)服務(wù)調(diào)用?(加入依賴,@EnableFeignClients啟動類上,@FeignClient加在接口上)
- Feign方式的服務(wù)調(diào)用原理是怎樣的?(底層基于代理對象實(shí)現(xiàn))
四、Sentinel限流和熔斷
Sentinel (分布式系統(tǒng)的流量防衛(wèi)兵) 是阿里開源的一套用于服務(wù)容錯的綜合性解決方案
以流量為切入點(diǎn), 從流量控制、熔斷降級、系統(tǒng)負(fù)載保護(hù)等多個維度來保護(hù)服務(wù)的穩(wěn)定性。
sentinel的核心:核心庫(java客戶端)、控制臺(Dashboard)儀表盤--整合了很多第三方的框架,圖表等等
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
1、添加了此依賴以后,會在項(xiàng)目中添加一個攔截器對象,這個對象會對此服務(wù)發(fā)出的請求進(jìn)行攔截,會與sentinel控制臺定義的限流規(guī)則進(jìn)行比對,在允許范圍內(nèi)則繼續(xù)訪問,否則則拋出處理后的異常。
stringMVC中有Handler Interceptor這個類提供的攔截器prehandle---在controller類之前攔截,如果服務(wù)重啟了,規(guī)則消失--需要改源碼
2、修改yml
sentinel:
transport:
dashboard: localhost:8180 #這里描述的是sentinel控制臺的地址
eager: true #服務(wù)啟動后就與sentimel控制臺注冊 而不用瀏覽器訪問請求了
4.1sentinel限流
三種限流模式:直接限流,關(guān)聯(lián)限流,鏈路限流(@SentinelResource("資源名"))
三種流控效果:快速失敗,預(yù)熱(WarmUp),排隊(duì)等待
- Sentinel是什么?(阿里推出一個流量控制平臺,防衛(wèi)兵)
- 類似Sentinel的產(chǎn)品你知道有什么?(hystrix-一代微服務(wù)產(chǎn)品spring-cloud微服務(wù))
- Sentinel是如何對請求進(jìn)行限流的?(基于sentinel依賴提供的攔截器)
- 為什么要進(jìn)行限流? (系統(tǒng)處理能力有限,可以通過限流方式,保證系統(tǒng)可靠運(yùn)行)
- Sentinel限流的基本原理?(底層對請求進(jìn)行攔截,然后通過流控規(guī)則限定對資源訪問)
- Sentinel限流有哪些算法? (計(jì)數(shù)器,令牌桶,漏桶,滑動窗口算法--sentinel默認(rèn))
- 你了解sentinel中的閾值應(yīng)用類型嗎?(兩種-QPS(每秒請求次數(shù)),線程數(shù))
4.2sentinel降級熔斷
鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級---調(diào)用超時或異常比例升高,讓請求快速失敗,避免影響到其它的資源而導(dǎo)致級聯(lián)錯誤。當(dāng)資源被降級后,在接下來的降級時間窗口之內(nèi),對該資源的調(diào)用都自動熔斷(默認(rèn)行為是拋出 DegradeException)
4.3sentinel異常處理
自己進(jìn)行定義異常處理機(jī)制,直接或間接實(shí)現(xiàn)BlockExceptionHandler接口,并將對象交給spring管理。用于處理BlockException類型以及子類類型異常
4.4熱點(diǎn)限流
@SentinelResource("resource")--方法上-基于參數(shù)或參數(shù)值進(jìn)行限流
熱點(diǎn)規(guī)則的限流模式只有QPS模式(這才叫熱點(diǎn))。sentinel中心設(shè)置的參數(shù)索引為@SentinelResource注解的參數(shù)的下標(biāo),0代表第一個參數(shù),1代表第二個參數(shù)
4.5授權(quán)規(guī)則(黑白名單)
自己創(chuàng)建類實(shí)現(xiàn)implements RequestOriginParser,交給spring管理加@Component注解,當(dāng)設(shè)置了授權(quán)規(guī)則后,系統(tǒng)底層攔截請求,會調(diào)用此方法,對請求數(shù)據(jù)進(jìn)行解析
- Sentinel 的降級(熔斷)策略有哪些?(慢調(diào)用-響應(yīng)時長,異常比例-異常占比,異常數(shù))
- Sentinel 的熱點(diǎn)規(guī)則中的熱點(diǎn)數(shù)據(jù)?(熱賣商品,微博大咖,新上映的電影)
- 為什么要進(jìn)行異常處理?(提高用戶體驗(yàn))
- Sentinel中限流,降級的父類異常類型是什么?(BlockException)
- Sentinel中默認(rèn)的BlockException處理對象是誰?(DefaultBlockException)
- 如何自己定義Sentinel的異常處理對象?(直接或間接繼承BlockExceptionHandler)
- 如何理解sentinel中的系統(tǒng)規(guī)則?(全局限流規(guī)則,基于QPS,CPU,…)
- 如何理解Sentinel中的授權(quán)規(guī)則?(黑白名單)
五、網(wǎng)關(guān)GateWay
訪問入口管理--API網(wǎng)關(guān)--就是URL的入口管理
網(wǎng)關(guān)是訪問內(nèi)部服務(wù)的入口,隱藏真實(shí)地址,對請求中的數(shù)據(jù)過濾--用Filter過濾器
Spring Cloud Gateway提供了權(quán)限認(rèn)證,監(jiān)控、限流等功能,--單點(diǎn)登錄認(rèn)證
- 優(yōu)點(diǎn):
- 缺點(diǎn):
5.1配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
API網(wǎng)關(guān)在微服務(wù)架構(gòu)中也是一個web服務(wù),但這個web服務(wù)的啟動不是依賴于tomcat
而是依賴于網(wǎng)絡(luò)編程框架Netty,添加依賴后,系統(tǒng)會自動幫我們關(guān)聯(lián)下載一個netty框架
server:
port: 9000
spring:
cloud:
gateway:
routes: #配置網(wǎng)關(guān)路由規(guī)則
- id: route01 #路由標(biāo)識符id,自己指定一個唯一值即可
uri: http://localhost:8081/ #網(wǎng)關(guān)幫我們轉(zhuǎn)發(fā)的url,轉(zhuǎn)發(fā)的目的地(微服務(wù)) url是屬于uri的子級
predicates: ###斷言(謂此):匹配請求規(guī)則,進(jìn)行條件判斷,只有斷言都返回真,才會執(zhí)行路由
- Path=/nacos/provider/echo/** #請求路徑定義,此路徑對應(yīng)uri中的資源
filters: ##網(wǎng)關(guān)過濾器,用于對謂詞中的內(nèi)容進(jìn)行判斷分析以及處理
- StripPrefix=1 #轉(zhuǎn)發(fā)之前去掉path中第一層路徑,例如nacos
啟動gateway、啟動nacos,啟動provider類,在類中找/provider/echo/*這個方法,如果有就能跳轉(zhuǎn)
訪問http://localhost:9000/nacos/provider/echo/dakun
5.2負(fù)載均衡設(shè)計(jì)
在訪問服務(wù)時,要基于服務(wù)serivce id(服務(wù)名)去查找對應(yīng)的服務(wù),讓請求從網(wǎng)關(guān)層進(jìn)行均衡轉(zhuǎn)發(fā)
1、我們需要將網(wǎng)關(guān)作為一個服務(wù)在nacos中進(jìn)行注冊--添加spring-cloud-alibaba-nacos-discovery
2、yml中配置, uri: lb://sca-provider lb實(shí)現(xiàn)負(fù)載均衡,后面加服務(wù)名
- 網(wǎng)關(guān)中沒有配置,是底層自己實(shí)現(xiàn)負(fù)載均衡,底層的LoadBalance Client Filter這個攔截器調(diào)用 Ribbon LoadBalanceClient對象中的choose方法實(shí)現(xiàn)負(fù)載均衡
- Ribbon是自己來實(shí)現(xiàn)Ribbon LoadBalancerClient對象實(shí)現(xiàn)的負(fù)載均衡。
網(wǎng)關(guān)請求轉(zhuǎn)發(fā)流程分析:
客戶端向Spring Cloud Gateway(網(wǎng)關(guān))發(fā)出請求。 如果Gateway Handler Mapping(處理器映射器) 通過斷言predicates(predicates)的集合確定請求與路由(Routers)匹配,則將其發(fā)送到Gateway Web Handler(web處理器)。 Gateway Web Handler 通過確定的路由中所配置的過濾鏈調(diào)用過濾器Filters。找到指定的攔截器(LoadBalanceClientFilter),調(diào)用Ribbon LoadBalanceClient對象實(shí)現(xiàn)負(fù)載均衡
Filter由虛線分隔的原因是, Filter可以在發(fā)送代理請求之前和之后運(yùn)行邏輯。處理的邏輯是 在處理請求時 排在前面的過濾器先執(zhí)行,而處理返回相應(yīng)的時候,排在后面的過濾器先執(zhí)行。
5.3限流設(shè)計(jì)
1、添加依賴:限流sentinel,sentinel-gateway(因?yàn)檫@個不是mvc的)
2、修改yml:
3、啟動網(wǎng)關(guān):-Dcsp.sentinel.app.type=1
1、自定義API維度限流(重點(diǎn))
是一種更細(xì)粒度的限流規(guī)則定義,它允許我們利用sentinel提供的API,將請求路徑進(jìn)行分組,然后在組上設(shè)置限流規(guī)則
1、新建API分組,定義匹配的字符串--請求路徑
2、根據(jù)API分組來限流
2、定制流控網(wǎng)關(guān)返回值
現(xiàn)在用的網(wǎng)關(guān)gateWay不是mvc了,不用寫那么多層,寫一個網(wǎng)關(guān)配置類,并用@Configuration注解標(biāo)識
一般都是用JSON串來返回響應(yīng)結(jié)果
- 新建一個BlockRequest Handler攔截器,
- 創(chuàng)建一個hashMap<>集合,put放狀態(tài)碼,和信息msg
- 把map轉(zhuǎn)換成json格式的字符串:String jsonStr=JSON.toJSONString(map);
- 返回服務(wù)端響應(yīng): return ServerResponse.ok().body();
5.4常見問題:
- 為什么使用網(wǎng)關(guān)?(服務(wù)安全,統(tǒng)一服務(wù)入口管理,負(fù)載均衡,限流,鑒權(quán))
- Gateway 服務(wù)的啟動 底層是通過誰去實(shí)現(xiàn)的?(Netty網(wǎng)絡(luò)編程框架--ServerSocket網(wǎng)絡(luò)服務(wù)端)
- 網(wǎng)關(guān)層面服務(wù)的映射方式怎樣的?(謂詞-path,…,服務(wù)名/服務(wù)實(shí)例)
- 何為謂詞?(網(wǎng)關(guān)中封裝了判斷邏輯的一個對象)
- 謂詞邏輯的設(shè)計(jì)是怎樣的?(謂詞判斷邏輯返回值為true則進(jìn)行請求轉(zhuǎn)發(fā))
- 你了解哪些謂詞邏輯?(path,請求參數(shù),請求方式,請求頭,….)
- 我們可以自己定義謂詞工廠對象嗎?(可以的)
- 網(wǎng)關(guān)層如何記錄服務(wù)的映射?(通過map,并要考慮讀寫鎖的應(yīng)用)
- 網(wǎng)關(guān)層面是如何實(shí)現(xiàn)負(fù)載均衡的?(通過服務(wù)id查找具體的服務(wù)實(shí)例,底層調(diào)用RibbonLoadBalanceClient對象中的choose方法實(shí)現(xiàn)負(fù)載均衡)
- 網(wǎng)關(guān)層面是如何通過服務(wù)名查找服務(wù)實(shí)例的?(Ribbon)
- 網(wǎng)關(guān)層面結(jié)合sentinel實(shí)現(xiàn)限流,其限流的類型有幾種?(兩種-Route id,api分組)
- 網(wǎng)關(guān)層面可以自定義限流后的異常處理結(jié)果嗎?(可以)
- 你知道Sentinel底層限流的算法有哪些?(滑動窗口,令牌桶,漏斗,。。。)
- 網(wǎng)關(guān)過濾器的作用是什么?(對請求和響應(yīng)數(shù)據(jù)做一個預(yù)處理)
- 網(wǎng)關(guān)過濾器的類型有哪些?(局部過濾器,全局過濾器--作用于所有請求鏈路,不用定義)
- 如何自己定義全局過濾器?(直接或間接實(shí)現(xiàn)Globa lFilter接口)
-------------------單點(diǎn)登錄系統(tǒng)SSO--------------
Single sign on
解決方案:
解決方案1:用戶登陸成功以后,將用戶登陸狀態(tài)存儲到redis數(shù)據(jù)庫,例如:弊端:多次操作數(shù)據(jù)庫,高并發(fā)的時候,性能差
解決方案2:用戶登陸成功以后,將用戶信息存儲到token(令牌),然后寫到客戶端進(jìn)行存儲。(本次設(shè)計(jì)方案),方案一的以前的token就是一個隨機(jī)的UUID,沒有實(shí)際意義;JWT token是攜帶了信息的令牌
關(guān)鍵技術(shù):
redis數(shù)據(jù)庫
auth認(rèn)證/授權(quán)
Spring Security技術(shù)(認(rèn)證,授權(quán))
JWT技術(shù) JWT token攜帶信息的令牌
也要用到服務(wù)的注冊、發(fā)現(xiàn)nacos,web服務(wù),mybatis-plus
生成序列化id?
工程的結(jié)構(gòu):用到的模塊
system系統(tǒng)模塊中涉及到的表
- tb_user_roles--用戶角色關(guān)系表,可以在此表中基于用戶id找到用戶角色
- tb_role_menus--角色菜單關(guān)系表,可以基于角色id找到菜單id
- tb_menus菜單表,為資源的外部表現(xiàn)形式,在此表中可以根據(jù)id找到權(quán)限標(biāo)識
一、auth2認(rèn)證服務(wù)中心
第三方應(yīng)用授權(quán)登錄,允許用戶授權(quán)第三方移動應(yīng)用訪問 另外服務(wù)提供者上的信息,不需要將用戶名/密碼提供給第三方移動應(yīng)用或分享他們數(shù)據(jù)的所有內(nèi)容
1、添加依賴、配置文件、啟動類
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- 用feign的方式調(diào)用,調(diào)用另外服務(wù)提供者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
加載的spring-cloud-starter-oauth2依賴中,包括Spring Security(內(nèi)置了一個頁面)+JWT+oauth2,所以我們一訪問localhost:8071,就會找到登錄頁面,登錄后默認(rèn)跳轉(zhuǎn)到index.html,并產(chǎn)生一個password
簡單的配置后已將可以登陸了
二、簡單的單體登錄
2.1遠(yuǎn)程信息調(diào)用
1、啟動類上添加@EnableFeignClients--啟動feign掃描,使用feign實(shí)現(xiàn)遠(yuǎn)程調(diào)用
2、創(chuàng)建service接口中@FeignClient遠(yuǎn)程調(diào)用 遠(yuǎn)程用戶 信息 ---直接sso-system中controller中的方法
@FeignClient(value = "sso-system",contextId = "remoteUserService")
2.2遠(yuǎn)程服務(wù)調(diào)用/認(rèn)證管理
3、創(chuàng)建業(yè)務(wù)邏輯處理對象,接口的實(shí)現(xiàn)類,實(shí)現(xiàn)遠(yuǎn)程 服務(wù) 調(diào)用 ,交給認(rèn)證管理器(AutnenticationManager)根據(jù)獲取到的信息去完成密碼的比對操作 (
- 根據(jù)用戶名查找用戶信息,判斷用戶是否存在,不存在則拋出異常
- 根據(jù)用戶id查詢用戶 是否有權(quán)限)
- 和封裝返回 用戶信息和權(quán)限對象,此對象最終會交給認(rèn)證管理器
request-->filer過濾器-->servlet控制器-->handler interceptor攔截器-->controller
2.3Security配置類--密碼加密
4、定義密碼加密對象Security配置類(要不然登錄會報錯)
@Configuration
public class SecurityConfig {
/*構(gòu)建密碼加密對象,比MD5更強(qiáng),登錄時,系統(tǒng)底層會基于此對象進(jìn)行密碼加密*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}//返回的是一個password的編碼器對象
2.4配置認(rèn)證規(guī)則(攔截規(guī)則)
繼承WebSecurityConfigurerAdapter
登陸請求url的放行(不需要認(rèn)證)和攔截(需要認(rèn)證)
2.5自定義處理方法--可省
2.6總結(jié)
單體登錄基本完成,但是上述設(shè)計(jì)所有的頁面都在一個項(xiàng)目中,只有一個tomcat——我們要完成分布式(多個服務(wù)器)。以上用戶的認(rèn)證操作,其實(shí)現(xiàn)主要基于Spring Security框架
設(shè)置cookies緩存,單體登錄都是存在session中的,servlet服務(wù)器端的Session,向客戶端響應(yīng)一些cookies(有效期就是會話時間)
還有生成JWT token給用戶的相關(guān)信息進(jìn)行加密——分布式架構(gòu),多個服務(wù)器
用auth認(rèn)證和授權(quán)
三、分布式單點(diǎn)登錄系統(tǒng)
3.0總結(jié):
Spring Security是基礎(chǔ),OAuth和Jwt是具體實(shí)現(xiàn),在security上生效
OAuth2.0是規(guī)范、一種授權(quán)協(xié)議,不是實(shí)現(xiàn)
jwt是token的實(shí)現(xiàn)
如果我們的系統(tǒng)要給第三方做授權(quán),就實(shí)現(xiàn)OAuth2.0
如果我們要做前后端分離,就實(shí)現(xiàn)token就可以了,jwt(JSON WEB Token)僅僅是token的一種實(shí)現(xiàn)方式
- 1)SpringSecurity (提供認(rèn)證和授權(quán)的實(shí)現(xiàn))
- 2)TokenConfig(提供了令牌的生成,存儲,校驗(yàn))
- 3)Oauth2(定義了一套認(rèn)證規(guī)范,例如為誰發(fā)令牌,都發(fā)什么,...)
3.0Spring Security企業(yè)級安全框架
對軟件系統(tǒng)中的認(rèn)證,授權(quán),加密等功能進(jìn)行封裝,Spring Security 在企業(yè)中實(shí)現(xiàn)認(rèn)證和授權(quán)業(yè)務(wù)時,底層構(gòu)建了大量的過濾器,在過濾連中定義Oauth2和JWT的具體實(shí)現(xiàn)
3.1定義令牌的存儲對象--三種
* 構(gòu)建令牌配置對象,在微服務(wù)架構(gòu)中登陸成功后,可以將用戶信息進(jìn)行存儲,
常用存儲方式
* 1、產(chǎn)生一個隨機(jī)字符串(token),然后基于此字符串將用戶信息存儲到關(guān)系數(shù)據(jù)庫(mysql)
* 2、產(chǎn)生一個隨機(jī)字符串(token),然后基于此字符串將用戶信息存儲到內(nèi)存數(shù)據(jù)庫(redis)
* 3、基于Jwt創(chuàng)建令牌,存儲用戶信息,不需要寫到數(shù)據(jù)庫,在客戶端存儲即可
* 基于上述設(shè)計(jì)方案,Oauth2協(xié)議給了具體的API實(shí)現(xiàn)對象,例如:
* 1、JdbcTokenStore(用的較少)
* 2、RedisTokenStore(中型應(yīng)用,比jdbc快)
* 3、JwtTokenStore(對性能要求比較高的分布式架構(gòu))
Jwt數(shù)據(jù)規(guī)范--令牌生成
JWT(JSON WEB Token),是基于token 的認(rèn)證協(xié)議的實(shí)現(xiàn)
有三部分構(gòu)成:
Header頭部:是一個JSON對象 描述JWT的元數(shù)據(jù) 包含算法、令牌token的類型
{
"alg": "HS256",//alg屬性表示簽名的算法(algorithm)
"typ": "JWT" //令牌的類型(type)
}
Payload:實(shí)際需要傳遞的數(shù)據(jù),JWT規(guī)范中規(guī)定了7個官方字段,供選用
Signature:簽名部分,防止數(shù)據(jù)被串改,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道
算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點(diǎn)"(.)分隔,就可以返回給用戶。
3.2定義Oauth2認(rèn)證授權(quán)配置類
1、創(chuàng)建配置類,添加@EnableAuthorizationServer 啟動授權(quán)和認(rèn)證,@Configuration和全參構(gòu)造
繼承AuthorizationServer Configurer Adapter 類(Adapter適配器模式)
2、配置認(rèn)證規(guī)則:
- 配置由誰完成認(rèn)證(認(rèn)證管理器)
- 配置由誰負(fù)責(zé)查詢用戶業(yè)務(wù)數(shù)據(jù),訪問數(shù)據(jù)庫(認(rèn)證時需要兩部分信息:客戶端,數(shù)據(jù)庫)
- 配置可以處理的認(rèn)證請求方式(默認(rèn)支持post方式)
- 配置token生成及存儲策略(默認(rèn)令牌生成UUID,存儲方式為內(nèi)存)
3、修改令牌方法,主要是令牌增強(qiáng),配置令牌有效時間、是否支持刷新令牌、另設(shè)置刷新有效時長
下面解決兩個問題:
- 對誰頒發(fā)令牌(對客戶端有沒有要求,需要進(jìn)行配置)
- 訪問哪個路徑時幫你頒發(fā)令牌,需要對外暴露認(rèn)證路徑(定義頒發(fā)、解析、效驗(yàn)令牌的路徑)
4、在內(nèi)存中配置,定義客戶端的id(client_id 客戶端提交用戶信息認(rèn)證時需要這個id),定義客戶端秘鑰(client_secret 客戶端提交用戶信息時候需要攜帶這個秘鑰),定義作用范圍(符合規(guī)則的客戶端),允許客戶端基于密碼方式,刷新令牌實(shí)現(xiàn)認(rèn)證(grant_type=password)
5、我們登陸時要對哪個url發(fā)起請求,通過哪個url可以加載令牌,配置對外暴露的認(rèn)證url,刷新令牌的url,檢查令牌的url,
6、postMan訪問
7、401是認(rèn)證失敗--沒有攜帶令牌
403是沒有訪問權(quán)限--某種請求方式?jīng)]有在數(shù)據(jù)庫中設(shè)置權(quán)限
3.3略過的內(nèi)容
資源服務(wù)工程的設(shè)計(jì)與實(shí)現(xiàn)--sso-resource模塊--不做認(rèn)證,只做資源管理
網(wǎng)關(guān)工程--sso-gateway
客戶端UI工程--sso-ui--前后端分離項(xiàng)目,添加login.html頁面
網(wǎng)關(guān)層面添加限流
資源服務(wù)resource中日志的獲取---AOP方式獲取日志---Feign方式將日志傳遞給系統(tǒng)服務(wù)
--------------Docker虛擬引擎----簡化運(yùn)維-----------
一、Go語言和CS架構(gòu)
Docker是一個虛擬引擎(基于現(xiàn)有的資源虛擬出一個容器)、容器化技術(shù)平臺,基于 Google 公司的 Go 語言進(jìn)行實(shí)現(xiàn),基于這種方式,可更快地打包、測試以及部署應(yīng)用程序
有了docker,一個安裝配置好的mysql容器,可以直接拿到另一臺主機(jī)上啟動,而不必重新安裝mysql
誕生背景:服務(wù)多了維護(hù)困難,簡化部署運(yùn)維,提高服務(wù)的可維護(hù)性
docker平臺基本架構(gòu)CS(Client/Server)
核心對象:鏡像Image 容器Container
二、docker的操作命令
登錄 · 語雀
三、Docker數(shù)據(jù)管理
- 數(shù)據(jù)卷(Volumes):可供一個或多個容器使用的特殊目錄,可以在容器之間共享和重用,默認(rèn)會一直存在,即使容器被刪除。多個容器共用一個數(shù)據(jù)卷
- 掛在主機(jī)目錄(Bind mounts):
四、Dockerfile鏡像制作
Jdk鏡像、
基于JDK鏡像sentinel鏡像、
Mysql數(shù)據(jù)庫鏡像、
Redis數(shù)據(jù)庫鏡像、
Nginx代理鏡像、
nacos容器
五、常見問題
什么是數(shù)據(jù)卷(Volume)?(docker中的一個數(shù)據(jù)管理對象)
常用數(shù)據(jù)卷操作有哪些?(創(chuàng)建,查看,應(yīng)用,刪除)
如何理解數(shù)據(jù)卷和目錄掛載(Mount),兩者的不同(是否由Docker管理)?
鏡像的制作過程是怎樣的?(app+Dockerfile->Build)
鏡像文件可以被多個容器共享嗎?(可以,可以基于同一個鏡像文件啟動多個容器)
常用鏡像文件的下載以及容器的啟動?(MySql,Redis,Nginx,Naocs等)
退出容器后想再進(jìn)入容器怎么辦? 首先docker ps查看容器是否在運(yùn)行,假如沒有運(yùn)行要start啟動
Docker平臺下容器互聯(lián)操作(創(chuàng)建網(wǎng)絡(luò),查看網(wǎng)絡(luò)信息,應(yīng)用網(wǎng)絡(luò))
-----------Redis數(shù)據(jù)庫--緩存技術(shù)--------
要求掌握:
- redis的核心api--Jedis --RedisTemplate
- 存儲數(shù)據(jù)的方式--五重
- 投票系統(tǒng)、購物車、秒殺、單點(diǎn)登錄系統(tǒng)
- redis的持久化(兩種)
redis--分布式緩存數(shù)據(jù)庫,是一個key-value存儲系統(tǒng)
我們現(xiàn)在的項(xiàng)目基本上是web服務(wù)器(Tomcat)和數(shù)據(jù)庫獨(dú)立部署的,獨(dú)占資源,隨著用戶的增多,高并發(fā)讀寫數(shù)據(jù)庫會增大數(shù)據(jù)庫的壓力,性能下降。如果我們在tomcat服務(wù)器上增加本地緩存,并在外部增加分布式緩存,緩存熱門數(shù)據(jù)。通過緩存就能把絕大多數(shù)請求在讀寫數(shù)據(jù)庫前攔截掉,降低壓力
一、redis操作指令
docker start redis01 ---啟動redis01鏡像
docker exec -it redis01 bash---進(jìn)入到容器
redis-cli ---進(jìn)入redis數(shù)據(jù)庫
redis-cli -p 6379--進(jìn)入指定端口號的數(shù)據(jù)庫
exit--退出
shutdown--安全關(guān)閉redis服務(wù),保存數(shù)據(jù)
keys*---查看redis中的額所有key
set key value--基于key/value的形式存儲數(shù)據(jù)
get key
flushdb--清除當(dāng)前數(shù)據(jù)庫數(shù)據(jù)
flushall---清除所有數(shù)據(jù)庫數(shù)據(jù)
expire key seconds---控制key的有效時長
二、Redis數(shù)據(jù)類型
2.1String類型操作--散列方式
命令:
incr/incrby key value---自增,并返回遞增后的值
decr/decrby key value--自減/自減多個---可以實(shí)現(xiàn)博客的點(diǎn)贊操作
append key---向尾部追加值,如果鍵不存在則創(chuàng)建
strlen key---字?jǐn)?shù)統(tǒng)計(jì)
mset/mget---同時設(shè)置/獲取多個鍵值對
2.2Hash類型--方便存儲對象
大哈希(rides中的key value)中的小哈希(value 中的key和value)
命令:
hset KEY key value key2 value2 ---可以設(shè)置多個值
hget KEY key ---獲取value
hmget---可以獲取多個key對應(yīng)的值
hmset=hset
hincrby---正數(shù)加 負(fù)數(shù)減
hgetall---獲取所有的key value
hexists---是否存在
hdel---刪除
hkeys--獲取所有的key
hvals---獲取所有的value
2.3List類型--可重復(fù)、有序
redis中的list相當(dāng)于java中的linkedList,是一個雙向鏈表,支持正向、反向查找和遍歷。經(jīng)常用于實(shí)現(xiàn)熱銷榜,最新評論等設(shè)計(jì)
lpush--每次都從左面放 A B C D 的存放是D C B A
rpush--從右面放
rpop--從右面取出,先進(jìn)先出--對列結(jié)構(gòu) 取出A B C D --比如秒殺活動
lpop--左面出--先進(jìn)后出--棧結(jié)構(gòu) 取出 D C B A --手機(jī)中的頁面,最新評論
del--刪除key中的所有元素-clean
llen--集合的長度
lrange key 0 -1 --查看從第一個取到最后一個數(shù)據(jù) 不會移除數(shù)據(jù),只是讀,pop是取出
linsert--在指定位置插入 linsert lst1 after/before B C --不論左右都是直接在B后面插入C--結(jié)果是BCA
lrem--移除幾個元素 lrem lst1 2 C --list中允許元素重復(fù),從左邊移除
ltrim--保留指定位置間的元素,其他的移除,以前是去空格
lindex--查看指定下標(biāo)位置的元素
rpoplpush ONE TWO--從ONE右邊拿,放到TWO左邊,也可以兩個集合
brpop--阻塞式對列,如果沒有數(shù)據(jù)給拿出來,會等待時間結(jié)束后在取(釋放cpu),b代表阻塞
如何基于redis實(shí)現(xiàn)一個隊(duì)列結(jié)構(gòu)?(lpush/rpop)
如何基于redis實(shí)現(xiàn)一個棧結(jié)構(gòu)?(lpush/lpop)
如何基于redis實(shí)現(xiàn)一個阻塞式隊(duì)列?(lpush/brpop)
如何實(shí)現(xiàn)秒殺活動的公平性?(先進(jìn)先出--對列)
用戶注冊時的郵件發(fā)送功能如何提高其效率?(郵件發(fā)送是要調(diào)用三方服務(wù),底層通過隊(duì)列優(yōu)化其效率,隊(duì)列一般是list結(jié)構(gòu))
如何動態(tài)更新商品的銷量列表?(賣的好的排名靠前一些,linsert)
商家的粉絲列表使用什么結(jié)構(gòu)實(shí)現(xiàn)呢?(list結(jié)構(gòu)--有先后順序)
2.4set類型--不重復(fù)、無序
Redis中Set集合是通過哈希表實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是O(1)。
sadd--添加元素,重復(fù)元素添加失敗,返回0--去重
smembers--獲取內(nèi)容,成員
sismember--是否存在元素,返回值是布爾類型
spop--移除隨機(jī)幾個元素,并返回移除元素
scard--獲取元素的個數(shù)--投票系統(tǒng) 計(jì)數(shù) 點(diǎn)贊
smove--移動元素 smove set1 set2 C 把set1中的C 移動到set2
sunion--合并的顯示,并不是創(chuàng)建新的集合
srem--指定要刪除的元素
2.5zset類型--不重復(fù)、有序
zadd--添加元素
zrange--獲取索引區(qū)間內(nèi)的元素
zrangebyscore--獲取分?jǐn)?shù)區(qū)間內(nèi)的元素
zrem--刪除元素
zcard--獲取集合中元素的個數(shù)
zincrby--增減,正數(shù)增
zcount--獲取分?jǐn)?shù)區(qū)間內(nèi)元素個數(shù)
zrank--獲取項(xiàng)在zset中的索引
zrevrank--獲取在zset中的倒序索引
2.6常見問題:
- Redis 簡介(分布式緩存數(shù)據(jù)庫,非關(guān)系型數(shù)據(jù)庫,NoSQL數(shù)據(jù)庫)
- Redis 服務(wù)的線程模型(6.0之前都是單線程,6.0之后網(wǎng)絡(luò)io操作引入了多線程)
- Redis 數(shù)據(jù)庫的基本操作(服務(wù)的啟動,停止,redis的登入,登出)
- 為什么使用redis?(解決分布式系統(tǒng)下數(shù)據(jù)緩存的問題)
- 如何理解Redis數(shù)據(jù)庫的大小哈希(hash)操作?(全局是大哈希,局部value是小哈希)
- Redis數(shù)據(jù)中常用的數(shù)據(jù)類型的應(yīng)用場景?(
string類型--方便字?jǐn)?shù)統(tǒng)計(jì),日志追加
hash類--方便存儲對象
list類型--實(shí)現(xiàn)熱銷排行榜,最新評論(棧結(jié)構(gòu)-先進(jìn)后出),秒殺活動(對列結(jié)構(gòu)-先進(jìn)先出)
set類型--投票系統(tǒng),計(jì)數(shù),點(diǎn)贊功能)
三、Java中操作redis--Jedis
java中操作json 三劍客 jackson fastjson gson(谷歌提供的)
創(chuàng)建springboot工程自動就能應(yīng)用jackson
Jedis是Java中操作redis的一個客戶端,類似通過jdbc訪問mysql數(shù)據(jù)庫----spring
頻繁的對象創(chuàng)建和銷毀對象,性能降低--底層是TCP協(xié)議(三次握手,四次揮手)
- Redis 客戶端API(Jedis)的基本應(yīng)用(對象的創(chuàng)建,對象的銷毀,常用方法的應(yīng)用)
redis的API就是在java中怎么取代虛擬機(jī)中的操作命令,基本上都差不多。.set .expire設(shè)置key的有效時長 .pipelined--獲取管道 .exists(key) .type(key) 返回值的類型
3.1JedisPool連接池
Jedis連接池的測試--享元設(shè)計(jì)模式--設(shè)計(jì)思想通過池減少對象的創(chuàng)建次數(shù),實(shí)現(xiàn)對象的可重用性,所有池的設(shè)計(jì)都有這個設(shè)計(jì)模式的應(yīng)用(整數(shù)池,字符串池,線程池,連接池)
創(chuàng)建線程池:
public static Jedis getConnection02(){
if(jedisPool==null){
synchronized (JedisDataSource.class){//保證線程安全
if(jedisPool==null){ //雙重校驗(yàn)
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxTotal(16);
config.setMaxIdle(8);//最大空間
jedisPool=new JedisPool(config,IP, PORT);//創(chuàng)建對象
}
}
}
return jedisPool.getResource();//獲得資源
}
volatile:關(guān)鍵字--多線程可見性、禁止指令重排序、不保證原子性
四、項(xiàng)目實(shí)踐
4.1分布式id
在分布式系統(tǒng)中,數(shù)據(jù)量將越來越大時,就需要對數(shù)據(jù)進(jìn)行分表操作,但是,分表后,每個表中的數(shù)據(jù)都會按自己的節(jié)奏進(jìn)行自增,很有可能出現(xiàn)ID沖突。這時就需要一個單獨(dú)的機(jī)制來負(fù)責(zé)生成唯一ID,生成出來的ID也可以叫做 分布式ID,這里我們借助redis實(shí)現(xiàn)一個簡易的分布式id進(jìn)行實(shí)現(xiàn),
4.2單點(diǎn)登錄--hash
之前單點(diǎn)登錄用的是認(rèn)證授權(quán),沒存儲到數(shù)據(jù)庫,這里借助redis類存儲用戶信息
流程:
1、執(zhí)行登錄認(rèn)證,將來寫到認(rèn)證服務(wù)器中
- 檢驗(yàn)數(shù)據(jù)的合法性(判斷用戶名,密碼是否為空,密碼的長度是否有數(shù)字字母等)
- 基于用戶名查詢用戶信息,并判定密碼是否正確
- 用戶存在且密碼正確,將用戶信息寫到redis
- 將token返回給客戶端
2、攜帶token訪問資源服務(wù)器
- 效驗(yàn)token是否為空(為空說明未登陸)
- 基于token查詢redis數(shù)據(jù),假如有對應(yīng)數(shù)據(jù)說明用戶登錄了
- 檢查用戶是否有訪問資源的權(quán)限,有權(quán)限則可以訪問
- 返回要訪問的資源
4.3秒殺隊(duì)列--list
將商品搶購信息先寫到redis(以隊(duì)列形式進(jìn)行存儲),因?yàn)閷憆edis內(nèi)存數(shù)據(jù)庫要比寫mysql數(shù)據(jù)庫快很多倍
算法:先進(jìn)先出(FIFO)-體現(xiàn)公平性lpush--rpop
4.4投票系統(tǒng)--set
* 1.投票數(shù)據(jù)存儲到redis(key:活動id value:多個用戶id的集合)
* 2.同一個用戶不能執(zhí)行多次投票--用sismember來判斷返回的布爾值
* 3.業(yè)務(wù)操作(投票,獲取總票數(shù),檢查是否投過票,取消投票,投票人)
4.5購物車系統(tǒng)--hash
1.向購物車添加商品--hset每次執(zhí)行都是覆蓋,不是追加
2.查看購物車商品--hgetall
3.刪除購物車商品----
4.改變購物車某個商品的購買數(shù)量---
需要的參數(shù):用戶的id,商品id,總數(shù)
五、redis的API--RedisTemplate
RedisTemplate是springboot工程中操作redis數(shù)據(jù)庫的一個Java對象
模板方法設(shè)計(jì)模式
特性:在springBoot中RedisTemplate就不用像Jedis那樣創(chuàng)建連接池了,springboot中默認(rèn)使用lettuce連接池
ValueOperations<String,String> vo=redisTemplate.opsForValue();//獲取字符串操作對象
5.1RedisTemplate 應(yīng)用--jdk
基于jdk序列化和反序列化,是SpringRedisTemplate的父類
源碼中默認(rèn)序列化 Jdk Serialization RedisSerializer
需求:key序列化方式采用StringRedis方式-----setKeySerializer(RedisSerializer.string())
set---序列化 給redis設(shè)置值
get---反序列化,從redis拿出值,并解析成數(shù)據(jù)顯示在控制臺
- StringRedisTemplate和RedisTemplate兩個應(yīng)用時有什么不同?
StringRedisTemplate基于spring序列化,傳的參數(shù)必須是string類型的。RedisTemplate是jdk方式序列化的,存在redis數(shù)據(jù)的形式不同,是StringRedisTemplate的父類
六、redis與AOP整合應(yīng)用
基于AOP方式操作redis緩存
@EnableCaching--在啟動類上,啟動aop緩存應(yīng)用
當(dāng)我們在調(diào)用一個緩存方法時會把該方法參數(shù)和返回結(jié)果作為一個鍵值對存在緩存中 redis中
@Cacheable--spring--方法上--此注解描述的方法為切入點(diǎn)方法--一般用于存儲
此方法執(zhí)行時,底層會通過AOP機(jī)制,先從緩存取數(shù)據(jù),緩存有則直接返回,緩存沒有則查數(shù)據(jù),最后將查詢的數(shù)據(jù),還會向redis存儲一份
@Cacheable--更新緩存
屬性:CacheNames用來指定緩存組件的名字,將方法的返回結(jié)果放在哪個緩存中,可以是數(shù)組的方式,支持指定多個緩存。
使用緩存的時候要注意:緩存的雪崩、穿透;要注意緩存的更新,比如定時,然后訪問mysql
七、redis的持久化-rdb和aof
Redis是一種內(nèi)存數(shù)據(jù)庫,斷電時,數(shù)據(jù)可能會丟失。如果通過持久化將數(shù)據(jù)搞一份兒到磁盤上去,然后再定期同步到一些云存儲服務(wù)上去,那么就可以保證一些數(shù)據(jù)不丟失,保證數(shù)據(jù)的可靠性。
7.1Rdb方式持久化
redis data base 宿主機(jī)中用來存儲數(shù)據(jù)的磁盤數(shù)據(jù)--redis的默認(rèn)的數(shù)據(jù)持久化方式.系統(tǒng)啟動時會自動開啟這種方式的持久化機(jī)制
Rdb方式是通過手動:三種方式保存redis中的key/value--生成快照的方式持久化
save-是一種阻塞式方式必須所有的保存操作都執(zhí)行完了,
bgsave-異步,保存的數(shù)據(jù)交給后臺(又啟動了一個進(jìn)程,不是線程),自己可以接受別的命令
周期性方式保存(如多久的時間數(shù)據(jù)發(fā)生了變化,去保存--生成快照)
常見問題:
1、Redis中的save和bgsave有什么不同?
Redis Save 命令執(zhí)行一個同步保存操作,將當(dāng)前 Redis 實(shí)例的所有數(shù)據(jù)快照(snapshot)以 RDB 文件的形式保存到硬盤。
BGSAVE 命令執(zhí)行之后立即返回 OK ,然后 Redis fork 出一個新子進(jìn)程,原來的 Redis 進(jìn)程(父進(jìn)程)繼續(xù)處理客戶端請求,而子進(jìn)程則負(fù)責(zé)將數(shù)據(jù)保存到磁盤,然后退出。
2、RDB持久化機(jī)制有哪些優(yōu)點(diǎn)?
第一:RDB會生成多個數(shù)據(jù)文件,每個數(shù)據(jù)文件都代表某一個時刻中redis的數(shù)據(jù),非常適合做冷備
第二:RDB對redis對外提供的讀寫服務(wù),影響非常小,可以讓redis保持高性能,因?yàn)閞edis主進(jìn)程只需要一個子進(jìn)程,執(zhí)行磁盤IO操作來進(jìn)行RDB持久化。
第三:相對于AOF持久化機(jī)制來說,直接基于RDB數(shù)據(jù)文件來重啟和恢復(fù)redis進(jìn)程,更加快速。存儲是內(nèi)存中的數(shù)據(jù),aof中存儲的數(shù)命令
3、缺點(diǎn):數(shù)據(jù)丟失比較嚴(yán)重
它都是每隔5分鐘或更長時間做一次快照,這個時候一旦redis進(jìn)程宕機(jī),那么會丟失最近幾分鐘的數(shù)據(jù)
7.2aof方式持久化
默認(rèn)是關(guān)閉的:appendonly no 可能會影響性能:寫一次指令 保存一次磁盤
Aof方式是通過記錄寫操作日志的方式,記錄redis數(shù)據(jù)的一種持久化機(jī)制,這個機(jī)制默認(rèn)是關(guān)閉的。
Redis支持三種刷寫模式:何時刷新指令
- #appendfsync always #每次收到寫命令就立即強(qiáng)制寫入磁盤,是最安全的,速度也是最慢的,一般不推薦。
- appendfsync everysec #每秒鐘強(qiáng)制寫入磁盤一次,在性能和持久化方面做平衡,推薦該方式。
- #appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,性能最好但是持久化最沒有保證,不推薦。
日志的重寫:何時啟動重寫
- 當(dāng)前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。
- 當(dāng)前AOF文件啟動新的 設(shè)置日志重寫過程的最小值,避免剛剛啟動Reids時由于文件尺寸較小導(dǎo)致頻繁的重寫。
常見問題:
1、如何理解AOF方式中的rewrite 重寫 操作?
redis中的可以存儲的數(shù)據(jù)是有限的,很多數(shù)據(jù)可能會自動過期,也可能會被用戶刪除或被redis用緩存清除的算法清理掉。只有一部分常用的數(shù)據(jù)會被自動保留在redis內(nèi)存中,所以可能很多之前的已經(jīng)被清理掉的數(shù)據(jù),對應(yīng)的寫日志還停留在AOF中,AOF日志文件就一個,會不斷的膨脹,最好導(dǎo)致文件很大。
所以,AOF會自動在后臺每隔一定時間做rewrite操作,比如當(dāng)前AOF文件大小是上次日志重寫得到AOF文件大小的二倍時,自動啟動新的日志重寫過程。當(dāng)上次文件過小時,不啟動重寫,覆蓋之前的老日志,從而,確保AOF日志文件不會過大,保持跟redis內(nèi)存數(shù)據(jù)量一致.
2、AOF持久化機(jī)制有哪些優(yōu)點(diǎn)?
第一:更好的保護(hù)數(shù)據(jù)不丟失,AOF會每隔1秒,執(zhí)行一次fsync刷寫操作,最多丟失1秒鐘的數(shù)據(jù).
第二:AOF日志文件通常以append-only模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,并且文件不容易破損,即使文件尾部破損,也很容易修復(fù)。
第三:AOF日志文件過大的時候,出現(xiàn)后臺重寫操作,也不會影響客戶端的讀寫
第四:AOF日志文件的命令通過易讀的方式進(jìn)行記錄,非常適合做災(zāi)難性的誤刪除的緊急恢復(fù),只要這個時候后臺rewrite還沒有發(fā)生,那么就可以立即拷貝AOF文件,將最后一條flushall命令給刪了,然后再將該AOF文件放回去,就可以通過恢復(fù)機(jī)制,自動恢復(fù)所有數(shù)據(jù).
3、AOF持久化機(jī)制有哪些缺點(diǎn)?
第一:對于同一份數(shù)據(jù)來說,AOF日志文件通常比RDB數(shù)據(jù)快照文件更大。
第二:AOF開啟后,支持的寫QPS會比RDB支持的寫QPS低,因?yàn)锳OF一般會配置成每秒fsync一次日志文件,當(dāng)然,每秒一次fsync,性能也還是很高的。性能比rdb低
第三:AOF這種基于命令日志方式,比基于RDB每次持久化一份完整的數(shù)據(jù)快照文件的方式,更加脆弱一些,容易有bug。不過AOF為了避免rewrite過程導(dǎo)致的bug,因此每次rewrite并不是基于舊的指令日志進(jìn)行merge的,而是基于當(dāng)時內(nèi)存中的數(shù)據(jù)進(jìn)行指令的重新構(gòu)建,這樣健壯性會好很多
不適合做冷備
4、如何選擇redis的持久化方式?
第一:不要僅僅使用RDB,因?yàn)槟菢訒?dǎo)致你丟失很多數(shù)據(jù)。
第二:也不要僅僅使用AOF,因?yàn)锳OF做冷備沒有RDB做冷備進(jìn)行數(shù)據(jù)恢復(fù)的速度快,并且RDB簡單粗暴的數(shù)據(jù)快照方式更加健壯。
第三:綜合使用AOF和RDB兩種持久化機(jī)制,用AOF來保證數(shù)據(jù)不丟失,作為數(shù)據(jù)恢復(fù)的第一選擇; 用RDB來做不同程度的冷備。
八、redis事務(wù)處理--樂觀鎖
mysql中大多數(shù)都是悲觀鎖,redis中采用樂觀鎖
它使用watch命令監(jiān)視給定的key,當(dāng)exec(提交事務(wù))的時候,如果監(jiān)視的key從調(diào)用watch后發(fā)生過變化,則整個事務(wù)會失敗--多個客戶端之間,都操作,但是只有一個成功了,多個客戶端都沒有阻塞。
也可以調(diào)用watch多次監(jiān)視多個key。注意watch的key是對整個連接有效的,如果連接斷開,監(jiān)視和事務(wù)都會被自動清除。當(dāng)然exec,discard,unwatch命令都會清除連接中的所有監(jiān)視。
redis的原子性:分怎么說,有的版本當(dāng)發(fā)生錯誤的時候,還會繼續(xù)輸入,但是后來又改了
redis如何保證數(shù)據(jù)的可靠性:1.持久化,數(shù)據(jù)不容易丟 2.事務(wù)保證提交的數(shù)據(jù) 3.架構(gòu)的設(shè)計(jì),多個redis
8.1基本指令
redis進(jìn)行事務(wù)控制時,通常是基于如下指令進(jìn)行實(shí)現(xiàn),例如:
- multi 開啟事務(wù)
- exec 提交事務(wù)
- discard 取消事務(wù)
- watch 監(jiān)控,如果監(jiān)控的值發(fā)生變化,則提交事務(wù)時會失敗--使用樂觀鎖
- unwatch 去掉監(jiān)控
exec提交事務(wù):先開啟multi事務(wù)后,把指令放到一個隊(duì)列里面,只有提交exec的時候才會執(zhí)行指令
discard取消事務(wù):redis事務(wù)太簡單,沒有回滾,不會回到原來的數(shù)據(jù),而只有取消(遇到錯誤也取消),不執(zhí)行對列中的指令
8.2秒殺、搶票--樂觀鎖
基于一個秒殺,搶購案例,演示redis樂觀鎖方式
客戶端1:
127.0.0.1:6379> set ticket 1
OK
127.0.0.1:6379> set money 0
OK
127.0.0.1:6379> watch ticket #樂觀鎖,對值進(jìn)行觀察,改變則事務(wù)失敗
OK
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379> decr ticket //票數(shù)-1
QUEUED
127.0.0.1:6379> incrby money 100 //沒有提交事務(wù)
QUEUED
第二步:打開客戶端2,執(zhí)行如下操作,演示還沒等客戶端1提交事務(wù),此時客戶端2把票買到了。
127.0.0.1:6379> get ticket
"1"
127.0.0.1:6379> decr ticket
(integer) 0//票搶到了 ticker變成了0 默認(rèn)自動提交事務(wù)
第三步,回到客戶端1:提交事務(wù),檢查ticket的值
127.0.0.1:6379> exec
(nil) #提交事務(wù),失敗
127.0.0.1:6379> get ticket
“0”//只有一個人成功了,客戶端1和2都減1了,但是只有一個成功了,兩個執(zhí)行都沒有阻塞,樂觀鎖
127.0.0.1:6379> unwatch #取消監(jiān)控
九、多個redis架構(gòu)設(shè)計(jì)--主從、哨兵、集群高可用
9.1Redis主從架構(gòu)(Master/Slave)
單個Redis支持的讀寫能力還是有限的(redis便于查詢 讀),此時我們可以使用多個redis來提高redis的并發(fā)處理能力,這些redis如何協(xié)同,就需要有一定的架構(gòu)設(shè)計(jì)(讀寫架構(gòu)的設(shè)計(jì),讀多或者寫少),這里我們首先從
主從架構(gòu)(Master/Slave)(一個master和多個slave)架構(gòu)進(jìn)行分析和實(shí)現(xiàn).,master負(fù)責(zé)讀寫,并將數(shù)據(jù)同步到salve,從節(jié)點(diǎn)負(fù)責(zé)讀操作.
當(dāng)向master寫數(shù)據(jù),master扔一個rdb冷備給slave(全量同步--初始化時),第二次寫master就是發(fā)送aof--日志了,只是增量同步實(shí)現(xiàn)數(shù)據(jù)的同步(只備份更新的部分)
9.2Redis哨兵模式
master宕機(jī)了,把slave升級成master
哨兵(Sentinel)是Redis的主從架構(gòu)模式下,實(shí)現(xiàn)高可用性(high availability)的一種機(jī)制。
由一個或多個Sentinel實(shí)例(instance)組成的Sentinel系統(tǒng)(system)可以監(jiān)視任意多個主服務(wù)器,以及這些主服務(wù)器屬下的所有從服務(wù)器,并在被監(jiān)視的主服務(wù)器進(jìn)入下線狀態(tài)時,自動將下線主服務(wù)器屬下的某個從服務(wù)器升級為新的主服務(wù)器,然后由新的主服務(wù)器代替已下線的主服務(wù)器繼續(xù)處理命令請求。
9.3Redis集群高可用
面試題
1、基礎(chǔ)
14.接口和抽象類有什么區(qū)別?
抽象類可以有 main 方法,并且我們能運(yùn)行它;接口不能有 main 方法。
16.BIO、NIO、AIO 有什么區(qū)別?
- BIO:Block IO 同步 阻塞式 IO,就是我們平常使用的傳統(tǒng) IO,它的特點(diǎn)是模式簡單使用方便,并發(fā)處理能力低。
- NIO:New IO 同步 非阻塞 IO,是傳統(tǒng) IO 的升級,客戶端和服務(wù)器端通過 Channel(通道)通訊,實(shí)現(xiàn)了多路復(fù)用。
- AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實(shí)現(xiàn)了異步 非堵塞 IO ,異步 IO 的操作基于事件和回調(diào)機(jī)制。
17、配置文件
1、bootstrap的配置文件優(yōu)先加載application
2、如果在不同的目錄中存在多個配置文件,它的讀取順序是:
1、config/application.properties(項(xiàng)目根目錄中config目錄下)
2、config/application.yml
3、application.properties(項(xiàng)目根目錄下)
4、application.yml
如果同一個目錄下,有application.yml也有application.properties,默認(rèn)先讀取application.properties。但是yml的可讀性更高些,yml使用key:value 和縮進(jìn)形式,properties文件必須寫全路徑
2、集合
19. Collection 和 Collections 有什么區(qū)別?
- java.util.Collection 是一個集合接口(集合類的一個頂級接口)。
- Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態(tài)方法,用于對集合中元素進(jìn)行排序、搜索以及線程安全等各種操作。
21. HashMap 和 Hashtable 有什么區(qū)別?
- hashTable同步的、安全的,而HashMap是非同步的,效率上比hashTable要高。
- hashMap允許空鍵值(不安全),而hashTable不允許。
- hashMap是數(shù)組+鏈表的結(jié)構(gòu)
22. 如何決定使用 HashMap 還是 TreeMap?
HashMap在Map中插入、刪除和定位元素這類操作
TreeMap對一個有序的key集合進(jìn)行遍歷,是更好的選擇。
24. 說一下 HashSet 的實(shí)現(xiàn)原理?
- HashSet底層由HashMap實(shí)現(xiàn)
- HashSet的值存放于HashMap的key上
- HashMap的value統(tǒng)一為PRESENT
25. ArrayList 和 LinkedList 的區(qū)別是什么?
ArrrayList底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機(jī)訪問,
LinkedList 的底層數(shù)據(jù)結(jié)構(gòu)是雙向循環(huán)鏈表,不支持隨機(jī)訪問。使用下標(biāo)訪問一個元素,ArrayList 的時間復(fù)雜度是 O(1),而 LinkedList 是 O(n)。
26. 如何實(shí)現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?
- List轉(zhuǎn)換成為數(shù)組:調(diào)用ArrayList的toArray方法。
- 數(shù)組轉(zhuǎn)換成為List:調(diào)用Arrays的asList方法。
27. ArrayList 和 Vector 的區(qū)別是什么?
- Vector是同步的、安全的
- ArrayList比Vector快,它因?yàn)橛型?#xff0c;不會過載。
- ArrayList更加通用,因?yàn)槲覀兛梢允褂肅ollections工具類輕易地獲取同步列表和只讀列表。
28. Array 和 ArrayList 有何區(qū)別?
- Array可以容納基本類型和對象,而ArrayList只能容納對象。
- Array是指定大小后不可變的,而ArrayList大小是可變的。
- Array沒有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
3、線程
線程和進(jìn)程的區(qū)別:
進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單元
線程是操作系統(tǒng)進(jìn)行任務(wù)分配的最小單元,線程隸屬于進(jìn)程
41. sleep() 和 wait() 有什么區(qū)別?
sleep():方法是線程類(Thread)的靜態(tài)方法,讓調(diào)用線程進(jìn)入睡眠狀態(tài),不釋放鎖機(jī)制,其他線程依然無法訪問這個對象。
wait():wait()是Object類的方法,當(dāng)一個線程執(zhí)行到wait方法時,它就進(jìn)入到一個和該對象相關(guān)的等待池,釋放對象的機(jī)鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程
42. notify()和 notifyAll()有什么區(qū)別?--都是object中的方法
當(dāng)有線程調(diào)用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機(jī)喚醒一個 wait 線程),被喚醒的的線程便會進(jìn)入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。
假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調(diào)用 wait()方法,它才會重新回到等待池中。
43. 線程的 run()和 start()有什么區(qū)別?
start()方法來啟動一個線程,真正實(shí)現(xiàn)了多線程運(yùn)行。這時無需等待run方法體代碼執(zhí)行完畢,可以直接繼續(xù)執(zhí)行下面的代碼;
用run()方法必須等待run()方法執(zhí)行完畢才能執(zhí)行下面的代碼,所以執(zhí)行路徑還是只有一條,根本就沒有線程的特征
44. 創(chuàng)建線程池有哪幾種方式?
兩類:通過Excutors創(chuàng)建、另一類:通過ThreadPoolExecutor創(chuàng)建--最原始的創(chuàng)建線程池方式
①. new Fixed ThreadPool(int nThreads)創(chuàng)建一個固定長度的線程池,每當(dāng)提交一個任務(wù)就創(chuàng)建一個線程,直到達(dá)到線程池的最大數(shù)量,這時線程規(guī)模將不再變化,當(dāng)線程發(fā)生未預(yù)期的錯誤而結(jié)束時,線程池會補(bǔ)充一個新的線程。
②. new Cached ThreadPool()創(chuàng)建一個可緩存的線程池,如果線程池的規(guī)模超過了處理需求,將自動回收空閑線程,而當(dāng)需求增加時,則可以自動添加新線程,線程池的規(guī)模不存在任何限制。
③. new Single ThreadExecutor()這是一個單線程的Executor,它創(chuàng)建單個工作線程來執(zhí)行任務(wù),如果這個線程異常結(jié)束,會創(chuàng)建一個新的來替代它;它的特點(diǎn)是能確保依照任務(wù)在隊(duì)列中的順序來串行執(zhí)行。
④. new Scheduled ThreadPool(int corePoolSize)創(chuàng)建了一個固定長度的線程池,而且以延遲或定時的方式來執(zhí)行任務(wù),類似于Timer。
⑤:new SingleThread ScheduledExecutor:創(chuàng)建一個單線程的可以執(zhí)行延遲任務(wù)的線程池;
⑥:new WorkStealing Pool:創(chuàng)建一個搶占式執(zhí)行的線程池(任務(wù)執(zhí)行順序不確定)JDK 1.8 添加
⑦:ThreadPoolExecutor:最原始的創(chuàng)建線程池的方式
45. 線程池都有哪些狀態(tài)?
線程池有5種狀態(tài):Running、ShutDown、Stop、Tidying、Terminated。
線程池各個狀態(tài)切換框架圖:
46.線程池的參數(shù):
core Pool Size 線程池核心線程大小 一個最小的線程數(shù)量,
maxi mum PoolSize 線程池最大線程數(shù)量 一個最大線程數(shù)量的限制
workQueue 工作隊(duì)列 新任務(wù)被提交后,會先進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時再從隊(duì)列中取出任務(wù)
keepAliveTime 空閑線程存活時間
unit 空閑線程存活時間單位
threadFactory 線程工廠
handler 拒絕策略
46. 線程池中 submit()和 execute()方法有什么區(qū)別?
接收的參數(shù)不一樣submit有返回值,而execute沒有,submit方便Exception處理
47. 在 java 程序中怎么保證多線程的運(yùn)行安全?
線程安全在三個方面體現(xiàn):
原子性:提供互斥訪問,同一時刻只能有一個線程對數(shù)據(jù)進(jìn)行操作,(atomic,synchronized);
可見性:一個線程對主內(nèi)存的修改可以及時地被其他線程看到,(synchronized,volatile);
有序性:一個線程觀察其他線程中的指令執(zhí)行順序,由于指令重排序,該觀察結(jié)果一般雜亂無序,(happens-before原則)。
48. 多線程鎖的升級原理是什么?
在Java中,鎖共有4種狀態(tài),級別從低到高依次為:無狀態(tài)鎖,偏向鎖,輕量級鎖和重量級鎖狀態(tài),這幾個狀態(tài)會隨著競爭情況逐漸升級。鎖可以升級但不能降級。
49. 什么是死鎖?
死鎖是指兩個或兩個以上的進(jìn)程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。
50. 怎么防止死鎖?
死鎖的四個必要條件:
互斥條件:進(jìn)程對所分配到的資源不允許其他進(jìn)程進(jìn)行訪問,若其他進(jìn)程訪問該資源只能等待,直至占有該資源的進(jìn)程使用完成后釋放該資源
請求和保持條件:進(jìn)程獲得一定的資源之后,又對其他資源發(fā)出請求,但是該資源可能被其他進(jìn)程占有,此事請求阻塞,但又對自己獲得的資源保持不放
不可剝奪條件:是指進(jìn)程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放
環(huán)路等待條件:是指進(jìn)程發(fā)生死鎖后,若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系
這四個條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發(fā)生死鎖。
52.說一下 synchronized 底層實(shí)現(xiàn)原理?
synchronized保證方法或者代碼塊在運(yùn)行時,同一時刻只有一個方法可以進(jìn)入到臨界區(qū),同時它還可以保證共享變量的內(nèi)存可見性(只有一個人看)。
Java中每一個對象都可以作為鎖,synchronized實(shí)現(xiàn)同步的基礎(chǔ):
普通同步方法,鎖是當(dāng)前實(shí)例對象
靜態(tài)同步方法,鎖是當(dāng)前類的class對象
同步方法塊,鎖是括號里面的對象
53. synchronized 和 volatile 的區(qū)別是什么?
volatile關(guān)鍵字,只能修飾屬性,標(biāo)記字段可能被多個線程訪問(異步),保證線程可見性,不保證原子性
synchronized則是鎖定當(dāng)前變量、方法、類,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。
保證變量的修改可見性和原子性。
volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
54. synchronized 和 Lock 有什么區(qū)別?
首先synchronized是java內(nèi)置關(guān)鍵字,在jvm層面,Lock是個java類;
synchronized無法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖;
synchronized會自動釋放鎖(a?線程執(zhí)行完同步代碼會釋放鎖 ;b 線程執(zhí)行過程中發(fā)生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
用synchronized關(guān)鍵字的兩個線程1和線程2,如果當(dāng)前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不一直等待就結(jié)束了;
synchronized的鎖可重入、不可中斷、非公平,Lock鎖可重入、可判斷、可公平(兩者皆可);
Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
55. synchronized 和 ReentrantLock 區(qū)別是什么?
synchronized是關(guān)鍵字,ReentrantLock是類
ReentrantLock可以對獲取鎖的等待時間進(jìn)行設(shè)置,這樣就避免了死鎖?
ReentrantLock可以獲取各種鎖的信息,可以靈活地實(shí)現(xiàn)多路通知?
鎖機(jī)制不同:ReentrantLock底層調(diào)用的是Unsafe的park方法加鎖,synchronized操作的應(yīng)該是對象頭中mark word。
4、反射
通過字節(jié)碼對象,通過反射,獲取構(gòu)造方法,成員方法,成員變量等信息。
還可以通過反射創(chuàng)建對象
暴力反射Declared獲取私有屬性,私有方法
57. 什么是反射?
反射主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力
Java反射:
在Java運(yùn)行時環(huán)境中,對于任意一個類,知道這個類有哪些屬性和方法,對于任意一個對象,能否調(diào)用它的任意一個方法
Java反射機(jī)制主要提供了以下功能
在運(yùn)行時判斷任意一個對象所屬的類,類所具有的成員變量和方法。調(diào)用任意一個對象的方法。
58. 什么是 java 序列化?什么情況下需要序列化?
分為序列化和反序列化,將我們的對象保存在磁盤當(dāng)中,然后再從磁盤中讀取對象。
基于序列化和反序列化的一個應(yīng)用-對象的克隆
什么情況下需要序列化:
a)當(dāng)你想把的內(nèi)存中的對象狀態(tài)保存到一個文件中或者數(shù)據(jù)庫中時候;
b)當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時候;c)當(dāng)你想通過RMI傳輸對象的時候;
59. 動態(tài)代理是什么?有哪些應(yīng)用?
動態(tài)代理:當(dāng)想要給實(shí)現(xiàn)了某個接口的類中的方法,加一些額外的處理。比如說加日志,加事務(wù)等。就是創(chuàng)建一個新的類,這個類不僅包含原來類方法的功能,還添加了額外處理的。這個代理類并不是定義好的,是動態(tài)生成的。具有解耦意義,靈活,擴(kuò)展性強(qiáng)。
動代理的應(yīng)用:Spring的AOP、加事務(wù),加權(quán)限,加日志
60. 怎么實(shí)現(xiàn)動態(tài)代理?
首先必須定義一個接口,還要有一個InvocationHandler(將實(shí)現(xiàn)接口的類的對象傳遞給它)處理類。再有一個工具類Proxy(習(xí)慣性將其稱為代理類,因?yàn)檎{(diào)用他的newInstance()可以產(chǎn)生代理對象,其實(shí)他只是一個產(chǎn)生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進(jìn)制碼,利用加載器加載,并將其實(shí)例化產(chǎn)生代理對象,最后返回。
5、Cookies、Session
67. session 和 cookie 有什么區(qū)別?
Session.典型的場景比如購物車,當(dāng)你點(diǎn)擊下單按鈕時,由于HTTP協(xié)議無狀態(tài),并不知道是哪個用戶操作的,所以服務(wù)端創(chuàng)建了特定的Session,用用于標(biāo)識這個用戶,并且跟蹤用戶,這樣才知道購物車?yán)锩嬗袔妆緯?。這個Session是保存在服務(wù)端的,有一個唯一標(biāo)識sessionId。Session 信息都是放在內(nèi)存的。
思考一下服務(wù)端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發(fā)送相應(yīng)的Cookie信息到服務(wù)端。實(shí)際上大多數(shù)的應(yīng)用都是用 Cookie 來實(shí)現(xiàn)Session跟蹤的,第一次創(chuàng)建Session的時候,服務(wù)端會在HTTP協(xié)議中告訴客戶端,需要在 Cookie 里面記錄一個Session ID,以后每次請求把這個會話ID發(fā)送到服務(wù)器,我就知道你是誰了。
用戶登錄信息賬號可以寫到Cookie里面,方便登錄,Session是在服務(wù)端保存的一個數(shù)據(jù)結(jié)構(gòu),用來跟蹤用戶的狀態(tài),這個數(shù)據(jù)可以保存在集群、數(shù)據(jù)庫、文件中;
Cookie是客戶端保存用戶信息的一種機(jī)制,用來記錄用戶的一些信息,也是實(shí)現(xiàn)Session的一種方式。
session存儲的是在服務(wù)器上面的 cookie是存放在客戶端上的(也就是所謂的瀏覽器上)
1. Session稱為“會話控制"
2. Session可以存儲用戶信息.
3. Session數(shù)據(jù)的生命周期是整個會話,如果會話關(guān)閉 則數(shù)據(jù)清空.
4. Session的數(shù)據(jù)結(jié)構(gòu) 是key-value結(jié)構(gòu).
1. Cookie是一個小型文本文件,存儲到本地終端上.
2. Cookie可以存儲用戶信息.
3. Cookie的數(shù)據(jù)類型key-value
4. Cookie中的數(shù)據(jù)一般采用加密的方式保存
5. Cookie的數(shù)據(jù)可以"永久"保存.
Session和Cookie選擇
1.如果數(shù)據(jù)需要臨時保存,則選用Session, 如果數(shù)據(jù)需要長時間存儲選用Cookie.
2.如果對于安全性要求較高的數(shù)據(jù),選用Session,如果安全性要求不高選用Cookie.
問題: 財(cái)務(wù)系統(tǒng)用戶信息選用什么技術(shù)保存信息? 選用session.
68. 說一下 session 的工作原理?
session是一個存在服務(wù)器上的類似于一個散列表格的文件。類似于一個大號的map,里面的鍵存儲的是用戶的sessionid,用戶向服務(wù)器發(fā)送請求的時候會帶上這個sessionid。這時就可以從中取出對應(yīng)的值了。
69. 如果客戶端禁止 cookie 能實(shí)現(xiàn) session 還能用嗎?不能
Cookie與 Session,一般認(rèn)為是兩個獨(dú)立的東西,Session采用的是在服務(wù)器端保持狀態(tài)的方案,Cookie采用的是在客戶端保持狀態(tài)的方案。但因?yàn)镾ession是用Session ID來確定當(dāng)前對話所對應(yīng)的服務(wù)器Session,而Session ID是通過Cookie來傳遞的,禁用Cookie相當(dāng)于失去了Session ID,也就得不到Session了。
71. 如何避免 sql 注入?
sql語句中出現(xiàn)了特殊符號#,改變了SQL語句
獲取新的傳輸器:Prepare Statement(簡單又有效的方法)
使用正則表達(dá)式過濾傳入的參數(shù)
字符串過濾
6、異常
74. throw 和 throws 的區(qū)別?
throw則是指拋出的一個具體的異常類型。
throws是用來聲明一個方法可能拋出的所有異常信息,不處理異常、異常往上傳,誰調(diào)誰處理
75. final、finally、finalize 有什么區(qū)別?
- final關(guān)鍵字用來修飾類、變量、方法--表示最終
- finally關(guān)鍵字一般作用在try-catch代碼塊中,將一定要執(zhí)行的代碼方法finally代碼塊中,
- finalize是一個方法,屬于Object類的一個方法,一般由垃圾回收器來調(diào)用,當(dāng)我們調(diào)用System的gc()方法的時候,由垃圾回收器調(diào)用finalize(),回收垃圾。
76. try-catch-finally 中哪個部分可以省略?
答:catch 可以省略
原因:
定義普通異常的時候需要捕獲catch,進(jìn)行進(jìn)一步處理。
運(yùn)行時異常,catch就可以省略
至于加上finally,則是在不管有沒捕獲異常,都要進(jìn)行的“掃尾”處理。
77、Error和Exception都是繼承于Throwable
error是系統(tǒng)錯誤
- Error類一般是指與虛擬機(jī)相關(guān)的問題,如系統(tǒng)崩潰,虛擬機(jī)錯誤,內(nèi)存空間不足,方法調(diào)用棧溢出等。如java.lang.StackOverFlowError和Java.lang.OutOfMemoryError。對于這類錯誤,Java編譯器不去檢查他們。對于這類錯誤的導(dǎo)致的應(yīng)用程序中斷,僅靠程序本身無法恢復(fù)和預(yù)防,遇到這樣的錯誤,建議讓程序終止
- Exception類表示程序可以處理的異常,可以捕獲且可能恢復(fù)。遇到這類異常,應(yīng)該盡可能處理異常,使程序恢復(fù)運(yùn)行,而不應(yīng)該隨意終止異常。
- Exception又分為運(yùn)行時異常(Runtime Exception)和受檢查的異常(Checked Exception )。
- RuntimeException:其特點(diǎn)是Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時,即使沒有用try……catch捕獲,也沒有用throws拋出,還是會編譯通過,如除數(shù)為零的ArithmeticException、錯誤的類型轉(zhuǎn)換、數(shù)組越界訪問和試圖訪問空指針等。處理RuntimeException的原則是:如果出現(xiàn)RuntimeException,那么一定是程序員的錯誤。
- 受檢查的異常(IOException等):這類異常如果沒有try……catch也沒有throws拋出,編譯是通不過的。這類異常一般是外部錯誤,例如文件找不到、試圖從文件尾后讀取數(shù)據(jù)等,這并不是程序本身的錯誤,而是在應(yīng)用環(huán)境中出現(xiàn)的外部錯誤。
數(shù)據(jù)庫
78、數(shù)據(jù)庫中存儲的數(shù)據(jù)類型有哪些
varchar、int、date、text
Mysql中的
整數(shù)類型:int、big int、small int、Tiny int 、bit、bool、medium int
int(m)里的m是表示SELECT查詢結(jié)果集中的顯示寬度,并不影響實(shí)際的取值范圍
浮點(diǎn)數(shù)類型:float、double、decimal
浮點(diǎn)型在數(shù)據(jù)庫中存放的是近似值,而定點(diǎn)類型在數(shù)據(jù)庫中存放的是精確值。
字符串類型:char、varchar、text、tinytext、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB二進(jìn)制數(shù)據(jù)(_Blob)TEXT以文本方式存儲,英文存儲區(qū)分大小寫,而_Blob是以二進(jìn)制方式存儲,不分大小寫。
日期類型:Date、DateTime、TimeStamp、Time、Year
其他數(shù)據(jù)類型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等
redis數(shù)據(jù)庫中的數(shù)據(jù)類型
string字符串:統(tǒng)計(jì)網(wǎng)站訪問數(shù)量,當(dāng)前在線人數(shù),文章字?jǐn)?shù)
散列hash:是一個鍵值(key=>value)對集合,存儲對象,讀取、修改用戶屬性(name,age,pwd等)
列表list:可重復(fù)、有序,簡單的字符串列表,實(shí)現(xiàn)熱銷榜,最新評論
集合set :不可重復(fù)、無序,利用交集求共同好友。利用唯一性,可以統(tǒng)計(jì)訪問網(wǎng)站的所有獨(dú)立IP
有序集合zset:不可重復(fù),zset 和 set 一樣也是string類型元素的集合,大型在線游戲的積分排行榜
79.Redis的五種數(shù)據(jù)類型和適用的場景
1.String(數(shù)據(jù)緩存)
2.Hash(購物車 單點(diǎn)登錄 用戶信息 登錄信息)
3.List(評論 評分 有序可重)
4.Set(共同好友 無序不重)
5.Zset(最熱商品)
80.什么是緩存穿透?如何避免?什么是緩存雪崩?何如避免?
緩存穿透?一般的緩存系統(tǒng),都是按照key去緩存查詢,如果不存在對應(yīng)的value,就應(yīng)該去后端系統(tǒng)查找(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對后端系統(tǒng)造成很大的壓力。這就叫做緩存穿透。
如何避免?對查詢結(jié)果為空的情況也進(jìn)行緩存,緩存時間設(shè)置短一點(diǎn),或者該key對應(yīng)的數(shù)據(jù)insert了之后清理緩存。 2:對一定不存在的key進(jìn)行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。
緩存雪崩?當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給后端系統(tǒng)帶來很大壓力。導(dǎo)致系統(tǒng)崩潰。
如何避免?
1:在緩存失效后,通過加鎖或者隊(duì)列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。
2:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設(shè)置為短期,A2設(shè)置為長期
3:不同的key,設(shè)置不同的過期時間,讓緩存失效的時間點(diǎn)盡量均勻。
81.Redis實(shí)現(xiàn)分布式鎖
Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關(guān)系Redis中可以使用SETNX命令實(shí)現(xiàn)分布式鎖。
將 key 的值設(shè)為 value ,當(dāng)且僅當(dāng) key 不存在。 若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作
82.有沒有嘗試進(jìn)行多機(jī)redis 的部署?如何保證數(shù)據(jù)一致的?
主從復(fù)制,讀寫分離
一類是主數(shù)據(jù)庫(master)一類是從數(shù)據(jù)庫(slave),主數(shù)據(jù)庫可以進(jìn)行讀寫操作,當(dāng)發(fā)生寫操作的時候自動將數(shù)據(jù)同步到從數(shù)據(jù)庫,而從數(shù)據(jù)庫一般是只讀的,并接收主數(shù)據(jù)庫同步過來的數(shù)據(jù),一個主數(shù)據(jù)庫可以有多個從數(shù)據(jù)庫,而一個從數(shù)據(jù)庫只能有一個主數(shù)據(jù)庫。
?
?
總結(jié)
- 上一篇: 今天
- 下一篇: pthread 简要使用指南