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

第1章

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

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

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

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

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

答:#將傳入的數據都當成一個字符串,會對傳入的數據自動加上引號;$將傳入的數據直接顯示生成在 SQL 中。注意:使用$占位符可能會導致 SQL 注射攻擊,能用#的地方就不要使用$,寫 order by 子句的時候應該用$而不是#。

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

答:在大型項目中,可能存在大量的 SQL 語句,這時候為每個 SQL 語句起一個唯一的標識(ID)就變得并不容易了。為了解決這個問題,在 MyBatis 中,可以為每個映射文件起一個唯一的命名空間,這樣定義在這個映射文件中的每個 SQL 語句就成了定義在這個命名空間中的一個ID。只要我們能夠保證每個命名空間中這個 ID 是唯一的,即使在不同映射文件中的語句 ID 相同,也不會再產生沖突了。

145、MyBatis 中的動態 SQL 是什么意思?

答:對于一些復雜的查詢,我們可能會指定多個查詢條件,但是這些條件可能存在也可能不存在,例如在 58 同城上面找房子,我們可能會指定面積、樓層和所在位置來查找房源,也可能會指定面積、價格、戶型和所在位置來查找房源,此時就需要根據用戶指定的條件動態生成 SQL 語句。如果不使用持久層框架我們可能需要自己拼裝 SQL 語句,還好 MyBatis 提供了動態 SQL 的功能來解決這個問題。MyBatis 中用于實現動態 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>

當然也可以像下面這些書寫。

<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>

再看看下面這個例子。

<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 是如何實現的?

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

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

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

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

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

補充:設計模式中的創建型模式中也有一個原型模式,原型模式也是一個常用的模式,例如做一個室內設計軟件,所有的素材都在工具箱中,而每次從工具箱中取出的都是素材對象的一個原型,可以通過對象克隆來實現原型模式。

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

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

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

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

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

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

ThreadLocal 是如何做到為每一個線程維護一份獨立的變量副本的呢?在 ThreadLocal 類中有一個 Map ,鍵為線程對象,值是其線程對應的變量的副本,自己要模擬實現一個 ThreadLocal 類其實并不困難,代碼如下所示: 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)指一種程序設計范型,該范型以一種稱為切面(aspect)的語言構造為基礎,切面是一種新的模塊化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscutting concern)。

149、你是如何理解“橫切關注“這個概念的?

答:“橫切關注“是會影響到整個應用程序的關注功能,它跟正常的業務邏輯是正交的,沒有必然的聯系,但是幾乎所有的業務邏輯都會涉及到這些關注功能。通常,事務、日志、安全性等關注就是應用中的橫切關注功能。

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

答:

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

切點(Pointcut):如果連接點相當于數據中的記錄,那么切點相當于查詢條件,一個切點可以匹配多個連接點。Spring AOP 的規則解析引擎負責解析切點所設定的查詢條件,找到對應的連接點。

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

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

引介(Introduction):引介是一種特殊的增強,它為類添加一些屬性和方法。這樣,即使一個業務類原本沒有實現某個接口,通過引介功能,可以動態的未該業務類添加接口的實現邏輯,讓業務類成為這個接口的實現類。

織入(Weaving):織入是將增強添加到目標類具體連接點上的過程,AOP 有三種織入方式:①編譯期織入:需要特殊的 Java 編譯期(例如 AspectJ 的 ajc);②裝載期織入:要求使用特殊的類加載器,在裝載類的時候對類進行增強;③運行時織入:在運行時為目標類生成代理實現增強。Spring 采用了動態代理的方式實現了運行時織入,而 AspectJ 采用了編譯期織入和裝載期織入的方式。

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

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

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

/**

*參考人員接口

*/

public interface Candidate {

/**

答題

*/

public void answerTheQuestions();

}

/**

*懶學生

*@author 駱昊

*

*/

public class LazyStudent implements Candidate {

private String name;//姓名

public LazyStudent(String name){

this.name = name;

}

@Override

public void answerTheQuestions(){

//懶學生只能寫出自己的名字不會答題

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

}

}

/**

槍手

@author 駱昊

*/

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

public Gunman(Candidate target){

this.target = target;

}

@Override

public void answerTheQuestions(){

//槍手要寫上代考的學生的姓名

target.answerTheQuestions();

//槍手要幫助懶學生答題并交卷

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 提供了動態代理技術,允許開發者在運行時創建接口的代理實例,主要包括 Proxy 類和 InvocationHandler 接口。下面的例子使用動態代理為 ArrayList 編寫一個代理,在添加和刪除元素時,在控制臺打印添加或刪除的元素以及 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 的動態代理有一個局限性就是代理的類必須要實現接口,雖然面向接口編程是每個優秀的 Java 程序都知道的規則,但現實往往不盡如人意,對于沒有實現接口的類如何為其生成代理呢?繼承!繼承是最經典的擴展已有代碼能力的手段,雖然繼承常常被初學者濫用,但繼承也常常被進階的程序員忽視。CGLib 采用非常底層的字節碼生成技術,通過為一個類創建子類來生成代理,它彌補了 Java 動態代理的不足,因此 Spring 中動態代理和 CGLib 都是創建代理的重要手段,對于實現了接口的類就用動態代理為其生成代理類,而沒有實現接口的類就用 CGLib 通過繼承的方式為其創建代理。

全書完
為你推薦
玄鑒仙族

陸江仙熬夜猝死,殘魂卻附在了一面滿是裂痕的青灰色銅鏡上,飄落到了浩瀚無垠的修仙世界。兇險難測的大黎山,眉尺河旁小小的村落,一個小家族拾到了這枚鏡子,于是傳仙道授仙法,開啟波瀾壯闊的新時代。(家族修仙,不圣母,種田,無系統,群像文)

季越人 128萬讀過
青山

飛光飛光,勸爾一杯酒。吾不識青山高,黃地厚。唯見月寒日暖,來煎人壽。

會說話的肘子 47.8萬讀過
撈尸人

人知鬼恐怖,鬼曉人心毒。這是一本傳統靈異小說。

純潔滴小龍 31.2萬讀過
沒錢修什么仙?

老者:“你想報仇?”少年:“我被強者反復侮辱,被師尊視為垃圾,我怎么可能不想報仇?”老者摸了摸少年的腦袋,嘆道:“好孩子,我來傳功給你吧。”少年驚道:“前輩!這怎么行?”老者伸出手:“把你手機給我。”少年看著手機上的變化,震驚道:“前輩!這哪里來的百年功力?”老者微微一笑:“好孩子,這是你在天庭的備用功力,以后急用的時候隨用隨取,別再被人侮辱了。”少年皺眉:“這不是法力貸嗎?我怕……”老者:“天庭是大平臺,新用戶借百年功力有30天免息,日息最低半天功力,還沒你吐納一周天多。”……張羽冷哼一聲,關掉了上面的廣告。

熊狼狗 20萬讀過
從斬妖除魔開始長生不死

消耗壽元灌注武學,可無限進行推演。沈儀凡人之軀,壽數不過百年,所幸可以通過斬殺妖魔獲取對方剩余壽元。在邪祟遍地的亂世中亮出長刀,讓這群活了千百年的生靈肝膽俱裂!從【鷹爪功】到【八荒裂天手】,從【伏魔拳】到【金身鎮獄法相】!沈儀偶爾也會沉思,這壽命怎么越用越多了?他收刀入鞘,抬眸朝天上看去,聽聞那云端之上有天穹玉府,其內坐滿了千真萬圣,任何一位都曾經歷無盡歲月。此番踏天而來,只為向諸仙借個百萬年,以證我長生不死大道。……此書又名《讓你氪命練武,你氪別人的?》、《道友請留步,你的壽元與在下有緣》。

陸月十九 67.6萬讀過
主站蜘蛛池模板: 印江| 观塘区| 张掖市| 尼勒克县| 建宁县| 炎陵县| 项城市| 太白县| 通海县| 浦北县| 邵阳县| 安吉县| 宜丰县| 宁河县| 汉寿县| 大港区| 永靖县| 彭泽县| 蒙城县| 基隆市| 宕昌县| 孝感市| 博湖县| 天门市| 拉萨市| 揭阳市| 佳木斯市| 若尔盖县| 晋州市| 报价| 南城县| 广元市| 长武县| 永登县| 历史| 绥江县| 清水县| 四川省| 弥勒县| 太和县| 治县。|