Java面試題集(151-180)html
摘要:這部分包含了Spring、Spring MVC以及Spring和其餘框架整合以及測試相關的內容,除此以外還包含了大型網站技術架構相關面試內容。前端
151. Spring中的BeanFactory和ApplicationContext有什麼聯繫?java
答:Spring經過配置文件描述Bean以及Bean之間的依賴關係,利用Java的反射機制實現Bean的實例化,並創建Bean之間的依賴關係,在此基礎上,Spring的IoC容器還提供了Bean實例緩存、生命週期管理、Bean實例代理、事件發佈、資源裝載等高級服務。BeanFactory是Spring框架最核心的接口,它提供了IoC容器的配置機制。ApplicationContext創建在BeanFactory之上,提供了更多面嚮應用的功能,包括對國際化和框架事件體系的支持。一般將BeanFactory稱爲IoC容器,而ApplicationContext稱爲應用上下文,前者更傾向於Spring自己,後者更傾向於開發者,所以被使用得更多。程序員
【補充】反射(reflection)又叫自省(introspection),是得到對象或類型元數據的方法,Java反射機制能夠在運行時判斷對象所屬的類,在運行時構造任意一個類的對象,在運行時得到一個類的屬性和方法,在運行時調用對象的方法,或者生成動態代理。在Java中,能夠經過類的Class對象得到類的構造器、屬性、方法等類的元數據,還能夠訪問這些屬性或調用這些方法,和反射相關的類還包括:web
- Constructor:表明類的構造器的類。經過Class對象的getConstructors方法能夠得到類的全部構造器的數組,Java 5之後的版本還能夠經過getConstrcutor(Class... parameterTypes)得到擁有特定參數的構造器對象。Constructor對象的一個主要方法是newInstance,經過該方法能夠建立一個類的實例。
- Method:表明方法的類。經過Class對象的getDeclaredMethods方法能夠得到全部方法的數組,Java 5之後的版本還能夠經過getDeclaredMethod(String name, Class... parameterTypes)得到特定簽名的方法,其中name是方法名,可變參數表明方法的參數列表。Method對象最重要的方法是invoke(Object obj, Object[] args),其中obj是調用該方法的目標對象,args是傳給方法的參數,這樣就能夠調用指定對象的方法。此外,Method對象還包括如下重要方法:
- Class getReturnType():獲取方法的返回類型。
- Class[] getParameterTypes():獲取方法參數類型的數組。
- Class[] getExceptionTypes():獲取方法異常類型數組。
- Annotation[][] getParameterAnnotations():得到方法參數註解信息的數組。
- Field:表明屬性的類。經過Class對象的getDeclaredFields()方法能夠獲取類的屬性數組。經過getDeclaredField(String name)則能夠獲取某個特定名稱的屬性。Field對象最重要的方法是set(Object obj, Object value),其中obj是目標對象,而value是要賦給屬性的值。
除此以外,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
答:在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對非線程安全的對象進行了特殊處理。express
【補充】ThreadLocal爲解決多線程程序的併發問題提供了一種新的思路。ThreadLocal,顧名思義是線程的一個本地化對象,當工做於多線程中的對象使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程分配一個獨立的變量副本,因此每個線程均可以獨立的改變本身的副本,而不影響其餘線程所對應的副本。從線程的角度看,這個變量就像是線程的本地變量。
ThreadLocal類很是簡單好用,只有四個方法,能用上的也就是下面三個方法:
- 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有三種織入方式:
- 編譯期織入:須要特殊的Java編譯期(例如AspectJ的ajc)
- 類裝載期織入:要求使用特殊的類加載器
- 動態代理織入:在運行時爲目標類生成代理實現加強
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推斷採用了何種服務器端的技術,另外一方面能夠欺騙搜索引擎,由於搜索引擎不會搜索動態頁面,這種作法能夠稱爲僞靜態化。
159. Spring MVC的工做原理是怎樣的?
答:Spring MVC的工做原理以下圖所示:

- 客戶端的全部請求都交給前端控制器DispatcherServlet來處理,它會負責調用系統的其餘模塊來真正處理用戶的請求。
- DispatcherServlet收到請求後,將根據請求的信息(包括URL、HTTP協議方法、請求頭、請求參數、Cookie等)以及HandlerMapping的配置找處處理該請求的Handler(任何一個對象均可以做爲請求的Handler)。固然,在這個地方Spring會經過HandlerAdapter對該處理器進行封裝,HandlerAdapter是一個適配器,它用統一的接口對各類Handler中的方法進行調用。
- Handler完成對用戶請求的處理後,會返回一個ModelAndView對象給DispatcherServlet,ModelAndView顧名思義,包含了數據模型以及相應的視圖的信息。固然,這裏的視圖是邏輯視圖,DispatcherServlet還要藉助ViewResolver完成從邏輯視圖到真實視圖對象的解析工做。
- 當獲得真正的視圖對象後,Dispatcher會利用視圖對象對模型數據進行渲染。
- 客戶端獲得響應,多是一個普通的HTML頁面,也能夠是XML或JSON字符串,還能夠是一張圖片或者一個PDF文件。
160. 如何在Spring IoC容器中配置數據源?
答:
- <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"/>
- <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">
-
-
- <bean id="fooService" class="x.y.service.DefaultFooService"/>
-
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
-
- <tx:attributes>
-
- <tx:method name="get*" read-only="true"/>
-
- <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>
-
-
- <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>
-
-
- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"/>
- </bean>
-
-
-
- </beans>
162. 選擇使用Spring框架的緣由(Spring框架爲企業級開發帶來的好處)?
答:能夠從如下幾個方面做答:
- 非侵入式:支持基於POJO的編程模式,不強制性的要求實現Spring框架中的接口或繼承Spring框架中的類。
- IoC容器:IoC容器幫助應用程序管理對象以及對象之間的依賴關係,對象之間的依賴關係若是發生了改變只須要修改配置文件而不是修改代碼,由於代碼的修改可能意味着項目的從新構建和完整的迴歸測試。有了IoC容器,程序員不再須要本身編寫工廠、單例,這一點特別符合Spring的精神「不要重複的發明輪子」。
- AOP:面向切面編程,將全部的橫切關注功能封裝到切面(aspect)中,經過配置的方式將橫切關注功能動態添加到目標代碼上,進一步實現了業務邏輯和系統服務之間的分離。另外一方面,有了AOP程序員能夠省去不少本身寫代理類的工做。
- MVC:Spring的MVC框架是很是優秀的,從各個方面均可以甩Struts 2幾條街,爲Web表示層提供了更好的解決方案。
- 事務管理:Spring以寬廣的胸懷接納多種持久層技術(我想這一點與Rod Johnson音樂學博士的身份不無關係,這一點特別值得Gavin King學習,上天不可能賦予一我的太多的優勢以致於他沒有表達謙虛的餘地。關於這二位的軼事能夠本身百度一下),而且爲其提供了聲明式的事務管理,在不須要任何一行代碼的狀況下就可以完成事務管理。
- 其餘:選擇Spring框架的緣由還遠不止於此,Spring爲Java企業級開發提供了一站式選擇,你能夠在須要的時候使用它的部分和所有,更重要的是,你甚至能夠在感受不到Spring存在的狀況下,在你的項目中使用Spring提供的各類優秀的功能。
163. 依賴注入的方式以及你在項目中的選擇?
答:依賴注入能夠經過setter方法注入(設值注入)、構造器注入和接口注入三種方式來實現,Spring支持setter注入和構造器注入,一般使用構造器注入來注入必須的依賴關係,對於可選的依賴關係,則setter注入是更好的選擇,setter注入須要類提供無參構造器或者無參的靜態工廠方法來建立對象。
164. 提供Spring IoC容器配置元數據的方式?
答:
- 基於XML文件進行配置。
- 基於註解進行配置。
- 基於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("駱昊", 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) {
-
- 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的生命週期?
答:
- Spring IoC容器找到關於Bean的定義並實例化該Bean。
- Spring IoC容器對Bean進行依賴注入。
- 若是Bean實現了BeanNameAware接口,則將該Bean的id傳給setBeanName方法。
- 若是Bean實現了BeanFactoryAware接口,則將BeanFactory對象傳給setBeanFactory方法。
- 若是Bean實現了BeanPostProcessor接口,則調用其postProcessBeforeInitialization方法。
- 若是Bean實現了InitializingBean接口,則調用其afterPropertySet方法。
- 若是有和Bean關聯的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調用。
- 當銷燬Bean實例時,若是Bean實現了DisposableBean接口,則調用其destroy方法。
167. 依賴注入時如何注入集合屬性?
答:能夠在定義Bean屬性時,經過<list>/<set>/<map>/<props>分別爲其注入列表、集合、映射和鍵值都是字符串的映射屬性。
168. Spring中的自動裝配有哪些限制?
答:
- 若是使用了構造器注入或者setter注入,那麼將覆蓋自動裝配的依賴關係。
- 基本數據類型的值、字符串字面量、類字面量沒法使用自動裝配來注入。
- 有先考慮使用顯式的裝配來進行更精確的依賴注入而不是使用自動裝配。
169. 和自動裝配相關的註解有哪些?
答:
- @Required:該依賴關係必須裝配(手動或自動裝配),不然將拋出BeanInitializationException異常。
- @Autowired:自動裝配,默認按類型進行自動裝配。
- @Qualifier:若是按類型自動裝配時有不止一個匹配的類型,那麼可使用該註解指定名字來消除歧義。
170. 如何使用HibernateDaoSupport整合Spring和Hibernate?
答:
- 在Spring中配置Hibernate的會話工廠(LocalSessionFactoryBean或AnnotationSessionFactoryBean)。
- 讓DAO的實現類繼承HibernateDaoSupport(繼承getHibernateTemplate方法來調用模板方法)。
- 讓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、圖片等)也能夠採用獨立分佈式部署並採用獨立的域名,這樣能夠減輕應用服務器的負載壓力,也使得瀏覽器對資源的加載更快。數據的存取也應該是分佈式的,傳統的商業級關係型數據庫產品基本上都支持分佈式部署,而新生的NoSQL產品幾乎都是分佈式的。固然,網站後臺的業務處理也要使用分佈式技術,例如查詢索引的構建、數據分析等,這些業務計算規模龐大,可使用Hadoop以及MapReduce分佈式計算框架來處理。
- 集羣:集羣使得有更多的服務器提供相同的服務,能夠更好的提供對併發的支持。
- 緩存:所謂緩存就是用空間換取時間的技術,將數據儘量放在距離計算最近的位置。使用緩存是網站優化的第必定律。咱們一般說的CDN、反向代理、熱點數據都是對緩存技術的使用。
- 異步:異步是實現軟件實體之間解耦合的又一重要手段。異步架構是典型的生產者消費者模式,兩者之間沒有直接的調用關係,只要保持數據結構不變,彼此功能實現能夠隨意變化而不互相影響,這對網站的擴展很是有利。使用異步處理還能夠提升系統可用性,加快網站的響應速度(用Ajax加載數據就是一種異步技術),同時還能夠起到削峯做用(應對瞬時高併發)。「能推遲處理的都要推遲處理」是網站優化的第二定律,而異步是踐行網站優化第二定律的重要手段。
- 冗餘:各類服務器都要提供相應的冗餘服務器以便在某臺或某些服務器宕機時還能保證網站能夠正常工做,同時也提供了災難恢復的可能性。冗餘是網站高可用性的重要保證。
176. 你用過的網站前端優化的技術有哪些?
答:
- 瀏覽器訪問優化
- 減小HTTP請求數量:合併CSS、合併JavaScript、合併圖片(CSS Sprite)
- 使用瀏覽器緩存:經過設置HTTP響應頭中的Cache-Control和Expires屬性,將CSS、JavaScript、圖片等在瀏覽器中緩存,當這些靜態資源須要更新時,能夠更新HTML文件中的引用來讓瀏覽器從新請求新的資源
- 啓用壓縮
- CSS前置,JavaScript後置
- 減小Cookie傳輸
- CDN加速:CDN(Content Distribute Network)的本質仍然是緩存,將數據緩存在離用戶最近的地方,CDN一般部署在網絡運營商的機房,不只能夠提高響應速度,還能夠減小應用服務器的壓力。固然,CDN緩存的一般都是靜態資源。
- 反向代理:反向代理至關於應用服務器的一個門面,能夠保護網站的安全性,也能夠實現負載均衡的功能,固然最重要的是它緩存了用戶訪問的熱點資源,能夠直接從反向代理將某些內容返回給用戶瀏覽器。
177. 你使用過的應用服務器優化技術有哪些?
答:
- 分佈式緩存:緩存的本質就是內存中的哈希表,若是設計一個優質的哈希函數,那麼理論上哈希表讀寫的漸近時間複雜度爲O(1)。緩存主要用來存放那些讀寫比很高、變化不多的數據,這樣應用程序讀取數據時先到緩存中讀取,若是沒有或者數據已經失效再去訪問數據庫或文件系統,並根據擬定的規則將數據寫入緩存。對網站數據的訪問也符合二八定律(Pareto分佈,冪律分佈),即80%的訪問都集中在20%的數據上,若是可以將這20%的數據緩存起來,那麼系統的性能將獲得顯著的改善。固然,使用緩存須要解決如下幾個問題:(1)頻繁修改的數據;(2)數據不一致與髒讀;(3)緩存雪崩(能夠採用分佈式緩存服務器集羣加以解決,Memcached是普遍採用的解決方案,它是一種互不通訊的集中式管理的分佈式緩存方案,能夠從http://memcached.org/瞭解到關於Memcached的相關信息);(4)緩存預熱;(5)緩存穿透(惡意持續請求不存在的數據)。
- 異步操做:可使用消息隊列將調用異步化,經過異步處理將短期高併發產生的事件消息存儲在消息隊列中,從而起到削峯做用。電商網站在進行促銷活動時,能夠將用戶的訂單請求存入消息隊列,這樣能夠抵禦大量的併發訂單請求對系統和數據庫的衝擊。目前,絕大多數的電商網站即使不進行促銷活動,訂單系統都採用了消息隊列來處理。
- 使用集羣
- 代碼優化
- 多線程:基於Java的Web開發基本上都經過多線程的方式響應用戶的併發請求,使用多線程技術在編程上要解決線程安全問題,主要能夠考慮如下幾個方面:
- 將對象設計爲無狀態對象(這和麪向對象的編程觀點是矛盾的,在面向對象的世界中被視爲不良設計),這樣就不會存在併發訪問時對象狀態不一致的問題。
- 在方法內部建立對象,這樣對象由進入方法的線程建立,不會出現多個線程訪問同一對象的問題。使用ThreadLocal將對象與線程綁定也是很好的作法,這一點在前面已經探討過了。
- 對資源進行併發訪問時應當使用合理的鎖機制。
- 非阻塞I/O: 使用單線程和非阻塞I/O是目前公認的比多線程的方式更能充分發揮服務器性能的應用模式,基於Node.js構建的服務器就採用了這樣的方式。Java在JDK 1.4中就引入了NIO(Non-blocking I/O),在Servlet 3規範中又引入了異步Servlet的概念,這些都爲在服務器端採用非阻塞I/O提供了必要的基礎。
- 資源複用:資源複用主要有兩種方式,一是單例,而是對象池,咱們使用的數據庫鏈接池、線程池都是對象池化技術,這是典型的用空間換取時間的策略,另外一方面也實現對資源的複用,從而避免了沒必要要的建立和釋放資源所帶來的開銷。
178. 什麼是XSS攻擊?什麼是SQL注入攻擊?什麼是CSRF攻擊?
答:
- XSS(Cross Site Script,跨站腳本攻擊)是向網頁中注入惡意腳本在用戶瀏覽網頁時在用戶瀏覽器中執行惡意腳本的攻擊方式。跨站腳本攻擊分有兩種形式:反射型攻擊(誘使用戶點擊一個嵌入惡意腳本的連接以達到攻擊的目標,目前有不少攻擊者利用論壇、微博發佈含有惡意腳本的URL就屬於這種方式)和持久型攻擊(將惡意腳本提交到被攻擊網站的數據庫中,用戶瀏覽網頁時,惡意腳本從數據庫中被加載到頁面執行,QQ郵箱的早期版本就曾經被利用做爲持久型跨站腳本攻擊的平臺)。XSS雖然不是什麼新鮮玩意,可是攻擊的手法卻不斷翻新,防範XSS主要有兩方面:消毒(對危險字符進行轉義)和HttpOnly(防範XSS攻擊者竊取Cookie數據)。
- SQL注入攻擊是注入攻擊最多見的形式(此外還有OS注入攻擊(Struts 2的高危漏洞就是經過OGNL實施OS注入攻擊致使的)),當服務器使用請求參數構造SQL語句時,惡意的SQL被嵌入到SQL中交給數據庫執行。SQL注入攻擊須要攻擊者對數據庫結構有所瞭解才能進行,攻擊者想要得到表結構有多種方式:(1)若是使用開源系統搭建網站,數據庫結構也是公開的(目前有不少現成的系統能夠直接搭建論壇,電商網站,雖然方便快捷可是風險是必需要認真評估的);(2)錯誤回顯(若是將服務器的錯誤信息直接顯示在頁面上,攻擊者能夠經過非法參數引起頁面錯誤從而經過錯誤信息瞭解數據庫結構,Web應用應當設置友好的錯誤頁,一方面符合最小驚訝原則,一方面屏蔽掉可能給系統帶來危險的錯誤回顯信息);(3)盲注。防範SQL注入攻擊也能夠採用消毒的方式,經過正則表達式對請求參數進行驗證,此外,參數綁定也是很好的手段,這樣惡意的SQL會被當作SQL的參數而不是命令被執行,JDBC中的PreparedStatement就是支持參數綁定的語句對象,從性能和安全性上都明顯優於Statement。
- CSRF攻擊(Cross Site Request Forgery,跨站請求僞造)是攻擊者經過跨站請求,以合法的用戶身份進行非法操做(如轉帳或發帖等)。CSRF的原理是利用瀏覽器的Cookie或服務器的Session,盜取用戶身份,其原理以下圖所示。防範CSRF的主要手段是識別請求者的身份,主要有如下幾種方式:(1)在表單中添加令牌(token);(2)驗證碼;(3)檢查請求頭中的Referer(前面提到防圖片盜連接也是用的這種方式)。令牌和驗證都具備一次消費性的特徵,所以在原理上一致的,可是驗證碼是一種糟糕的用戶體驗,不是必要的狀況下不要輕易使用驗證碼,目前不少網站的作法是若是在短期內屢次提交一個表單未得到成功後纔要求提供驗證碼,這樣會得到較好的用戶體驗。
【補充】防火牆的架設是Web安全的重要保障,ModSecurity是開源的Web防火牆中的佼佼者,有興趣的能夠在
http://www.modsecurity.org/網站得到相關信息。企業級防火牆的架設應當有兩級防火牆,Web服務器和部分應用服務器能夠架設在兩級防火牆之間的DMZ,而數據和資源服務器應當架設在第二級防火牆以後,以下圖所示。
179. 什麼是領域模型(domain model)?貧血模型(anaemic domain model)
和充血模型(rich domain model)有什麼區別?
答:領域模型是領域內的概念類或現實世界中對象的可視化表示,又稱爲概念模型或分析對象模型,它專一於分析問題領域自己,發掘重要的業務領域概念,並創建業務領域概念之間的關係。貧血模型是指使用的領域對象中只有setter和getter方法(POJO),全部的業務邏輯都不包含在領域對象中而是放在業務邏輯層。有人將咱們這裏說的貧血模型進一步劃分紅失血模型(領域對象徹底沒有業務邏輯)和貧血模型(領域對象有少許的業務邏輯),咱們這裏就不對此加以區分了。充血模型將大多數業務邏輯和持久化放在領域對象中,業務邏輯(業務門面)只是完成對業務邏輯的封裝、事務和權限等的處理。下面兩張圖分別展現了貧血模型和充血模型的分層架構。
貧血模型
充血模型
貧血模型下組織領域邏輯一般使用事務腳本模式,讓每一個過程對應用戶可能要作的一個動做,每一個動做由一個過程來驅動。也就是說在設計業務邏輯接口的時候,每一個方法對應着用戶的一個操做,這種模式有如下幾個有點:
- 它是一個大多數開發者都可以理解的簡單過程模型(適合國內的絕大多數開發者)。
- 它可以與一個使用行數據入口或表數據入口的簡單數據訪問層很好的協做。
- 事務邊界的顯而易見,一個事務開始於腳本的開始,終止於腳本的結束,很容易經過代理(或切面)實現聲明式事務。
而後,事務腳本模式的缺點也是不少的,隨着領域邏輯複雜性的增長,系統的複雜性將迅速增長,程序結構將變得極度混亂。開源中國社區上有一篇很好的譯文
《貧血領域模型是如何致使糟糕的軟件產生》對這個問題作了比較細緻的闡述。
180. 談一談測試驅動開發(TDD)的好處以及你的理解。
答:TDD是指在編寫真正的功能實現代碼以前先寫測試代碼,而後根據須要重構實現代碼。在JUnit的做者Kent Beck的大做《測試驅動開發:實戰與模式解析》(Test-Driven Development: by Example)一書中有這麼一段內容:「消除恐懼和不肯定性是編寫測試驅動代碼的重要緣由」。由於編寫代碼時的恐懼會讓你當心試探,讓你迴避溝通,讓你羞於獲得反饋,讓你變得焦躁不安,而TDD是消除恐懼、讓Java開發者更加自信更加樂於溝通的重要手段。TDD會帶來的好處可能不會立刻呈現,可是你在某個時候必定會發現,這些好處包括:
- 更清晰的代碼 --- 只寫須要的代碼
- 更好的設計
- 更出色的靈活性 --- 鼓勵程序員面向接口編程
- 更快速的反饋 --- 不會到系統上線時才知道bug的存在
【補充:】
敏捷軟件開發的概念已經有不少年了,並且也部分的改變了軟件開發這個行業,TDD也是敏捷開發所倡導的。
TDD能夠在多個層級上應用,包括單元測試(測試一個類中的代碼)、集成測試(測試類之間的交互)、系統測試(測試運行的系統)和系統集成測試(測試運行的系統包括使用的第三方組件)。TDD的實施步驟是:紅(失敗測試) --- 綠(經過測試) --- 重構。關於實施TDD的詳細步驟請參考另外一篇文章《測試驅動開發之初窺門徑》。
在使用TDD開發時,常常會遇到須要被測對象須要依賴其餘子系統的狀況,可是你但願將測試代碼跟依賴項隔離,以保證測試代碼僅僅針對當前被測對象或方法展開,這時候你須要的是測試替身。測試替身能夠分爲四類:
- 虛設替身:只傳遞可是不會使用到的對象,通常用於填充方法的參數列表
- 存根替身:老是返回相同的預設響應,其中可能包括一些虛設狀態
- 假裝替身:能夠取代真實版本的可用版本(比真實版本仍是會差不少)
- 模擬替身:能夠表示一系列指望值的對象,而且能夠提供預設響應