一、Java基礎(chǔ)
1、Java中兩種數(shù)據(jù)類型(為后面進一步提問做鋪墊)
(1)基本數(shù)據(jù)類型,分為boolean、byte、int、char、long、short、double、float;
(2)引用數(shù)據(jù)類型 ,分為數(shù)組、類、接口。
擴展:Java中引入了基本數(shù)據(jù)類型,但是為了能夠?qū)⑦@些基本數(shù)據(jù)類型當(dāng)成對象操作,Java為每一個基本數(shù)據(jù)類型都引入了對應(yīng)的包裝類型(wrapper class),int的包裝類就是Integer,從Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉(zhuǎn)換。
基本數(shù)據(jù)類型: boolean,char,byte,short,int,long,float,double
封裝類類型: Boolean,Character,Byte,Short,Integer,Long,F(xiàn)loat,Double
2、java中==和eqauls()的區(qū)別
==分兩類分析,既可以比較基本類型也可以比較引用類型,對于基本類型來說是比較的數(shù)值。對于引用類型來說比較的內(nèi)存地址值;equals是屬于java.lang.Object類里面的方法,Object里的equals里默認的是雙等于==。分兩種情況討論:一種是自定義類,看自定義類有沒有重寫equals方法,通常情況下,如果重寫了equals則比較的是類中相應(yīng)屬性是否相等。如果沒有重寫equals方法,則仍然使用==比較的是地址。
總結(jié)、:
== :
基本類型:比較值是否相等
引用類型:比較的就是內(nèi)存地址是否相同
equals :
引用類型:默認情況下,比較的是地址值。可以進行重寫,使其比較對象的值是否相等。
3、說說int和Integer有何區(qū)別
(1)Integer是int的包裝類;int是基本數(shù)據(jù)類型;
(2)Integer變量必須實例化后才能使用;int變量不需要;
(3)Integer實際是對象的引用,指向此new的Integer對象;int是直接存儲數(shù)據(jù)值 ;
(4)Integer的默認值是null;int的默認值是0。
4、switch中能否使用string類型的參數(shù)作為變量(涉及到j(luò)dk版本的理解)
在JDK1.7之前,switch只能支持byte、short、char、int、float、double或者其對應(yīng)的封裝類以及Enum類型。JDK1.7開始支持String。當(dāng)字符串不會頻繁改變時可以用枚舉來代替String。
5、說明ArrayList和LinkedList的區(qū)別和優(yōu)缺點,在哪些場景會使用?
區(qū)別:
(1)ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList是基于鏈表結(jié)構(gòu);
(2)對于隨機訪問的get和set方法,ArrayList要優(yōu)于LinkedList,因為LinkedList要移動指針;
(3)對于新增和刪除操作add和remove,LinkedList比較占優(yōu)勢,因為ArrayList要移動數(shù)據(jù);
各自優(yōu)缺點:
(1)對ArrayList和LinkedList而言,在列表末尾增加一個元素所花的開銷都是固定的。對ArrayList而言,主要是在內(nèi)部數(shù)組中增加一項,指向所添加的元素,偶爾可能會導(dǎo)致對數(shù)組重新進行分配;而對LinkedList而言,這個開銷是 統(tǒng)一的,分配一個內(nèi)部Entry對象。
(2)在ArrayList集合中添加或者刪除一個元素時,當(dāng)前的列表移動元素后面所有的元素都會被移動。而LinkedList集合中添加或者刪除一個元素的開銷是固定的。
(3)LinkedList集合不支持 高效的隨機隨機訪問(RandomAccess),因為可能產(chǎn)生二次項的行為。
(4)ArrayList的空間浪費主要體現(xiàn)在在list列表的結(jié)尾預(yù)留一定的容量空間,而LinkedList的空間花費則體現(xiàn)在它的每一個元素都需要消耗相當(dāng)?shù)目臻g。
應(yīng)用場景:
ArrayList使用在查詢比較多,但是插入和刪除比較少的情況,而LinkedList用在查詢比較少而插入刪除比較多的情況
6、實現(xiàn)多線程的幾種方式,多線程的應(yīng)用場景有哪些?
(1)、繼承Thread類,重寫run方法
(2)、實現(xiàn)Runnable接口,重寫run方法。【可以避免由于Java的單繼承特性而帶來的局限。適合多個線程去處理同一資源的情況】
(3)、實現(xiàn)Callable接口,重寫call方法?!居蟹祷刂担试S拋出異?!?/p>
(4)、使用線程池【減少創(chuàng)建新線程的時間,重復(fù)利用線程池中線程,降低資源消耗,可有返回值】
7、${}和#{}的區(qū)別
使用#{}:
(1)、傳入?yún)?shù),sql在解析的時候會加上" ",當(dāng)成字符串來解析,如 id = "id";
(2)、#{}能夠很大程度上防止sql注入;
使用${}:
(1)、傳入數(shù)據(jù)直接顯示在生成的sql中,sql在解析的時候值為id = id。
(2)、${}方式無法防止sql注入
最后:能用#{}時盡量用#{},但有些場合需要使用$。
注意MyBatis排序時使用order by 動態(tài)參數(shù)時需要注意,用$而不是#(#會自動拼接符號)
8、描述一下JVM加載class文件的原理機制和特點。
Java中的所有類,都需要由類加載器裝載到JVM中才能運行。JVM中類的加載是由類加載器(ClassLoader)和它的子類來實現(xiàn)的。在我們使用一個類之前,JVM需要先將該類的字節(jié)碼文件(.class文件)從磁盤、網(wǎng)絡(luò)或其他來源加載到內(nèi)存中(加載Class文件到JVM),并對字節(jié)碼進行解析生成對應(yīng)的Class對象,這就是類加載器的功能。我們可以利用類加載器,實現(xiàn)類的動態(tài)加載。在寫程序的時候,我們幾乎不需要關(guān)心類的加載,因為這些都是隱式裝載的,除非我們有特殊的用法,像是反射,就需要顯式的加載所需要的類。
Java類的加載是動態(tài)的,它并不會一次性將所有類全部加載后再運行,而是保證程序運行的基礎(chǔ)類(像是基類)完全加載到j(luò)vm中,至于其他類,則在需要的時候才加載。這當(dāng)然就是為了節(jié)省內(nèi)存開銷。
進一步提問:類加載的方式有幾種?區(qū)別是什么?
類裝載方式有兩類:隱式裝載和顯示裝載,其中顯示裝載又分2種方式。
(1)、隱式裝載,程序在運行過程中當(dāng)碰到通過new 等方式生成對象時,隱式調(diào)用類裝載器加載對應(yīng)的類到j(luò)vm中,比如
Dog dog = new Dog();【第一種方式】
(2)、顯式裝載,
【第二種方式】使用Class.forName()通過反射加載類型,并創(chuàng)建對象實例,比如
Class clazz = Class.forName(“Dog”);
? Object dog =clazz.newInstance();
如果無法找到Dog,則拋出ClassNotFoundException。
【第三種方式】使用某個ClassLoader實例的loadClass()方法
Class clazz = classLoader.loadClass("Dog");
Object dog=clazz.newInstance();
如果無法找到Dog,則拋出ClassNotFoundException。
區(qū)別:
方式1和2使用的類加載器是相同的,都是當(dāng)前類加載器(即:this.getClass.getClassLoader)。
方式3由用戶指定類加載器。如果需要在當(dāng)前類路徑以外尋找類,則只能采用方式3。即第3種方式加載的類與當(dāng)前類分屬不同的命名空間。
方式1是靜態(tài)加載,方式2和3是動態(tài)加載。
進一步提問:java內(nèi)置的類加載器(ClassLoader)有哪些,簡述一下類加載器工作原理
Java的類加載器有三個:
第一種是Bootstrap Loader(引導(dǎo)類加載器)。它的實現(xiàn)依賴于底層操作系統(tǒng),由C編寫而成,沒有繼承于ClassLoader類。根類加載器從系統(tǒng)屬性sun.boot.class.path所指定的目錄中加載類庫。默認為jre目錄下的lib目錄下的class文件,該加載器沒有父加載器。負責(zé)加載虛擬機的核心類庫,如java.lang.*。Object類就是由根類加載器加載的。
第二種是Extended Loader(標(biāo)準(zhǔn)擴展類加載器)。它的父加載器為根類加載器。由java編寫而成,是ClassLoader的子類。它從java.ext.dirs中加載類庫,或者從JDK安裝目錄jre\lib\ext子目錄下加載類庫。如果把用戶創(chuàng)建的jar文件放在該目錄下,也會自動由擴展類加載器加載。
第三種是AppClass Loader(應(yīng)用程序類路徑類加載器)。它的父加載器為擴展類加載器。由java編寫而成,是ClassLoader的子類,它從環(huán)境變量classpath或者系統(tǒng)屬性java.class.path所指定的目錄中加載類,是用戶自定義的類加載器的默認父加載器。
加載類時,會以Bootstrap Loader→Extended Loader→AppClass Loader的順序來尋找類,如果找不到,就會丟出NoClassDefFoundError。
9、在一個類中,聲明了若干個static方法和非static方法,聲明的static方法能否直接訪問聲明的非static方法?
static方法不能直接訪問非static方法,因為static方法是屬于這個類本身的一個方法,在編譯期間就已經(jīng)確定了;而非static方法是屬于這個類的對象的方法,需要在實例化之后才能訪問到。即:static方法調(diào)用時不需要創(chuàng)建對象,可以直接調(diào)用,非static方法是要與對象關(guān)聯(lián)在一起的,必須創(chuàng)建一個對象后,才可以在該對象上進行方法調(diào)用,若在static方法中訪問非static方法,非static方法不知道關(guān)聯(lián)到哪個對象上,將不能通過編譯。
進一步提問:說一下靜態(tài)方法和非靜態(tài)方法都是在什么時候被裝載到內(nèi)存中的?
靜態(tài)方法(Static Method)與靜態(tài)成員變量一樣,屬于類本身,在類裝載的時候被裝載到內(nèi)存(Memory),不自動進行銷毀,會一直存在于內(nèi)存中,直到JVM關(guān)閉。
非靜態(tài)方法(Non-Static Method)又叫實例化方法,屬于實例對象,實例化后才會分配內(nèi)存,必須通過類的實例來引用。不會常駐內(nèi)存,當(dāng)實例對象被JVM 回收之后,也跟著消失。
進一步提問:靜態(tài)方法怎樣訪問非靜態(tài)方法?
靜態(tài)方法不能直接使用本類的非靜態(tài)方法
解決方式有三種
(1)、兩個方法都改成非靜態(tài)
(2)、兩個方法都改成靜態(tài)
(3)、先創(chuàng)建類的實例,然后靜態(tài)方法再調(diào)用這個實例的非靜態(tài)方法
10、在Java中,對象什么時候可以被垃圾回收?
Java垃圾回收不是實時的,垃圾回收器的作用是查找和回收(清理)無用的對象。以便讓JVM更有效的使用內(nèi)存。垃圾回收器的運行時間是不確定的,由JVM決定,在運行時是間歇執(zhí)行的。也可以通過System.gc()來強制回收垃圾,但是這個命令下達后JVM不一定會立即響應(yīng)執(zhí)行,但間隔一小段時間基本都會執(zhí)行。
11、一個漢字占幾個字節(jié)
中文在不同編碼下占不定長的 2~4個字節(jié)。注意在utf-16中占用兩個字節(jié),在java 運行時用UTF-16編碼在轉(zhuǎn)碼的時候會在前面加上表示字節(jié)順序的字符,這個字符稱為”零寬度非換行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。FEFF占用兩個字節(jié)。

12、項目中使用Maven進行構(gòu)建,有哪些優(yōu)勢?你使用過哪些maven命令?說一下maven中本地倉庫和遠程倉庫的區(qū)別和聯(lián)系。
優(yōu)點:
(1)創(chuàng)建項目,自動關(guān)聯(lián)和下載依賴的jar包,統(tǒng)一維護jar包
(2)升級框架版本方便
常用 Maven 命令:
(1)、安裝項目到本地倉庫:mvn install
(2)、創(chuàng)建maven項目:mvn archetype:generate
(3)、驗證項目是否正確:mvn validate
(4)、maven 打包:mvn package
(5)、只打jar包:mvn jar:jar
(6)、生成源碼jar包:mvn source:jar
(7)、產(chǎn)生應(yīng)用需要的任何額外的源代碼:mvn generate-sources
(8)、編譯源代碼: mvn compile
(9)、編譯測試代碼:mvn test-compile
(10)、運行測試:mvn test
(11)、運行檢查:mvn verify
(12)、清理maven項目:mvn clean
Maven倉庫關(guān)系:
maven的倉庫只有兩大類:1.本地倉庫 2.遠程倉庫,在遠程倉庫中又分成了3種:2.1 中央倉庫 2.2 私服 2.3 其它公共庫
運行Maven的時候,Maven所需要的任何構(gòu)件都是直接從本地倉庫獲取的。如果本地倉庫沒有,它會首先嘗試從遠程倉庫下載構(gòu)件至本地倉庫,然后再使用本地倉庫的構(gòu)件。
13、Thread.sleep(0)有沒有實際作用
觸發(fā)操作系統(tǒng)立刻重新進行一次CPU競爭,操作系統(tǒng)重新計算線程的優(yōu)先級(包括當(dāng)前線程)。競爭的結(jié)果也許是當(dāng)前線程仍然獲得CPU控制權(quán),也許會換成別的線程獲得CPU控制權(quán)。
二、前端框架
1、在項目中使用過哪些前端框架?
Vue(用于構(gòu)建用戶界面的 漸進式框架,特點:輕量級、雙向數(shù)據(jù)綁定、組件化)、
React(構(gòu)建用戶界面的 JAVASCRIPT 庫,特點:只負責(zé)顯示、聲明式框架、數(shù)據(jù)驅(qū)動DOM)、
Angular(前端JS框架,核心:MVVM、模塊化、自動化雙向數(shù)據(jù)綁定、語義化標(biāo)簽、依賴注入等)、
QucikUI(企業(yè)級web前端開發(fā)解決方案)、
Layui(前端 UI 框架)、
Avalon(前端MVVM框架)、
還有Dojo、Ember、Aurelia等
2、js中如何查看某變量的數(shù)據(jù)類型?可以查看的數(shù)據(jù)類型有哪些?
使用typeof查看變量的數(shù)據(jù)類型。
javascript共有6種數(shù)據(jù)類型:
基本類型5種:number、string、boolean、null、undefined。引用類型1種:object
typeof檢測返回6種:undefined、boolean、string、number、object、function
3、前端進行文件下載時,能不能用ajax向后端發(fā)起請求?
Ajax不能實現(xiàn)文件下載功能
原因:ajax的返回值是json,text,html,xml類型,或者可以說ajax的接收類型只能是String字符串,不是流類型,所以無法實現(xiàn)文件下載。但用aja仍然可以獲得文件的內(nèi)容(可以讀取到返回的response,但只是讀取而已),該文件將被保留在內(nèi)存中,無法將文件保存到磁盤,這是因為javascript無法和磁盤進行交互,否則這會是一個嚴(yán)重的安全問題,js無法調(diào)用瀏覽器的下載處理機制和程序,會被瀏覽器阻塞。
4、如何解決前后端交互過程中特殊字符的傳參(比如中文、特殊符號等)?
(這個問題屬于送分題,有一定項目經(jīng)驗的人,都會注意到在前后端進行交互時,需要進行decoder編碼-Encoder解碼的過程,防止亂碼)
進一步提問:前后端數(shù)組傳參如何處理?
前端:數(shù)據(jù)使用JSON.stringify(str)處理
后端:數(shù)據(jù)轉(zhuǎn)換:List<Object> objectList = JSONObject.parseArray(str, Object.class)
5、FreeMarker、jsp、html 三者的區(qū)別
先說說freemarker和jsp的不同,運行機制就不大一樣,jsp是編譯成繼承自servlet的class文件,運行jsp就是運行一個servlet(Java文件編譯后會產(chǎn)生一個class文件,最終執(zhí)行的就是這個class文件,JSP也一樣,它也要編譯成class文件。JSP不止要編譯,它還得要轉(zhuǎn)譯,首先把JSP轉(zhuǎn)譯成一個Servlet文件,然后再編譯成class文件。當(dāng)用戶訪問JSP時就執(zhí)行了class文件)。
而freemarker就是套模板,通過模板+內(nèi)容直接生成HTML然后輸出。
HTML(Hypertext Markup Language)文本標(biāo)記語言,它是靜態(tài)頁面,和JavaScript一樣是解釋性語言。
JSP(Java Server Page)Java服務(wù)端的頁面,它是動態(tài)頁面,它是需要經(jīng)過JDK編譯后把內(nèi)容發(fā)給客戶端去顯示。
6、vue實例內(nèi)部和外部分別怎么調(diào)用vue中的方法?
內(nèi)部調(diào)用: this.operate();
外部調(diào)用:vm.operate(); (vm是vue實例名)
三、后端技術(shù)
1、Spring:
(1)Spring中IOC和AOP的應(yīng)用場景。
AOP:面向切面編程??梢赃\用在日志,事務(wù)和異常處理等。如果不使用aop,那么就必須在每個類和方法中去實現(xiàn)它們。代碼糾纏在一起。每個類和方法中都包含日志、事務(wù)或者異常處理甚至是業(yè)務(wù)邏輯。在一個這樣的方法中,很難分清代碼中實際做的是什么處理。AOP 所做的就是將所有散落各處的事務(wù)代碼集中到一個事務(wù)切面中。
AOP日志處理:使用Aop在接口方法上插入一行自定義的切面注解類,在切面處理類中可以記錄接口名稱、請求參數(shù)、請求ip、請求url、請求時間、響應(yīng)參數(shù)、響應(yīng)狀態(tài)、調(diào)用時長等;
AOP事務(wù)處理:Spring在方法訪問數(shù)據(jù)庫之前,自動開啟事務(wù),當(dāng)訪問數(shù)據(jù)庫結(jié)束之后,自動提交/回滾事務(wù);
AOP異常處理:自定義開啟環(huán)繞通知,一旦運行接口報錯,環(huán)繞通知捕獲異常跳轉(zhuǎn)異常處理頁面。
IOC就是Inversion of Control,即控制反轉(zhuǎn),又稱依賴注入。它不是什么技術(shù),而是一種設(shè)計思想。在Java開發(fā)中,傳統(tǒng)的創(chuàng)建對象的方法是直接通過 new 關(guān)鍵字(之前我們通過 "類名 對象名 = new 類名( )"的方式進行對象的創(chuàng)建,也就是說我們的程序負責(zé)對象的創(chuàng)建,控制了它是否被創(chuàng)建這件事情,這就叫做控制),而 spring 則是通過 IOC 容器來創(chuàng)建對象,也就是說我們將創(chuàng)建對象的控制權(quán)交給了 IOC 容器。這稱為控制反轉(zhuǎn)。概括的說就是:
IOC 讓程序員不再關(guān)注怎么去創(chuàng)建對象,而是關(guān)注于對象創(chuàng)建之后的操作,把對象的創(chuàng)建、初始化、銷毀等工作交給spring容器來做。
舉個例子:梳理這個問題在各種社會形態(tài)里如何解決:一個人(Java實例,調(diào)用者)需要一把斧子(Java實例,被調(diào)用者)
(1) 原始社會里,幾乎沒有社會分工。需要斧子的人(調(diào)用者)只能自己去磨一把斧子(被調(diào)用者)。對應(yīng)的情形為:Java程序里的調(diào)用者自己創(chuàng)建被調(diào)用者。
(2)進入工業(yè)社會,工廠出現(xiàn)。斧子不再由普通人完成,而在工廠里被生產(chǎn)出來,此時需要斧子的人(調(diào)用者)找到工廠,購買斧子,無須關(guān)心斧子的制造過程。對應(yīng)Java程序的簡單工廠的設(shè)計模式。
(3)進入“按需分配”社會,需要斧子的人不需要找到工廠,坐在家里發(fā)出一個簡單指令:需要斧子。斧子就自然出現(xiàn)在他面前。對應(yīng)Spring的依賴注入。
第一種情況下,Java實例的調(diào)用者創(chuàng)建被調(diào)用的Java實例,必然要求被調(diào)用的Java類出現(xiàn)在調(diào)用者的代碼里。無法實現(xiàn)二者之間的松耦合。
第二種情況下,調(diào)用者無須關(guān)心被調(diào)用者具體實現(xiàn)過程,只需要找到符合某種標(biāo)準(zhǔn)(接口)的實例,即可使用。此時調(diào)用的代碼面向接口編程,可以讓調(diào)用者和被調(diào)用者解耦,這也是工廠模式大量使用的原因。但調(diào)用者需要自己定位工廠,調(diào)用者與特定工廠耦合在一起。
第三種情況下,調(diào)用者無須自己定位工廠,程序運行到需要被調(diào)用者時,系統(tǒng)自動提供被調(diào)用者實例。事實上,調(diào)用者和被調(diào)用者都處于Spring的管理下,二者之間的依賴關(guān)系由Spring提供。
生活中這種例子比比皆是,支付寶在整個淘寶體系里就是龐大的ioc容器,交易雙方之外的第三方資金管理中心。
(2)Spring依賴注入的方式有哪些?
【set設(shè)值注入、構(gòu)造函數(shù)注入、spring注解注入】
Spring IOC既可以通過XML的形式進行bean與依賴注入配置,也可以通過注解的方式。(在springmvc中,我們一般使用xml進行裝配,而springboot使用全注解的形式)
①通過XML的形式進行bean與依賴注入
通常有兩種: 設(shè)值注入&構(gòu)造注入。
設(shè)值注入就是指要被注入的類中定義有一個setter()方法,并在參數(shù)中定義需要注入的對象。
構(gòu)造注入就是指要被注入的類中聲明一個構(gòu)造方法,并在此方法的參數(shù)中定義要注入的對象。
②注解的方式:
注解包含三部分:
| 組件類型注解--聲明當(dāng)前類的功能與職責(zé)
|| 自動裝配注解--根據(jù)屬性特征自動注入對象
||| 元數(shù)據(jù)注解--更細化的輔助IoC容器管理對象的注解
A、四種組件類型注解
@Component:組件注解,通用注解,該注解描述的類將被IoC容器管理并實例化
@Controller:語義注解,說明當(dāng)前類是MVC應(yīng)用中的控制類
@Service:語義注解,說明當(dāng)前類是Service業(yè)務(wù)服務(wù)類
@Repository:語義注解,說明當(dāng)前類作用于業(yè)務(wù)持久層,通常描述對應(yīng)Dao類
此外,在使用四種組件類型的注解時,必須開啟組件掃描,詳細配置如下:
B、兩類自動裝配注解
按類型裝配
@Autowired
@Inject
按名稱裝配
@Named
@Resource
優(yōu)先設(shè)置name屬性,若未包含name屬性,會按照@Autowired注入
C、元數(shù)據(jù)注解
@Primary--按類型裝配時出現(xiàn)多個相同類型的對象,擁有此注解對象優(yōu)先被注入
@PostContruct:相當(dāng)于init-method
@PreDestory:相當(dāng)于destory--method
@Scope:設(shè)置對象Scope屬性
@Value:為屬性注入靜態(tài)數(shù)據(jù)
(3)為什么非使用依賴注入,我要用到一個其他對象時,new一個怎么就不好了。
本質(zhì)上都是創(chuàng)建對象,最大的區(qū)別還是生命周期的管理以及復(fù)雜依賴的處理。
①、生命周期
比如一個類或者接口全程只要一個實例,用依賴注入的話只需要注冊成單例即可,如果自己實例化的話你需要擼一個單機模式(餓漢、懶漢、線程安全等模式)的類,并發(fā)下還要考慮線程安全。
②、復(fù)雜依賴
如果這個類或者接口不依賴其他的類或者接口差異不明顯,如果依賴的類比較多的情況下(A依賴B,B又依賴C,C又依賴D,D又依賴其他)自己實例化會很麻煩。要創(chuàng)建A, 要先B、C、D先new一遍再new A。用ioc就快多了,A(B b),其他自動創(chuàng)建,是不是快多了。
總結(jié):在程序中如果不是必須同一個對象多個實例時,也就是一個對象只是在某個地方使用一下時new一下,依賴注入就比new一個對象更好,因為new一個對象必選面臨頻繁創(chuàng)建和銷毀內(nèi)存實例對象的問題。而ioc管控下實例對象都是單例模式的,就是在程序運行時始終只有一個對象實例生成不需要頻繁創(chuàng)建和銷毀,也因為在內(nèi)存中只有一個實例對象,減少內(nèi)存開銷。
(4)描述一下DispatcherServlet的工作流程?
(5)SpringMVC如何區(qū)分控制器返回的是頁面還是數(shù)據(jù)(比如JSON格式的數(shù)據(jù))
使用@ResponseBody注解,該注解用于將Controller方法返回的對象,通過適當(dāng)?shù)腍ttpMessageConverter轉(zhuǎn)化為指定格式后,寫入到Response對象的body數(shù)據(jù)區(qū)。
使用時機:返回的數(shù)據(jù)不是html標(biāo)簽的頁面,而是其他某種格式的數(shù)據(jù)時(如json,xml等)。(如果是在程序中返回的html頁面代碼,也可以使用@ResponseBody,在HttpServletResponse寫入,設(shè)置ContentType為text/html)
(6)Spring全家桶有哪些?
Spring、Spring MVC、Spring Boot、Spring Cloud 、 Spring Security 、Spring Data
(7)Spring普通類與工具類如何調(diào)用service層方法,為什么不能直接使用注解調(diào)用?
Spring中的Service不是你想new就能new的,因為通過new實例化的對象脫離了Spring容器的管理,獲取不到注解的屬性值,所以會是null,就算調(diào)用service的類中有@Component注解加入了Spring容器管理,也還是null。
新建SpringContextUtil類,在application.xml配置SpringContextUtil,最后使用DictService dictService = (DictService) SpringContextUtil.getBean("dictService");
2、Springboot:
(1) SpringBoot中如何進行單元測試?
導(dǎo)入spring-boot-starter-test依賴。測試類使用注解@SpringBootTest,測試的方法上加@Test注解。
(2) SpringBootApplication注解的作用。
@SpringBootApplication注解是一個組合注解,@SpringBootApplication注解的源碼我們發(fā)現(xiàn),它是由ComponentScan、SpringBootConfiguration、EnableAutoConfiguration等注解組合而成:
(3) 在一個Springboot+mybatis+mysql+oracle+redis+aop功能的項目中,在pom.xml中需要引入哪些jar包依賴?
進一步提問:以redis為例,springboot 1.x與springboot 2.x引入的jar包有何不同?
在 springboot 1.5.x版本的默認的Redis客戶端是Jedis實現(xiàn)的,springboot 2.x版本中默認客戶端是用lettuce實現(xiàn)的。
3、SpringCloud:
(1)springCloud的核心組件有哪些,解決什么問題?
Eureka(注冊中心)
每個微服務(wù)都有一個EurekaClient組件,專門負責(zé)將這個服務(wù)的信息注冊到EurekaServer中,也就是告訴EurekaServer,自己在哪臺機器上,監(jiān)聽著哪個端口。而EurekaServer是一個注冊中心,里面有一個注冊表,保存了各服務(wù)所在的機器和端口號。
Feign(REST客戶端)
Feign是一個聲明式REST客戶端,主要是為了簡便服務(wù)調(diào)用,更快捷、優(yōu)雅地調(diào)用HTTPAPI。主要是實現(xiàn)原理是用動態(tài)代理,你要是調(diào)用哪個接口,本質(zhì)就是調(diào)用Feign創(chuàng)建的動態(tài)代理。
Ribbon(負載均衡)
Ribbon的作用是負載均衡,會幫你在每次請求時選擇一臺機器,均勻地把請求分發(fā)到各個機器上,默認使用的最經(jīng)典的RoundRobin輪詢算法(如果發(fā)起10次請求,那就先讓你請求第1臺機器、然后是第2臺機器、第3臺機器,接著再來—個循環(huán),第1臺機器、第2臺機器。。。以此類推)
Hystrix(熔斷器)
微服務(wù)框架是許多服務(wù)互相調(diào)用的,要是不做任何保護的話,某一個服務(wù)掛了,就會引起連鎖反應(yīng),導(dǎo)致別的服務(wù)也掛。Hystrix是隔離、熔斷以及降級的一個框架。如果調(diào)用某服務(wù)報錯或者掛了,就對該服務(wù)熔斷,在5分鐘內(nèi)請求此服務(wù)直接就返回一個默認值,不需要每次都卡幾秒,這個過程,就是所謂的熔斷。但是熔斷了之后就會少調(diào)用一個服務(wù),此時需要做下標(biāo)記,標(biāo)記本來需要做什么業(yè)務(wù),但是因為服務(wù)掛了,暫時沒有做,等熔斷的服務(wù)恢復(fù)了,就可以手工處理這些業(yè)務(wù)。這個過程,就是所謂的降級。
Zuul(服務(wù)網(wǎng)關(guān))
Zuul微服務(wù)網(wǎng)關(guān),負責(zé)網(wǎng)絡(luò)路由。假設(shè)你后臺部署了幾百個服務(wù),現(xiàn)在有個前端兄弟要來調(diào)用這些服務(wù),難不成你讓他把所有服務(wù)的名稱和地址全部記住,這是不現(xiàn)實的,所以一般微服務(wù)架構(gòu)中都必然會設(shè)計一個網(wǎng)關(guān),所有請求都往網(wǎng)關(guān)走,網(wǎng)關(guān)會根據(jù)請求中的一些特征,將請求轉(zhuǎn)發(fā)給后端的各個服務(wù)。而且有一個網(wǎng)關(guān)之后,還有很多好處,比如可以做統(tǒng)一的降級、限流、認證授權(quán)、安全,等等。
總結(jié)步驟:①服務(wù)注冊—》②服務(wù)發(fā)現(xiàn)—》③負載均衡—》④服務(wù)調(diào)用—》⑤隔離、熔斷與降級—》⑥網(wǎng)關(guān)路由
流程說明:各個服務(wù)啟動時,Eureka Client都會將服務(wù)注冊到Eureka Server,并且Eureka Client還可以反過來從Eureka Server拉取注冊表,從而知道其他服務(wù)在哪里。服務(wù)間發(fā)起請求的時候,基于Ribbon做負載均衡,從一個服務(wù)的多臺機器中選擇一臺?;贔eign的動態(tài)代理機制,根據(jù)注解和選擇的機器,拼接請求URL地址,發(fā)起請求。發(fā)起請求是通過Hystrix的線程池來走的,不同的服務(wù)走不同的線程池,實現(xiàn)了不同服務(wù)調(diào)用的隔離,避免了服務(wù)雪崩的問題。如果前端、移動端要調(diào)用后端系統(tǒng),統(tǒng)一從Zuul網(wǎng)關(guān)進入,由Zuul網(wǎng)關(guān)轉(zhuǎn)發(fā)請求給對應(yīng)的服務(wù)。
(2)SpringCloud和Dubbo兩種微服務(wù)架構(gòu)有何區(qū)別?
Dubbo的定位始終是一款RPC框架,而SpringCloud的目標(biāo)是微服務(wù)架構(gòu)下的一站式解決方案。如果非要比較的話,Dubbo可以類比到NetflixOSS技術(shù)棧,而SpringCloud集成了NetflixOSS作為分布式服務(wù)治理解決方案,但除此之外SpringCloud還提供了配置、消息、安全、調(diào)用鏈跟蹤等分布式問題解決方案。
(3)SpringBoot和SpringCloud 側(cè)重點分別在哪些方面?
SpringBoot是Spring的一套快速配置腳手架,可以基于SpringBoot快速開發(fā)單個微服務(wù),SpringCloud是一個基于SpringBoot實現(xiàn)的云應(yīng)用開發(fā)工具;
SpringBoot專注于快速、方便集成的單個微服務(wù)個體,SpringCloud關(guān)注全局的服務(wù)治理框架;
SpringBoot使用了默認大于配置的理念,很多集成方案已經(jīng)幫你選擇好了,能不配置就不配置,SpringCloud很大的一部分是基于SpringBoot來實現(xiàn)。SpringBoot可以離開SpringCloud獨立使用開發(fā)項目,但是SpringCloud離不開SpringBoot,屬于依賴的關(guān)系。
(4)springcloud如何實現(xiàn)服務(wù)的注冊和調(diào)用?
1)服務(wù)發(fā)布時,指定對應(yīng)的服務(wù)名(服務(wù)名包括了IP地址和端口),將服務(wù)注冊到注冊中心(eureka 或者zookeeper)
2)注冊中心加@EnableEurekaServer,服務(wù)用@EnableDiscoveryClient,然后用ribbon或feign進行服務(wù)直接的調(diào)用發(fā)現(xiàn)。(這一過程是springcloud自動實現(xiàn) 只需要在main方法添加@EnableDisscoveryClient。同一個服務(wù)修改端口就可以啟動多個實例)
4、mybatis:
(1)Mybatis中mapper.xml映射文件,通常都會寫一個Mapper接口與之對應(yīng),這個Mapper層接口是怎么能夠找到指定xml下的方法的?
Mapper接口是沒有實現(xiàn)類的,當(dāng)調(diào)用接口方法時,接口全限名(就是映射文件中的namespace的值)+方法名拼接字符串作為key值,可唯一定位一個MapperStatement。在Mybatis中,每一個、、、標(biāo)簽,都會被解析為一個MapperStatement對象。
(2)Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重復(fù)?
不同的Xml映射文件,如果配置了namespace,那么id可以重復(fù);如果沒有配置namespace,那么id不能重復(fù);原因就是namespace+id是作為Map的key使用的,如果沒有namespace,就剩下id,那么,id重復(fù)會導(dǎo)致數(shù)據(jù)互相覆蓋。有了namespace,自然id就可以重復(fù),namespace不同,namespace+id自然也就不同。
(3)Mybatis中使用MySQL和Oracle分頁的區(qū)別
MySQL分頁:(利用LIMIT關(guān)鍵字)計算參數(shù)為開始序號(startNum),要查的總條數(shù)(totalNum)
Oracle分頁:(利用自帶的rownum)計算參數(shù)為開始序號(startNum),結(jié)束序號(endNum)【注意:Oracle分頁利用其自帶的rownum,但是rownum在表中不能使用>號(比如select rownum,a.* from A a where rownum > n,查出的都是空),但是可以使用n(n>1的自然數(shù))這種條件依舊不成立,所以查不到記錄】
(4)Mybatis是如何將sql執(zhí)行結(jié)果封裝為目標(biāo)對象并返回的?
第一種是使用resultMap標(biāo)簽,逐一定義列名和對象屬性名之間的映射關(guān)系。
第二種是使用sql列的別名功能,將列別名書寫為對象屬性名,比如T_NAME AS NAME,對象屬性名一般是name,小寫,但是列名不區(qū)分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應(yīng)對象屬性名,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常工作。
進一步提問:resultMap和resultType有啥區(qū)別?
resultType:當(dāng)使用resultType做SQL語句返回結(jié)果類型處理時,對于SQL語句查詢出的字段在相應(yīng)的pojo中必須有和它相同的字段對應(yīng),而resultType中的內(nèi)容就是pojo在本項目中的位置。
resultMap:當(dāng)使用resultMap做SQL語句返回結(jié)果類型處理時,通常需要先在mapper.xml中定義resultMap進行pojo和相應(yīng)表字段的對應(yīng)關(guān)系。然后再使用resultMap。
(5)Mybatis xml映射文件中,除了常見的select、insert、updae、delete標(biāo)簽之外,還有哪些標(biāo)簽及其作用?
除了這四個標(biāo)簽,還有<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,一個9個標(biāo)簽,其中<sql>為sql片段標(biāo)簽,通過<include>標(biāo)簽引入sql片段,<selectKey>為不支持自增的主鍵生成策略標(biāo)簽。
(6)為什么說Mybatis是半自動ORM映射工具?它與全自動的區(qū)別在哪里?
hibernate對很多數(shù)據(jù)庫的操作已經(jīng)進行了封裝,hibernate操作對象時,比如往數(shù)據(jù)庫添加一條記錄,直接save就可以了。(Hibernate屬于全自動ORM映射工具,使用Hibernate查詢關(guān)聯(lián)對象或者關(guān)聯(lián)集合對象時,可以根據(jù)對象關(guān)系模型直接獲?。鳰ybatis在查詢關(guān)聯(lián)對象或關(guān)聯(lián)集合對象時,需要手動編寫sql來完成,雖然現(xiàn)在已經(jīng)有不少程序可以自動生成xml文件,但還是需要自己調(diào)整sql,所以稱之為半自動ORM映射工具。這也從側(cè)面可以看出hibernate的拓展性不如Mybatis(hibernate做了很多封裝)。
(7)mybatis 為什么大于不用轉(zhuǎn)義,小于必須轉(zhuǎn)義?
mybatis不支持“<”,本質(zhì)是xml不支持這個符號,<會引起xml格式的錯誤,xml文件中的標(biāo)簽是?<…>?這種形式的, 所以當(dāng)出現(xiàn) “<” 號時, 會認為是一個標(biāo)簽的開始。