摘要:這部分包括了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
經過Class對象的getConstructors方法可以得到類的所有構造器的數組。Java 5之後的版本號還可以經過getConstrcutor(Class... parameterTypes)得到擁有特定參數的構造器對象。Constructor對象的一個主要方法是newInstance,經過該方法可以建立一個類的實例。數據庫
經過Class對象的getDeclaredMethods方法可以得到所有方法的數組,Java 5之後的版本號還可以經過getDeclaredMethod(String name, Class... parameterTypes)得到特定簽名的方法,當中name是方法名,可變參數表明方法的參數列表。Method對象最重要的方法是invoke(Object obj, Object[] args),當中obj是調用該方法的目標對象。args是傳給方法的參數,這樣就可以調用指定對象的方法。此外,Method對象還包含下面重要方法: express
經過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好用,僅僅有四個方法。能用上的也就是如下三個方法:
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()); } }
答:
Spring僅支持方法的鏈接點。
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中本身主動裝配的方式有哪些?
答:
155. Spring中怎樣使用註解來配置Bean?有哪些相關的註解?
答:首先需要在Spring配置文件裏添加例如如下配置:
<context:component-scan base-package="org.example"/>
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的工做原理例如如下圖所看到的:
固然,在這個地方Spring會經過HandlerAdapter對該處理器進行封裝,HandlerAdapter是一個適配器,它用統一的接口對各類Handler中的方法進行調用。
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"> <!-- 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框架爲企業級開發帶來的優勢)?
答:可以從下面幾個方面做答:
163. 依賴注入的方式以及你在項目中的選擇?
答:依賴注入可以經過setter方法注入(設值注入)、構造器注入和接口注入三種方式來實現,Spring支持setter注入和構造器注入,一般使用構造器注入來注入必須的依賴關係。對於可選的依賴關係。則setter注入是更好的選擇,setter注入需要類提供無參構造器或者無參的靜態工廠方法來建立對象。
164. 提供Spring IoC容器配置元數據的方式?
答:
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); } } }
答:
166. 闡述Spring框架中Bean的生命週期?
答:
167. 依賴注入時怎樣注入集合屬性?
答:可以在定義Bean屬性時。經過<list>/<set>/<map>/<props>分別爲其注入列表、集合、映射和鍵值都是字符串的映射屬性。
168. Spring中的本身主動裝配有哪些限制?
答:
169. 和本身主動裝配相關的註解有哪些?
答:
170. 怎樣使用HibernateDaoSupport整合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)層與層之間應當有清晰的邊界。這樣分層纔有意義。才更利於軟件的開發和維護。
使用緩存是站點優化的第必定律。咱們一般說的CDN、反向代理、熱點數據都是對緩存技術的使用。
176. 你用過的站點前端優化的技術有哪些?
答:
177. 你使用過的應用server優化技術有哪些?
答:
緩存主要用來存放那些讀寫比很是高、變化很是少的數據。這樣應用程序讀取數據時先到緩存中讀取。假設沒有或者數據已經失效再去訪問數據庫或文件系統。並依據擬定的規則將數據寫入緩存。對站點數據的訪問也符合二八定律(Pareto分佈,冪律分佈)。即80%的訪問都集中在20%的數據上,假設可以將這20%的數據緩存起來,那麼系統的性能將獲得顯著的改善。固然,使用緩存需要解決下面幾個問題:(1)頻繁改動的數據;(2)數據不一致與髒讀。(3)緩存雪崩(可以採用分佈式緩存server集羣加以解決。Memcached是普遍採用的解決方式,它是一種互不通訊的集中式管理的分佈式緩存方案,可以從http://memcached.org/瞭解到關於Memcached的相關信息)。(4)緩存預熱;(5)緩存穿透(惡意持續請求不存在的數據)。
電商站點在進行促銷活動時,可以將用戶的訂單請求存入消息隊列。這樣可以抵禦大量的併發訂單請求對系統和數據庫的衝擊。
眼下。絕大多數的電商站點即使不進行促銷活動,訂單系統都採用了消息隊列來處理。
使用ThreadLocal將對象與線程綁定也是很是好的作法,這一點在前面已經探討過了。
178. 什麼是XSS攻擊?什麼是SQL注入攻擊?什麼是CSRF攻擊?
答:
CSRF的原理是利用瀏覽器的Cookie或server的Session,盜取用戶身份。其原理例如如下圖所看到的。防範CSRF的主要手段是識別請求者的身份,主要有下面幾種方式:(1)在表單中加入令牌(token);(2)驗證碼。(3)檢查請求頭中的Referer(前面提到防圖片盜連接也是用的這樣的方式)。令牌和驗證都具備一次消費性的特徵,所以在原理上一致的。但是驗證碼是一種糟糕的用戶體驗,不是必要的狀況下不要輕易使用驗證碼,眼下很是多站點的作法是假設在短期內屢次提交一個表單未得到成功後纔要求提供驗證碼,這樣會得到較好的用戶體驗。
企業級防火牆的架設應當有兩級防火牆。Webserver和部分應用server可以架設在兩級防火牆之間的DMZ,而數據和資源server應當架設在第二級防火牆以後。例如如下圖所看到的。
如下兩張圖分別展現了貧血模型和充血模型的分層架構。
也就是說在設計業務邏輯接口的時候。每個方法相應着用戶的一個操做,這樣的模式有下面幾個有點:
TDD可以在多個層級上應用,包含單元測試(測試一個類中的代碼)、集成測試(測試類之間的交互)、系統測試(測試執行的系統)和系統集成測試(測試執行的系統包含使用的第三方組件)。TDD的實施步驟是:紅(失敗測試) --- 綠(經過測試) --- 重構。
關於實施TDD的具體步驟請參考還有一篇文章《測試驅動開發之初窺門徑》。
在使用TDD開發時。經常會遇到需要被測對象需要依賴其它子系統的狀況,但是你但願將測試代碼跟依賴項隔離。以保證測試代碼只針對當前被測對象或方法展開。這時候你需要的是測試替身。測試替身可以分爲四類: