官术网_书友最值得收藏!

最新章節(jié)

書友吧

第1章

141、Hibernate 中 DetachedCriteria 類是做什么的?

答:DetachedCriteria 和 Criteria 的用法基本上是一致的,但 Criteria 是由 Session 的createCriteria()方法創(chuàng)建的,也就意味著離開創(chuàng)建它的 Session,Criteria 就無法使用了。 DetachedCriteria 不需要 Session 就可以創(chuàng)建(使用 DetachedCriteria.forClass()方法創(chuàng)建),所以通常也稱其為離線的 Criteria,在需要進(jìn)行查詢操作的時(shí)候再和 Session 綁定(調(diào)用其 getExecutableCriteria(Session)方法),這也就意味著一個(gè) DetachedCriteria 可以在需要的時(shí)候和不同的 Session 進(jìn)行綁定。

142、@OneToMany 注解的 mappedBy 屬性有什么作用?

答:@OneToMany 用來配置一對多關(guān)聯(lián)映射,但通常情況下,一對多關(guān)聯(lián)映射都由多的一方來維護(hù)關(guān)聯(lián)關(guān)系,例如學(xué)生和班級(jí),應(yīng)該在學(xué)生類中添加班級(jí)屬性來維持學(xué)生和班級(jí)的關(guān)聯(lián)關(guān)系(在數(shù)據(jù)庫中是由學(xué)生表中的外鍵班級(jí)編號(hào)來維護(hù)學(xué)生表和班級(jí)表的多對一關(guān)系),如果要使用雙向關(guān)聯(lián),在班級(jí)類中添加一個(gè)容器屬性來存放學(xué)生,并使用@OneToMany 注解進(jìn)行映射,此時(shí) mappedBy 屬性就非常重要。如果使用 XML 進(jìn)行配置,可以用<set>標(biāo)簽的inverse=“true“設(shè)置來達(dá)到同樣的效果。

143、MyBatis 中使用#和$書寫占位符有什么區(qū)別?

答:#將傳入的數(shù)據(jù)都當(dāng)成一個(gè)字符串,會(huì)對傳入的數(shù)據(jù)自動(dòng)加上引號(hào);$將傳入的數(shù)據(jù)直接顯示生成在 SQL 中。注意:使用$占位符可能會(huì)導(dǎo)致 SQL 注射攻擊,能用#的地方就不要使用$,寫 order by 子句的時(shí)候應(yīng)該用$而不是#。

144、解釋一下 MyBatis 中命名空間(namespace)的作用。

答:在大型項(xiàng)目中,可能存在大量的 SQL 語句,這時(shí)候?yàn)槊總€(gè) SQL 語句起一個(gè)唯一的標(biāo)識(shí)(ID)就變得并不容易了。為了解決這個(gè)問題,在 MyBatis 中,可以為每個(gè)映射文件起一個(gè)唯一的命名空間,這樣定義在這個(gè)映射文件中的每個(gè) SQL 語句就成了定義在這個(gè)命名空間中的一個(gè)ID。只要我們能夠保證每個(gè)命名空間中這個(gè) ID 是唯一的,即使在不同映射文件中的語句 ID 相同,也不會(huì)再產(chǎn)生沖突了。

145、MyBatis 中的動(dòng)態(tài) SQL 是什么意思?

答:對于一些復(fù)雜的查詢,我們可能會(huì)指定多個(gè)查詢條件,但是這些條件可能存在也可能不存在,例如在 58 同城上面找房子,我們可能會(huì)指定面積、樓層和所在位置來查找房源,也可能會(huì)指定面積、價(jià)格、戶型和所在位置來查找房源,此時(shí)就需要根據(jù)用戶指定的條件動(dòng)態(tài)生成 SQL 語句。如果不使用持久層框架我們可能需要自己拼裝 SQL 語句,還好 MyBatis 提供了動(dòng)態(tài) SQL 的功能來解決這個(gè)問題。MyBatis 中用于實(shí)現(xiàn)動(dòng)態(tài) SQL 的元素主要有:- if

- choose / when / otherwise

- trim

- where

- set

- foreach

下面是映射文件的片段。

<select id=“foo“ parameterType=“Blog“ resultType=“Blog“> select * from t_blog where 1 = 1

<if test=“title != null“>

and title =#{title}

</if>

<if test=“content != null“>

and content =#{content}

</if>

<if test=“owner != null“>

and owner =#{owner}

</if>

</select>

當(dāng)然也可以像下面這些書寫。

<select id=“foo“ parameterType=“Blog“ resultType=“Blog“> select * from t_blog where 1 = 1

<choose>

<when test=“title != null“>

and title =#{title}

</when>

<when test=“content != null“>

and content =#{content}

</when>

<otherwise>

and owner =“owner1“

</otherwise>

</choose>

</select>

再看看下面這個(gè)例子。

<select id=“bar“ resultType=“Blog“> select * from t_blog where id in <foreach collection=“array“ index=“index“

item=“item“ open=“(“ separator=“,“ close=“)“>#{item}

</foreach>

</select>

146、什么是 IoC 和 DI?DI 是如何實(shí)現(xiàn)的?

答:IoC 叫控制反轉(zhuǎn),是 Inversion of Control 的縮寫,DI(Dependency Injection)叫依賴注入,是對 IoC 更簡單的詮釋。控制反轉(zhuǎn)是把傳統(tǒng)上由程序代碼直接操控的對象的調(diào)用權(quán)交給容器,通過容器來實(shí)現(xiàn)對象組件的裝配和管理。所謂的“控制反轉(zhuǎn)“就是對組件對象控制權(quán)的轉(zhuǎn)移,從程序代碼本身轉(zhuǎn)移到了外部容器,由容器來創(chuàng)建對象并管理對象之間的依賴關(guān)系。IoC 體現(xiàn)了好萊塢原則-“Don’t call me, we will call you“。依賴注入的基本原則是應(yīng)用組件不應(yīng)該負(fù)責(zé)查找資源或者其他依賴的協(xié)作對象。配置對象的工作應(yīng)該由容器負(fù)責(zé),查找資源的邏輯應(yīng)該從應(yīng)用組件的代碼中抽取出來,交給容器來完成。DI 是對 IoC 更準(zhǔn)確的描述,即組件之間的依賴關(guān)系由容器在運(yùn)行期決定,形象的來說,即由容器動(dòng)態(tài)的將某種依賴關(guān)系注入到組件之中。

舉個(gè)例子:一個(gè)類 A 需要用到接口 B 中的方法,那么就需要為類 A 和接口 B 建立關(guān)聯(lián)或依賴關(guān)系,最原始的方法是在類 A 中創(chuàng)建一個(gè)接口 B 的實(shí)現(xiàn)類 C 的實(shí)例,但這種方法需要開發(fā)人員自行維護(hù)二者的依賴關(guān)系,也就是說當(dāng)依賴關(guān)系發(fā)生變動(dòng)的時(shí)候需要修改代碼并重新構(gòu)建整個(gè)系統(tǒng)。如果通過一個(gè)容器來管理這些對象以及對象的依賴關(guān)系,則只需要在類 A 中定義好用于關(guān)聯(lián)接口 B 的方法(構(gòu)造器或 setter 方法),將類 A 和接口 B 的實(shí)現(xiàn)類 C 放入容器中,通過對容器的配置來實(shí)現(xiàn)二者的關(guān)聯(lián)。

依賴注入可以通過 setter 方法注入(設(shè)值注入)、構(gòu)造器注入和接口注入三種方式來實(shí)現(xiàn), Spring 支持 setter 注入和構(gòu)造器注入,通常使用構(gòu)造器注入來注入必須的依賴關(guān)系,對于可選的依賴關(guān)系,則 setter 注入是更好的選擇,setter 注入需要類提供無參構(gòu)造器或者無參的靜態(tài)工廠方法來創(chuàng)建對象。

147、Spring 中 Bean 的作用域有哪些?

答:在 Spring 的早期版本中,僅有兩個(gè)作用域:singleton 和 prototype,前者表示 Bean 以單例的方式存在;后者表示每次從容器中調(diào)用 Bean 時(shí),都會(huì)返回一個(gè)新的實(shí)例,prototype通常翻譯為原型。

補(bǔ)充:設(shè)計(jì)模式中的創(chuàng)建型模式中也有一個(gè)原型模式,原型模式也是一個(gè)常用的模式,例如做一個(gè)室內(nèi)設(shè)計(jì)軟件,所有的素材都在工具箱中,而每次從工具箱中取出的都是素材對象的一個(gè)原型,可以通過對象克隆來實(shí)現(xiàn)原型模式。

Spring 2.x 中針對 WebApplicationContext 新增了 3 個(gè)作用域,分別是:request(每次 HTTP 請求都會(huì)創(chuàng)建一個(gè)新的 Bean)、session(同一個(gè) HttpSession 共享同一個(gè) Bean,不同的 HttpSession 使用不同的 Bean)和 globalSession(同一個(gè)全局 Session 共享一個(gè) Bean)。說明:單例模式和原型模式都是重要的設(shè)計(jì)模式。一般情況下,無狀態(tài)或狀態(tài)不可變的類適合使用單例模式。在傳統(tǒng)開發(fā)中,由于 DAO 持有 Connection 這個(gè)非線程安全對象因而沒有使用單例模式;但在 Spring 環(huán)境下,所有 DAO 類對可以采用單例模式,因?yàn)?Spring 利用 AOP 和 Java API 中的 ThreadLocal 對非線程安全的對象進(jìn)行了特殊處理。

ThreadLocal 為解決多線程程序的并發(fā)問題提供了一種新的思路。ThreadLocal,顧名思義是線程的一個(gè)本地化對象,當(dāng)工作于多線程中的對象使用 ThreadLocal 維護(hù)變量時(shí), ThreadLocal 為每個(gè)使用該變量的線程分配一個(gè)獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立的改變自己的副本,而不影響其他線程所對應(yīng)的副本。從線程的角度看,這個(gè)變量就像是線程的本地變量。

ThreadLocal 類非常簡單好用,只有四個(gè)方法,能用上的也就是下面三個(gè)方法:

- void set(T value):設(shè)置當(dāng)前線程的線程局部變量的值。

- T get():獲得當(dāng)前線程所對應(yīng)的線程局部變量的值。

- void remove():刪除當(dāng)前線程中線程局部變量的值。

ThreadLocal 是如何做到為每一個(gè)線程維護(hù)一份獨(dú)立的變量副本的呢?在 ThreadLocal 類中有一個(gè) Map ,鍵為線程對象,值是其線程對應(yīng)的變量的副本,自己要模擬實(shí)現(xiàn)一個(gè) ThreadLocal 類其實(shí)并不困難,代碼如下所示: import java.util.Collections;

import java.util.HashMap;

import java.util.Map;

public class MyThreadLocal<T>{

private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());

public void set(T newValue){ map.put(Thread.currentThread(), ewValue);

}

public T get(){

return map.get(Thread.currentThread());

}

public void remove(){

map.remove(Thread.currentThread());

}

}

148、解釋一下什么叫 AOP(面向切面編程)?

答:AOP(Aspect-Oriented Programming)指一種程序設(shè)計(jì)范型,該范型以一種稱為切面(aspect)的語言構(gòu)造為基礎(chǔ),切面是一種新的模塊化機(jī)制,用來描述分散在對象、類或方法中的橫切關(guān)注點(diǎn)(crosscutting concern)。

149、你是如何理解“橫切關(guān)注“這個(gè)概念的?

答:“橫切關(guān)注“是會(huì)影響到整個(gè)應(yīng)用程序的關(guān)注功能,它跟正常的業(yè)務(wù)邏輯是正交的,沒有必然的聯(lián)系,但是幾乎所有的業(yè)務(wù)邏輯都會(huì)涉及到這些關(guān)注功能。通常,事務(wù)、日志、安全性等關(guān)注就是應(yīng)用中的橫切關(guān)注功能。

150、你如何理解 AOP 中的連接點(diǎn)(Joinpoint)、切點(diǎn)(Pointcut)、增強(qiáng)(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?

答:

連接點(diǎn)(Joinpoint):程序執(zhí)行的某個(gè)特定位置(如:某個(gè)方法調(diào)用前、調(diào)用后,方法拋出異常后)。一個(gè)類或一段程序代碼擁有一些具有邊界性質(zhì)的特定點(diǎn),這些代碼中的特定點(diǎn)就是連接點(diǎn)。Spring 僅支持方法的連接點(diǎn)。

切點(diǎn)(Pointcut):如果連接點(diǎn)相當(dāng)于數(shù)據(jù)中的記錄,那么切點(diǎn)相當(dāng)于查詢條件,一個(gè)切點(diǎn)可以匹配多個(gè)連接點(diǎn)。Spring AOP 的規(guī)則解析引擎負(fù)責(zé)解析切點(diǎn)所設(shè)定的查詢條件,找到對應(yīng)的連接點(diǎn)。

增強(qiáng)(Advice):增強(qiáng)是織入到目標(biāo)類連接點(diǎn)上的一段程序代碼。Spring 提供的增強(qiáng)接口都是帶方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice 等。很多資料上將增強(qiáng)譯為“通知”,這明顯是個(gè)詞不達(dá)意的翻譯,讓很多程序員困惑了許久。

說明: Advice 在國內(nèi)的很多書面資料中都被翻譯成“通知“,但是很顯然這個(gè)翻譯無法表達(dá)其本質(zhì),有少量的讀物上將這個(gè)詞翻譯為“增強(qiáng)“,這個(gè)翻譯是對 Advice 較為準(zhǔn)確的詮釋,我們通過 AOP 將橫切關(guān)注功能加到原有的業(yè)務(wù)邏輯上,這就是對原有業(yè)務(wù)邏輯的一種增強(qiáng),這種增強(qiáng)可以是前置增強(qiáng)、后置增強(qiáng)、返回后增強(qiáng)、拋異常時(shí)增強(qiáng)和包圍型增強(qiáng)。

引介(Introduction):引介是一種特殊的增強(qiáng),它為類添加一些屬性和方法。這樣,即使一個(gè)業(yè)務(wù)類原本沒有實(shí)現(xiàn)某個(gè)接口,通過引介功能,可以動(dòng)態(tài)的未該業(yè)務(wù)類添加接口的實(shí)現(xiàn)邏輯,讓業(yè)務(wù)類成為這個(gè)接口的實(shí)現(xiàn)類。

織入(Weaving):織入是將增強(qiáng)添加到目標(biāo)類具體連接點(diǎn)上的過程,AOP 有三種織入方式:①編譯期織入:需要特殊的 Java 編譯期(例如 AspectJ 的 ajc);②裝載期織入:要求使用特殊的類加載器,在裝載類的時(shí)候?qū)︻愡M(jìn)行增強(qiáng);③運(yùn)行時(shí)織入:在運(yùn)行時(shí)為目標(biāo)類生成代理實(shí)現(xiàn)增強(qiáng)。Spring 采用了動(dòng)態(tài)代理的方式實(shí)現(xiàn)了運(yùn)行時(shí)織入,而 AspectJ 采用了編譯期織入和裝載期織入的方式。

切面(Aspect):切面是由切點(diǎn)和增強(qiáng)(引介)組成的,它包括了對橫切關(guān)注功能的定義,也包括了對連接點(diǎn)的定義。

補(bǔ)充:代理模式是 GoF 提出的 23 種設(shè)計(jì)模式中最為經(jīng)典的模式之一,代理模式是對象的結(jié)構(gòu)模式,它給某一個(gè)對象提供一個(gè)代理對象,并由代理對象控制對原對象的引用。簡單的說,代理對象可以完成比原對象更多的職責(zé),當(dāng)需要為原對象添加橫切關(guān)注功能時(shí),就可以使用原對象的代理對象。我們在打開 Office 系列的 Word 文檔時(shí),如果文檔中有插圖,當(dāng)文檔剛加載時(shí),文檔中的插圖都只是一個(gè)虛框占位符,等用戶真正翻到某頁要查看該圖片時(shí),才會(huì)真正加載這張圖,這其實(shí)就是對代理模式的使用,代替真正圖片的虛框就是一個(gè)虛擬代理; Hibernate 的 load 方法也是返回一個(gè)虛擬代理對象,等用戶真正需要訪問對象的屬性時(shí),才向數(shù)據(jù)庫發(fā)出 SQL 語句獲得真實(shí)對象。

下面用一個(gè)找槍手代考的例子演示代理模式的使用:

/**

*參考人員接口

*/

public interface Candidate {

/**

答題

*/

public void answerTheQuestions();

}

/**

*懶學(xué)生

*@author 駱昊

*

*/

public class LazyStudent implements Candidate {

private String name;//姓名

public LazyStudent(String name){

this.name = name;

}

@Override

public void answerTheQuestions(){

//懶學(xué)生只能寫出自己的名字不會(huì)答題

System.out.println(“姓名:“+ name);

}

}

/**

槍手

@author 駱昊

*/

public class Gunman implements Candidate { private Candidate target;//被代理對象

public Gunman(Candidate target){

this.target = target;

}

@Override

public void answerTheQuestions(){

//槍手要寫上代考的學(xué)生的姓名

target.answerTheQuestions();

//槍手要幫助懶學(xué)生答題并交卷

System.out.println(“奮筆疾書正確答案“);

System.out.println(“交卷“);

}

}

public class ProxyTest1 {

public static void main(String[] args){

Candidate c = new Gunman(new LazyStudent(“王小二“)); c.answerTheQuestions();

}

}

說明:從 JDK 1.3 開始,Java 提供了動(dòng)態(tài)代理技術(shù),允許開發(fā)者在運(yùn)行時(shí)創(chuàng)建接口的代理實(shí)例,主要包括 Proxy 類和 InvocationHandler 接口。下面的例子使用動(dòng)態(tài)代理為 ArrayList 編寫一個(gè)代理,在添加和刪除元素時(shí),在控制臺(tái)打印添加或刪除的元素以及 ArrayList 的大小:

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List;

public class ListProxy<T> implements InvocationHandler { private List<T> target;

public ListProxy(List<T> target){

this.target = target;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object retVal = null;

System.out.println(“[“+ method.getName()+“:“+ args[0]+“]“); retVal = method.invoke(target, args); System.out.println(“[size=“+ target.size()+“]“); return retVal;

}

}

import java.lang.reflect.Proxy;

import java.util.ArrayList;

import java.util.List;

public class ProxyTest2 {

@SuppressWarnings(“unchecked“)

public static void main(String[] args){ List<String> list = new ArrayList<String>(); Class<?> clazz = list.getClass();

ListProxy<String> myProxy = new ListProxy<String>(list); List<String> newList =(List<String>)

Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), myProxy);

newList.add(“apple“);

newList.add(“banana“);

newList.add(“orange“);

newList.remove(“banana“);

}

}

說明:使用 Java 的動(dòng)態(tài)代理有一個(gè)局限性就是代理的類必須要實(shí)現(xiàn)接口,雖然面向接口編程是每個(gè)優(yōu)秀的 Java 程序都知道的規(guī)則,但現(xiàn)實(shí)往往不盡如人意,對于沒有實(shí)現(xiàn)接口的類如何為其生成代理呢?繼承!繼承是最經(jīng)典的擴(kuò)展已有代碼能力的手段,雖然繼承常常被初學(xué)者濫用,但繼承也常常被進(jìn)階的程序員忽視。CGLib 采用非常底層的字節(jié)碼生成技術(shù),通過為一個(gè)類創(chuàng)建子類來生成代理,它彌補(bǔ)了 Java 動(dòng)態(tài)代理的不足,因此 Spring 中動(dòng)態(tài)代理和 CGLib 都是創(chuàng)建代理的重要手段,對于實(shí)現(xiàn)了接口的類就用動(dòng)態(tài)代理為其生成代理類,而沒有實(shí)現(xiàn)接口的類就用 CGLib 通過繼承的方式為其創(chuàng)建代理。

版權(quán):起點(diǎn)中文網(wǎng)

QQ閱讀手機(jī)版

主站蜘蛛池模板: 张家界市| 德惠市| 临澧县| 城步| 宣化县| 公主岭市| 湘阴县| 资阳市| 中牟县| 湟源县| 中卫市| 巴青县| 竹溪县| 吉水县| 河西区| 深圳市| 岳普湖县| 嫩江县| 滨海县| 武穴市| 平和县| 竹溪县| 泽普县| 思南县| 陆川县| 江华| 永靖县| 云龙县| 化德县| 改则县| 巨野县| 沁源县| 鄂伦春自治旗| 阜城县| 昂仁县| 新疆| 襄垣县| 衢州市| 宝坻区| 蒙自县| 江山市|