Java面試題集(151-180)

摘要:這部分包括了Spring、Spring MVC以及Spring和其它框架整合以及測試相關的內容,除此以外還包括了大型站點技術架構相關面試內容。html


151. Spring中的BeanFactory和ApplicationContext有什麼聯繫?前端

答:Spring經過配置文件描寫敘述Bean以及Bean之間的依賴關係。利用Java的反射機制實現Bean的實例化,並創建Bean之間的依賴關係,在此基礎上,Spring的IoC容器還提供了Bean實例緩存、生命週期管理、Bean實例代理、事件公佈、資源裝載等高級服務。BeanFactory是Spring框架最核心的接口。它提供了IoC容器的配置機制。ApplicationContext創建在BeanFactory之上。提供了不少其它面向應用的功能,包含對國際化和框架事件體系的支持。java

一般將BeanFactory稱爲IoC容器。而ApplicationContext稱爲應用上下文。前者更傾向於Spring自己。後者更傾向於開發人員,所以被使用得不少其它。web

【補充】反射(reflection)又叫自省(introspection),是得到對象或類型元數據的方法,Java反射機制可以在執行時推斷對象所屬的類。在執行時構造隨意一個類的對象。在執行時得到一個類的屬性和方法,在執行時調用對象的方法,或者生成動態代理。面試

在Java中,可以經過類的Class對象得到類的構造器、屬性、方法等類的元數據。還可以訪問這些屬性或調用這些方法,和反射相關的類還包含:spring

  1. Constructor:表明類的構造器的類。

    經過Class對象的getConstructors方法可以得到類的所有構造器的數組。Java 5之後的版本號還可以經過getConstrcutor(Class... parameterTypes)得到擁有特定參數的構造器對象。Constructor對象的一個主要方法是newInstance,經過該方法可以建立一個類的實例。數據庫

  2. Method:表明方法的類。

    經過Class對象的getDeclaredMethods方法可以得到所有方法的數組,Java 5之後的版本號還可以經過getDeclaredMethod(String name, Class... parameterTypes)得到特定簽名的方法,當中name是方法名,可變參數表明方法的參數列表。Method對象最重要的方法是invoke(Object obj, Object[] args),當中obj是調用該方法的目標對象。args是傳給方法的參數,這樣就可以調用指定對象的方法。此外,Method對象還包含下面重要方法: express

    • Class getReturnType():獲取方法的返回類型。
    • Class[] getParameterTypes():獲取方法參數類型的數組。
    • Class[] getExceptionTypes():獲取方法異常類型數組。
    • Annotation[][] getParameterAnnotations():得到方法參數註解信息的數組。
  3. Field:表明屬性的類。經過Class對象的getDeclaredFields()方法可以獲取類的屬性數組。

    經過getDeclaredField(String name)則可以獲取某個特定名稱的屬性。Field對象最重要的方法是set(Object obj, Object value),當中obj是目標對象,而value是要賦給屬性的值。apache

除此以外,Java還提供了Package類用於包的反射,Java 5之後的版本號還提供了AnnotationElement類用於註解的反射。總之,Java的反射機制保證了可以經過編程的方式訪問目標類或對象的所有元素,對於被private和protected訪問修飾符修飾的成員。僅僅要JVM的安全機制贊成,也可以經過反射進行調用。如下是一個反射的樣例:編程

Car.java

package com.lovo;  
  
public class Car {  
    private String brand;  
    private int currentSpeed;  
    private int maxSpeed;  
  
    public Car(String brand, int maxSpeed) {  
        this.brand = brand;  
        this.maxSpeed = maxSpeed;  
    }  
      
    public void run() {  
        currentSpeed += 10;  
        System.out.println(brand + " is running at " + currentSpeed + " km/h");  
        if(currentSpeed > maxSpeed) {  
            System.out.println("It's dangerous!");  
        }  
    }  
  
}  

CarTest.java

package com.lovo;  
  
import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.Method;  
  
public class CarTest {  
  
    public static void main(String[] args) throws Exception {  
        Constructor<Car> con = Car.class.getConstructor(String.class, int.class);  
        Car myCar = (Car) con.newInstance("Benz", 280);  
        Method m = myCar.getClass().getDeclaredMethod("run");  
        for(int i = 0; i < 10; i++) {  
            m.invoke(myCar);  
        }  
        Field f1 = myCar.getClass().getDeclaredField("maxSpeed");  
        Field f2 = myCar.getClass().getDeclaredField("brand");  
        f1.setAccessible(true);  
        f1.set(myCar, 80);  
        f2.setAccessible(true);  
        f2.set(myCar, "QQ");  
        m.invoke(myCar);  
    }  
}  


執行結果:



152. 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類很easy好用,僅僅有四個方法。能用上的也就是如下三個方法:

  • void set(T value):設置當前線程的線程局部變量的值。
  • T get():得到當前線程所相應的線程局部變量的值。
  • void remove():刪除當前線程中線程局部變量的值。

ThreadLocal是怎樣作到爲每一個線程維護一份獨立的變量副本的呢?在ThreadLocal類中有一個Map,鍵爲線程對象。值是其線程相應的變量的副本,本身要模擬實現一個ThreadLocal類事實上並不困難,代碼例如如下所看到的:

package com.lovo;  
  
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(), newValue);  
    }  
      
    public T get() {  
        return map.get(Thread.currentThread());  
    }  
      
    public void remove() {  
        map.remove(Thread.currentThread());  
    }  
}  


153. 你怎樣理解AOP中的鏈接點(Joinpoint)、切點(Pointcut)、加強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?

答:

  • 鏈接點:程序運行的某個特定位置(如:某個方法調用前、調用後,方法拋出異常後)。一個類或一段程序代碼擁有一些具備邊界性質的特定點。這些代碼中的特定點就是鏈接點。

    Spring僅支持方法的鏈接點。

  • 切點:假設鏈接點至關於數據中的記錄,那麼切點至關於查詢條件,一個切點可以匹配多個鏈接點。Spring AOP的規則解析引擎負責解析切點所設定的查詢條件,找到相應的鏈接點。

  • 加強:加強是織入到目標類鏈接點上的一段程序代碼。Spring提供的加強接口都是帶方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很是多資料上將加強譯爲「通知」,這明顯是個詞不達意的翻譯。讓很是多程序猿困惑了許久。
  • 引介:引介是一種特殊的加強,它爲類加入一些屬性和方法。這樣,即便一個業務類本來沒有實現某個接口,經過引介功能,可以動態的未該業務類加入接口的實現邏輯。讓業務類成爲這個接口的實現類。
  • 織入:織入是將加強加入到目標類詳細鏈接點上的過程。AOP有三種織入方式:
    1. 編譯期織入:需要特殊的Java編譯期(好比AspectJ的ajc)
    2. 類裝載期織入:要求使用特殊的類載入器
    3. 動態代理織入:在執行時爲目標類生成代理實現加強
Spring採用了動態代理織入,而AspectJ採用了編譯期織入和類裝載期織入的方式。
  • 切面:切面是由切點和加強(引介)組成的,它包含了對橫切關注功能的定義,也包含了對鏈接點的定義。

【補充】代理模式是GoF提出的23種設計模式中最爲經典的模式之中的一個,代理模式是對象的結構模式,它給某一個對象提供一個代理對象。並由代理對象控制對原對象的引用。簡單的說,代理對象可以完畢比原對象不少其它的職責,當需要爲原對象加入橫切關注功能時。就可以使用原對象的代理對象。咱們在打開Office系列的Word文檔時,假設文檔中有插圖,當文檔剛載入時,文檔中的插圖都僅僅是一個虛框佔位符,等用戶真正翻到某頁要查看該圖片時,纔會真正載入這張圖,這事實上就是對代理模式的使用。取代真正圖片的虛框就是一個虛擬代理;Hibernate的load方法也是返回一個虛擬代理對象,等用戶真正需要訪問對象的屬性時。才向數據庫發出SQL語句得到真實對象。

代理模式的類圖例如如下所看到的:


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

package com.lovo;  
  
/** 
 * 參考人員接口 
 */  
public interface Candidate {  
      
    /** 
     * 答題 
     */  
    public void answerTheQuestions();  
}  

package com.lovo;  
  
/** 
 * 懶學生 
 */  
public class LazyStudent implements Candidate {  
    private String name;        // 姓名  
      
    public LazyStudent(String name) {  
        this.name = name;  
    }  
      
    @Override  
    public void answerTheQuestions() {  
        // 懶學生僅僅能寫出本身的名字不會答題  
        System.out.println("姓名: " + name);  
    }  
  
}  

package com.lovo;  
  
/** 
 * 槍手 
 * 
 */  
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("交卷");  
    }  
  
}  


package com.lovo;  
  
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的大小:

package com.lovo;  
  
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;  
    }  
  
}  

package com.lovo;  
  
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經過繼承的方式爲其建立代理。


154. Spring中本身主動裝配的方式有哪些?

答:

  • no:不進行本身主動裝配,手動設置Bean的依賴關係
  • byName:依據Bean的名字進行本身主動裝配
  • byType:依據Bean的類型進行本身主動裝配
  • constructor:相似於byType。只是是應用於構造器的參數。假設正好有一個Bean與構造器的參數類型一樣則可以本身主動裝配,不然會致使錯誤
  • autodetect:假設有默認的構造器,則經過constructor的方式進行本身主動裝配,不然使用byType的方式進行本身主動裝配
【注意】本身主動裝配沒有本身定義裝配方式那麼精確。而且不能本身主動裝配簡單屬性(基本類型、字符串等)。在使用時應注意。


155. Spring中怎樣使用註解來配置Bean?有哪些相關的註解?

答:首先需要在Spring配置文件裏添加例如如下配置:

<context:component-scan base-package="org.example"/>  

而後可以用@Component、@Controller、@Service、@Repository註解來標註需要由Spring IoC容器進行對象託管的類。


156. Spring支持的事務管理類型有哪些?你在項目中使用哪一種方式?

答:Spring支持編程式事務管理和聲明式事務管理。不少Spring框架的用戶選擇聲明式事務管理,因爲這樣的方式和應用程序的關聯較少。所以更加符合輕量級容器的概念。聲明式事務管理要優於編程式事務管理。雖然在靈活性方面它弱於編程式事務管理(編程式事務贊成你經過代碼控制業務)。


157. 怎樣在Web項目中配置Spring的IoC容器?

答:假設需要在Web項目中使用Spring的IoC容器。可以在Web項目配置文件web.xml中作出例如如下配置:

<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>  
</context-param>  
  
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>  


158. 怎樣在Web項目中配置Spring MVC?

答:要使用Spring MVC需要在Web項目配置文件裏配置其前端控制器DispatcherServlet,例如如下所看到的:

<web-app>  
  
    <servlet>  
        <servlet-name>example</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
  
    <servlet-mapping>  
        <servlet-name>example</servlet-name>  
        <url-pattern>*.html</url-pattern>  
    </servlet-mapping>  
  
</web-app>  


【注意】上面的配置中使用了*.html的後綴映射,這樣作一方面不可以經過URL判斷採用了何種server端的技術,還有一方面可以欺騙搜索引擎,因爲搜索引擎不會搜索動態頁面。這樣的作法可以稱爲僞靜態化。


159. Spring MVC的工做原理是如何的?

答:Spring MVC的工做原理例如如下圖所看到的:


  1. client的所有請求都交給前端控制器DispatcherServlet來處理,它會負責調用系統的其它模塊來真正處理用戶的請求。
  2. DispatcherServlet收到請求後。將依據請求的信息(包括URL、HTTP協議方法、請求頭、請求參數、Cookie等)以及HandlerMapping的配置找處處理該請求的Handler(不論什麼一個對象都可以做爲請求的Handler)。

    固然,在這個地方Spring會經過HandlerAdapter對該處理器進行封裝,HandlerAdapter是一個適配器,它用統一的接口對各類Handler中的方法進行調用。

  3. Handler完畢對用戶請求的處理後。會返回一個ModelAndView對象給DispatcherServlet,ModelAndView顧名思義。包括了數據模型以及對應的視圖的信息。固然,這裏的視圖是邏輯視圖,DispatcherServlet還要藉助ViewResolver完畢從邏輯視圖到真實視圖對象的解析工做。
  4. 當獲得真正的視圖對象後。Dispatcher會利用視圖對象對模型數據進行渲染。
  5. client獲得響應。多是一個普通的HTML頁面,也可以是XML或JSON字符串,還可以是一張圖片或者一個PDF文件。

160. 怎樣在Spring IoC容器中配置數據源?

答:

  • DBCP配置:
<bean id="dataSource"  
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
    <property name="driverClassName" value="${jdbc.driverClassName}"/>  
    <property name="url" value="${jdbc.url}"/>  
    <property name="username" value="${jdbc.username}"/>  
    <property name="password" value="${jdbc.password}"/>  
</bean>  
  
<context:property-placeholder location="jdbc.properties"/>  


  • C3P0配置:
<bean id="dataSource"  
        class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
    <property name="driverClass" value="${jdbc.driverClassName}"/>  
    <property name="jdbcUrl" value="${jdbc.url}"/>  
    <property name="user" value="${jdbc.username}"/>  
    <property name="password" value="${jdbc.password}"/>  
</bean>  
  
<context:property-placeholder location="jdbc.properties"/>  


161. 怎樣配置配置事務加強?

答:

<?

xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- this is the service object that we want to make transactional --> <bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --> <tx:advice id="txAdvice" transaction-manager="txManager"> <!-- the transactional semantics... --> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true"/> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- ensure that the above transactional advice runs for any execution of an operation defined by the FooService interface --> <aop:config> <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> <!-- don't forget the DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </bean> <!-- similarly, don't forget the PlatformTransactionManager --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- other <bean/> definitions here --> </beans>


162. 選擇使用Spring框架的緣由(Spring框架爲企業級開發帶來的優勢)?

答:可以從下面幾個方面做答:

  1. 非侵入式:支持基於POJO的編程模式,不強制性的要求實現Spring框架中的接口或繼承Spring框架中的類。
  2. IoC容器:IoC容器幫助應用程序管理對象以及對象之間的依賴關係。對象之間的依賴關係假設發生了改變僅僅需要改動配置文件而不是改動代碼。因爲代碼的改動可能意味着項目的又一次構建和完整的迴歸測試。有了IoC容器。程序猿不再需要本身編寫工廠、單例,這一點特別符合Spring的精神「不要反覆的發明輪子」。
  3. AOP:面向切面編程。將全部的橫切關注功能封裝到切面(aspect)中。經過配置的方式將橫切關注功能動態加入到目標代碼上。進一步實現了業務邏輯和系統服務之間的分離。還有一方面。有了AOP程序猿可以省去很是多本身寫代理類的工做。
  4. MVC:Spring的MVC框架是很是優秀的,從各個方面都可以甩Struts 2幾條街。爲Web表示層提供了更好的解決方式。

  5. 事務管理:Spring以寬廣的胸懷接納多種持久層技術(我想這一點與Rod Johnson音樂學博士的身份不無關係,這一點特別值得Gavin King學習,上天不可能賦予一我的太多的長處以致於他沒有表達謙虛的餘地。關於這二位的軼事可以本身百度一下),並且爲其提供了聲明式的事務管理,在不需要不論什麼一行代碼的狀況下就可以完畢事務管理。

  6. 其它:選擇Spring框架的緣由還遠不止於此。Spring爲Java企業級開發提供了一站式選擇,你可以在需要的時候使用它的部分和全部,更重要的是,你甚至可以在感受不到Spring存在的狀況下。在你的項目中使用Spring提供的各類優秀的功能。

163. 依賴注入的方式以及你在項目中的選擇?

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


164. 提供Spring IoC容器配置元數據的方式?

答:

  1. 基於XML文件進行配置。
  2. 基於註解進行配置。

  3. 基於Java程序進行配置(Spring 3+)
package com.lovo.bean;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
  
@Component  
public class Person {  
    private String name;  
    private int age;  
    @Autowired  
    private Car car;  
      
    public Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    public void setCar(Car car) {  
        this.car = car;  
    }  
  
    @Override  
    public String toString() {  
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";  
    }  
  
}  

package com.lovo.bean;  
  
import org.springframework.stereotype.Component;  
  
@Component  
public class Car {  
    private String brand;  
    private int maxSpeed;  
  
    public Car(String brand, int maxSpeed) {  
        this.brand = brand;  
        this.maxSpeed = maxSpeed;  
    }  
  
    @Override  
    public String toString() {  
        return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";  
    }  
  
}  

package com.lovo.config;  
  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
import com.lovo.bean.Car;  
import com.lovo.bean.Person;  
  
@Configuration  
public class AppConfig {  
  
    @Bean  
    public Car car() {  
        return new Car("Benz", 320);  
    }  
      
    @Bean  
    public Person person() {  
        return new Person("Somnus", 34);  
    }  
}  

package com.lovo.test;  
  
import org.springframework.context.ConfigurableApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
  
import com.lovo.bean.Person;  
import com.lovo.config.AppConfig;  
  
class Test {  
  
    public static void main(String[] args) {  
        // TWR (Java 7+)  
        try(ConfigurableApplicationContext factory = new AnnotationConfigApplicationContext(AppConfig.class)) {  
            Person person = factory.getBean(Person.class);  
            System.out.println(person);  
        }  
    }  
}  


165. Spring提供了哪些Bean的做用域?

答:

  • singleton:Bean以單例的方式存在(IoC容器中僅存在該Bean的惟一實例)。

  • prototype:每次從容器中獲取Bean的實例時,都會返回一個新的實例(原型模式)。
  • request:每次HTTP請求都會建立新的實例,該做用域僅適用於WebApplicationContext環境。
  • session:同一個HTTP會話共享一個Bean的實例,不一樣的HTTP會話使用不一樣的Bean實例,該做用域僅適用於WebApplicationContext環境。

  • globalSession:同一個全局會話共享一個Bean的實例,通常應用於Portlet應用中。


166. 闡述Spring框架中Bean的生命週期?

答:

  1. Spring IoC容器找到關於Bean的定義並實例化該Bean。
  2. Spring IoC容器對Bean進行依賴注入。
  3. 假設Bean實現了BeanNameAware接口,則將該Bean的id傳給setBeanName方法。

  4. 假設Bean實現了BeanFactoryAware接口,則將BeanFactory對象傳給setBeanFactory方法。
  5. 假設Bean實現了BeanPostProcessor接口,則調用其postProcessBeforeInitialization方法。
  6. 假設Bean實現了InitializingBean接口。則調用其afterPropertySet方法。
  7. 假設有和Bean關聯的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調用。

  8. 當銷燬Bean實例時,假設Bean實現了DisposableBean接口。則調用其destroy方法。


167. 依賴注入時怎樣注入集合屬性?

答:可以在定義Bean屬性時。經過<list>/<set>/<map>/<props>分別爲其注入列表、集合、映射和鍵值都是字符串的映射屬性。


168. Spring中的本身主動裝配有哪些限制?

答:

  1. 假設使用了構造器注入或者setter注入。那麼將覆蓋本身主動裝配的依賴關係。
  2. 基本數據類型的值、字符串字面量、類字面量沒法使用本身主動裝配來注入。

  3. 有先考慮使用顯式的裝配來進行更精確的依賴注入而不是使用本身主動裝配。


169. 和本身主動裝配相關的註解有哪些?

答:

  • @Required:該依賴關係必須裝配(手動或本身主動裝配)。不然將拋出BeanInitializationException異常。

  • @Autowired:本身主動裝配,默認按類型進行本身主動裝配。
  • @Qualifier:假設按類型本身主動裝配時有不止一個匹配的類型。那麼可以使用該註解指定名字來消除歧義。


170. 怎樣使用HibernateDaoSupport整合Spring和Hibernate?

答:

  1. 在Spring中配置Hibernate的會話工廠(LocalSessionFactoryBean或AnnotationSessionFactoryBean)。
  2. 讓DAO的實現類繼承HibernateDaoSupport(繼承getHibernateTemplate方法來調用模板方法)。

  3. 讓Spring來管理Hibernate的事務(推薦使用聲明式事務)。


171. 你是怎樣理解「橫切關注」這個概念的?

答:「橫切關注」是會影響到整個應用程序的關注功能,它跟正常的業務邏輯是正交的。沒有一定的聯繫,但是差點兒所有的業務邏輯都會涉及到這些關注功能。一般,事務、日誌、安全性等關注就是應用中的橫切關注功能。


172. 怎樣理解Spring AOP中Advice這個概念?

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


173. 在Web項目中怎樣得到Spring的IoC容器?

答:

WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); 


174. Spring MVC怎樣對RESTful風格提供支持?

答:假設不瞭解RESTful可以看看百度百科的解說。關於這個問題,可以看看blogjava上的還有一個帖子。


175. 大型站點在架構上應當考慮哪些問題?

答:

  • 分層:分層是處理不論什麼複雜系統最多見的手段之中的一個,將系統橫向切分紅若干個層面。每個層面僅僅承擔單一的職責,而後經過下層爲上層提供的基礎設施和服務以及上層對下層的調用來造成一個完整的複雜的系統。

    計算機網絡的開放系統互聯參考模型(OSI/RM)和Internet的TCP/IP模型都是分層結構,大型站點的軟件系統也可以使用分層的理念將其分爲持久層(提供數據存儲和訪問服務)、業務層(處理業務邏輯。系統中最核心的部分)和表示層(系統交互、視圖展現)。

    需要指出的是:(1)分層是邏輯上的劃分,在物理上可以位於同一設備上也可以在不一樣的設備上部署不一樣的功能模塊,這樣可以使用不少其它的計算資源來應對用戶的併發訪問;(2)層與層之間應當有清晰的邊界。這樣分層纔有意義。才更利於軟件的開發和維護。

  • 切割:切割是對軟件的縱向切分。咱們可以將大型站點的不一樣功能和服務切割開,造成高內聚低耦合的功能模塊(單元)。在設計初期可以作一個粗粒度的切割,將站點切割爲若干個功能模塊。後期還可以進一步對每個模塊進行細粒度的切割,這樣一方面有助於軟件的開發和維護,還有一方面有助於分佈式的部署。提供站點的併發處理能力和功能的擴展。
  • 分佈式:除了上面提到的內容,站點的靜態資源(JavaScript、CSS、圖片等)也可以採用獨立分佈式部署並採用獨立的域名。這樣可以減輕應用server的負載壓力,也使得瀏覽器對資源的載入更快。數據的存取也應該是分佈式的。傳統的商業級關係型數據庫產品基本上都支持分佈式部署。而新生的NoSQL產品差點兒都是分佈式的。固然。站點後臺的業務處理也要使用分佈式技術,好比查詢索引的構建、數據分析等,這些業務計算規模龐大。可以使用Hadoop以及MapReduce分佈式計算框架來處理。

  • 集羣:集羣使得有不少其它的server提供一樣的服務。可以更好的提供對併發的支持。

  • 緩存:所謂緩存就是用空間換取時間的技術,將數據儘量放在距離計算近期的位置。

    使用緩存是站點優化的第必定律。咱們一般說的CDN、反向代理、熱點數據都是對緩存技術的使用。

  • 異步:異步是實現軟件實體之間解耦合的又一重要手段。異步架構是典型的生產者消費者模式。兩者之間沒有直接的調用關係,僅僅要保持數據結構不變,彼此功能實現可以任意變化而不互相影響。這對站點的擴展頗有利。使用異步處理還可以提升系統可用性,加快站點的響應速度(用Ajax載入數據就是一種異步技術)。同一時候還可以起到削峯做用(應對瞬時高併發)。「能推遲處理的都要推遲處理」是站點優化的第二定律,而異步是踐行站點優化第二定律的重要手段。
  • 冗餘:各類server都要提供對應的冗餘server以便在某臺或某些server宕機時還能保證站點可以正常工做,同一時候也提供了災難恢復的可能性。冗餘是站點高可用性的重要保證。


176. 你用過的站點前端優化的技術有哪些?

答:

  1. 瀏覽器訪問優化
    • 下降HTTP請求數量:合併CSS、合併JavaScript、合併圖片(CSS Sprite)
    • 使用瀏覽器緩存:經過設置HTTP響應頭中的Cache-Control和Expires屬性。將CSS、JavaScript、圖片等在瀏覽器中緩存,當這些靜態資源需要更新時,可以更新HTML文件裏的引用來讓瀏覽器又一次請求新的資源
    • 啓用壓縮
    • CSS前置,JavaScript後置
    • 下降Cookie傳輸
  2. CDN加速:CDN(Content Distribute Network)的本質仍然是緩存,將數據緩存在離用戶近期的地方,CDN一般部署在網絡運營商的機房,不只可以提高響應速度,還可以下降應用server的壓力。固然,CDN緩存的一般都是靜態資源。
  3. 反向代理:反向代理至關於應用server的一個門面,可以保護站點的安全性,也可以實現負載均衡的功能,固然最重要的是它緩存了用戶訪問的熱點資源,可以直接從反向代理將某些內容返回給用戶瀏覽器。


177. 你使用過的應用server優化技術有哪些?

答:

  1. 分佈式緩存:緩存的本質就是內存中的哈希表,假設設計一個優質的哈希函數,那麼理論上哈希表讀寫的漸近時間複雜度爲O(1)。

    緩存主要用來存放那些讀寫比很是高、變化很是少的數據。這樣應用程序讀取數據時先到緩存中讀取。假設沒有或者數據已經失效再去訪問數據庫或文件系統。並依據擬定的規則將數據寫入緩存。對站點數據的訪問也符合二八定律(Pareto分佈,冪律分佈)。即80%的訪問都集中在20%的數據上,假設可以將這20%的數據緩存起來,那麼系統的性能將獲得顯著的改善。固然,使用緩存需要解決下面幾個問題:(1)頻繁改動的數據;(2)數據不一致與髒讀。(3)緩存雪崩(可以採用分佈式緩存server集羣加以解決。Memcached是普遍採用的解決方式,它是一種互不通訊的集中式管理的分佈式緩存方案,可以從http://memcached.org/瞭解到關於Memcached的相關信息)。(4)緩存預熱;(5)緩存穿透(惡意持續請求不存在的數據)。

  2. 異步操做:可以使用消息隊列將調用異步化,經過異步處理將短期高併發產生的事件消息存儲在消息隊列中,從而起到削峯做用。

    電商站點在進行促銷活動時,可以將用戶的訂單請求存入消息隊列。這樣可以抵禦大量的併發訂單請求對系統和數據庫的衝擊。

    眼下。絕大多數的電商站點即使不進行促銷活動,訂單系統都採用了消息隊列來處理。

  3. 使用集羣
  4. 代碼優化
    • 多線程:基於Java的Web開發基本上都經過多線程的方式響應用戶的併發請求,使用多線程技術在編程上要解決線程安全問題,主要可以考慮下面幾個方面:
      • 將對象設計爲無狀態對象(這和麪向對象的編程觀點是矛盾的,在面向對象的世界中被視爲不良設計),這樣就不會存在併發訪問時對象狀態不一致的問題。
      • 在方法內部建立對象。這樣對象由進入方法的線程建立。不會出現多個線程訪問同一對象的問題。

        使用ThreadLocal將對象與線程綁定也是很是好的作法,這一點在前面已經探討過了。

      • 對資源進行併發訪問時應當使用合理的鎖機制。
    • 非堵塞I/O: 使用單線程和非堵塞I/O是眼下公認的比多線程的方式更能充分發揮server性能的應用模式。基於Node.js構建的server就採用了這種方式。Java在JDK 1.4中就引入了NIO(Non-blocking I/O),在Servlet 3規範中又引入了異步Servlet的概念。這些都爲在server端採用非堵塞I/O提供了必要的基礎。

    • 資源複用:資源複用主要有兩種方式。一是單例。而是對象池,咱們使用的數據庫鏈接池、線程池都是對象池化技術,這是典型的用空間換取時間的策略,還有一方面也實現對資源的複用,從而避免了沒必要要的建立和釋放資源所帶來的開銷。


178. 什麼是XSS攻擊?什麼是SQL注入攻擊?什麼是CSRF攻擊?

答:

  • XSS(Cross Site Script,跨站腳本攻擊)是向網頁中注入惡意腳本在用戶瀏覽網頁時在用戶瀏覽器中運行惡意腳本的攻擊方式。跨站腳本攻擊分有兩種形式:反射型攻擊(誘使用戶點擊一個嵌入惡意腳本的連接以達到攻擊的目標,眼下有很是多攻擊者利用論壇、微博公佈含有惡意腳本的URL就屬於這樣的方式)和持久型攻擊(將惡意腳本提交到被攻擊站點的數據庫中,用戶瀏覽網頁時,惡意腳本從數據庫中被載入到頁面運行。QQ郵箱的早期版本號就之前被利用做爲持久型跨站腳本攻擊的平臺)。XSS儘管不是什麼新奇玩意,但是攻擊的手法卻不斷翻新,防範XSS主要有雙方面:消毒(對危急字符進行轉義)和HttpOnly(防範XSS攻擊者竊取Cookie數據)。
  • SQL注入攻擊是注入攻擊最多見的形式(此外還有OS注入攻擊(Struts 2的高危漏洞就是經過OGNL實施OS注入攻擊致使的)),當server使用請求參數構造SQL語句時。惡意的SQL被嵌入到SQL中交給數據庫運行。SQL注入攻擊須要攻擊者對數據庫結構有所瞭解才幹進行,攻擊者想要得到表結構有多種方式:(1)假設使用開源系統搭建站點。數據庫結構也是公開的(眼下有很是多現成的系統可以直接搭建論壇,電商站點。儘管方便快捷但是風險是必須要認真評估的)。(2)錯誤回顯(假設將server的錯誤信息直接顯示在頁面上,攻擊者可以經過非法參數引起頁面錯誤從而經過錯誤信息瞭解數據庫結構,Web應用應當設置友好的錯誤頁,一方面符合最小吃驚原則,一方面屏蔽掉可能給系統帶來危急的錯誤回顯信息);(3)盲注。防範SQL注入攻擊也可以採用消毒的方式。經過正則表達式對請求參數進行驗證,此外,參數綁定也是很是好的手段,這樣惡意的SQL會被當作SQL的參數而不是命令被運行。JDBC中的PreparedStatement就是支持參數綁定的語句對象。從性能和安全性上都明顯優於Statement。

  • CSRF攻擊(Cross Site Request Forgery,跨站請求僞造)是攻擊者經過跨站請求,以合法的用戶身份進行非法操做(如轉帳或發帖等)。

    CSRF的原理是利用瀏覽器的Cookie或server的Session,盜取用戶身份。其原理例如如下圖所看到的。防範CSRF的主要手段是識別請求者的身份,主要有下面幾種方式:(1)在表單中加入令牌(token);(2)驗證碼。(3)檢查請求頭中的Referer(前面提到防圖片盜連接也是用的這樣的方式)。令牌和驗證都具備一次消費性的特徵,所以在原理上一致的。但是驗證碼是一種糟糕的用戶體驗,不是必要的狀況下不要輕易使用驗證碼,眼下很是多站點的作法是假設在短期內屢次提交一個表單未得到成功後纔要求提供驗證碼,這樣會得到較好的用戶體驗。


【補充】防火牆的架設是Web安全的重要保障。ModSecurity是開源的Web防火牆中的佼佼者。有興趣的可以在http://www.modsecurity.org/站點得到相關信息。

企業級防火牆的架設應當有兩級防火牆。Webserver和部分應用server可以架設在兩級防火牆之間的DMZ,而數據和資源server應當架設在第二級防火牆以後。例如如下圖所看到的。



179. 什麼是領域模型(domain model)?貧血模型(anaemic domain model)  

和充血模型(rich domain model)有什麼差異?

答:領域模型是領域內的概念類或現實世界中對象的可視化表示。又稱爲概念模型或分析對象模型。它專一於分析問題領域自己,發掘重要的業務領域概念。並創建業務領域概念之間的關係。貧血模型是指使用的領域對象中僅僅有setter和getter方法(POJO),所有的業務邏輯都不包括在領域對象中而是放在業務邏輯層。有人將咱們這裏說的貧血模型進一步劃分紅失血模型(領域對象全然沒有業務邏輯)和貧血模型(領域對象有少許的業務邏輯)。咱們這裏就不正確此加以區分了。充血模型將大多數業務邏輯和持久化放在領域對象中。業務邏輯(業務門面)僅僅是完畢對業務邏輯的封裝、事務和權限等的處理。

如下兩張圖分別展現了貧血模型和充血模型的分層架構。

貧血模型

充血模型

貧血模型下組織領域邏輯一般使用事務腳本模式。讓每個過程相應用戶可能要作的一個動做。每個動做由一個過程來驅動。

也就是說在設計業務邏輯接口的時候。每個方法相應着用戶的一個操做,這樣的模式有下面幾個有點:

  1. 它是一個大多數開發人員均可以理解的簡單過程模型(適合國內的絕大多數開發人員)。
  2. 它能夠與一個使用行數據入口或表數據入口的簡單數據訪問層很是好的協做。

  3. 事務邊界的顯而易見。一個事務開始於腳本的開始,終止於腳本的結束,很是easy經過代理(或切面)實現聲明式事務。
而後,事務腳本模式的缺點也是很是多的,隨着領域邏輯複雜性的添加。系統的複雜性將迅速添加,程序結構將變得極度混亂。開源中國社區上有一篇很是好的譯文《貧血領域模型是怎樣致使糟糕的軟件產生》對這個問題作了比較仔細的闡述。

180. 談一談測試驅動開發(TDD)的優勢以及你的理解。
答:TDD是指在編寫真正的功能實現代碼以前先寫測試代碼,而後依據需要重構實現代碼。在JUnit的做者Kent Beck的大做《測試驅動開發:實戰與模式解析》(Test-Driven Development: by Example)一書中有這麼一段內容:「消除恐懼和不肯定性是編寫測試驅動代碼的重要緣由」。因爲編寫代碼時的恐懼會讓你當心試探,讓你迴避溝通,讓你羞於獲得反饋,讓你變得焦躁不安,而TDD是消除恐懼、讓Java開發人員更加自信更加樂於溝通的重要手段。TDD會帶來的優勢可能不會當即呈現,但是你在某個時候必定會發現,這些優勢包含:
  1. 更清晰的代碼 --- 僅僅寫需要的代碼
  2. 更好的設計
  3. 更出色的靈活性 --- 鼓舞程序猿面向接口編程
  4. 更高速的反饋 --- 不會到系統上線時才知道bug的存在
【補充:】敏捷軟件開發的概念已經有很是多年了,而且也部分的改變了軟件開發這個行業。TDD也是敏捷開發所倡導的。


TDD可以在多個層級上應用,包含單元測試(測試一個類中的代碼)、集成測試(測試類之間的交互)、系統測試(測試執行的系統)和系統集成測試(測試執行的系統包含使用的第三方組件)。TDD的實施步驟是:紅(失敗測試) --- 綠(經過測試) --- 重構。

關於實施TDD的具體步驟請參考還有一篇文章《測試驅動開發之初窺門徑》。

在使用TDD開發時。經常會遇到需要被測對象需要依賴其它子系統的狀況,但是你但願將測試代碼跟依賴項隔離。以保證測試代碼只針對當前被測對象或方法展開。這時候你需要的是測試替身。測試替身可以分爲四類:

  1. 虛設替身:僅僅傳遞但是不會使用到的對象,通常用於填充方法的參數列表
  2. 存根替身:老是返回一樣的預設響應,當中可能包含一些虛設狀態
  3. 假裝替身:可以代替真實版本號的可用版本號(比真實版本號仍是會差很是多)
  4. 模擬替身:可以表示一系列指望值的對象,並且可以提供預設響應
Java世界中實現模擬替身的第三方工具許多,包含EasyMock、Mockito、jMock等。

相關文章
相關標籤/搜索