面試專題之Spring

題目

  1. 什麼是 Spring 框架? Spring 框架有哪些主要模塊?
  2. 使用 Spring 框架能帶來哪些好處?講講Spring 的優缺點?
  3. 什麼是控制反轉(IOC)?什麼是依賴注入(DI)?
  4. 請解釋下 Spring 框架中的 IoC?
  5. BeanFactory 和 ApplicationContext 有什麼區別?
  6. Spring 有幾種配置方式?
  7. 如何用基於 XML 配置的方式配置 Spring?
  8. 如何用基於 Java 配置的方式配置 Spring?
  9. 怎樣用註解的方式配置 Spring?
  10. 創建Spring Bean的流程?Spring怎樣解決循環依賴(三級緩存)?(重點) Spring Bean 的生命週期?
  11. Spring Bean 的作用域之間有什麼區別?
  12. 什麼是 Spring inner beans?
  13. Spring 框架中的單例 Beans 是線程安全的麼?
  14. 請舉例說明如何在 Spring 中注入一個 Java Collection?
  15. 如何向 Spring Bean 中注入一個 Java.util.Properties?
  16. 請解釋 Spring Bean 的自動裝配?
  17. 請解釋自動裝配模式的區別?
  18. 如何開啓基於註解的自動裝配?
  19. 請舉例解釋@Required 註解?
  20. 請舉例解釋@Autowired 註解?
  21. 請舉例說明@Qualifier 註解?
  22. 構造方法注入和設值注入有什麼區別?
  23. Spring 框架中有哪些不同類型的事件?
  24. FileSystemResource 和 ClassPathResource 有何區別?
  25. Spring 框架中都用到了哪些設計模式?
  26. 開發中主要使用 Spring 的什麼技術 ?
  27. 簡述 AOP 和 IOC 概念 AOP:
  28. 在 Spring 中如何配置 Bean ?
  29. IOC 容器對 Bean 的生命週期:
  30. 構造器注入和 setter 依賴注入,那種方式更好?
  31. 依賴注入和工程模式之間有什麼不同?
  32. 講講springIOC/AOP怎麼實現的能講講原理嗎?說說Ioc容器的加載過程Spring IOC底層存儲結構 ? 自己實現IOC要怎麼做,哪些步驟?
  33. aop的應用場景?
  34. AOP的原理是什麼? 除了動態代理還有什麼?
  35. 攔截器和AOP的關係 Spring攔截器的底層是怎麼實現的
  36. 你如何理解AOP中的連接點(Joinpoint)、切點(Pointcut)、增強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?
  37. Spring支持的事務管理類型有哪些?你在項目中使用哪種方式?
  38. 什麼是Spring的聲明式事務管理。
  39. AOP 的 @transaction 是怎麼做的?爲什麼不加這個註解就不是事務?
  40. spring事務傳播機制
  41. 如果spring的事務中拋出了IOException,會回滾嗎?
  42. 在spring中雙重檢查鎖有效嗎
  43. 依賴注入和工程模式之間有什麼不同?
  44. 看過Spring源碼沒,說說Ioc容器的加載過程吧

答案

1、什麼是 Spring 框架? Spring 框架有哪些主要模塊?
Spring 框架是一個爲 Java 應用程序的開發提供了綜合、廣泛的基礎性支持的 Java 平臺。
Spring 幫助開發者解決了開發中基礎性的問題,使得開發人員可以專注於應用程序的開發。
Spring 框架本身亦是按照設計模式精心打造,這使得我們可以在開發環境中安心的集成 Spring 框架,不必擔心 Spring 是如何在後臺進行工作的。
Spring 框架至今已集成了 20 多個模塊。這些模塊主要被分如下圖所示的核心容器、數據訪問/集成,、 Web、 AOP(面向切面編程)、工具、消息和測試模塊。
在這裏插入圖片描述

2、使用 Spring 框架能帶來哪些好處?講講Spring 的優缺點?
下面列舉了一些使用 Spring 框架帶來的主要好處:

  • Dependency Injection(DI) 方法使得構造器和 JavaBean properties 文件中的依賴關係一目瞭然。
  • 與 EJB 容器相比較, IoC 容器更加趨向於輕量級。這樣一來 IoC 容器在有限的內存和 CPU資源的情況下進行應用程序的開發和發佈就變得十分有利。
  • Spring 並沒有閉門造車, Spring 利用了已有的技術比如 ORM 框架、 logging 框架、 J2EE、 Quartz 和 JDK Timer,以及其他視圖技術。
  • Spring 框架是按照模塊的形式來組織的。由包和類的編號就可以看出其所屬的模塊,開發者僅僅需要選用他們需要的模塊即可。
  • 要測試一項用 Spring 開發的應用程序十分簡單,因爲測試相關的環境代碼都已經囊括在框架中了。更加簡單的是,利用 JavaBean
    形式的 POJO 類,可以很方便的利用依賴注入來寫入測試數據。
  • Spring 的 Web 框架亦是一個精心設計的 Web MVC 框架,爲開發者們在 web 框架的選擇上 提供了一個除了主流框架比如 Struts、過度設計的、不流行 web 框架的以外的有力選項。
  • Spring 提供了一個便捷的事務管理接口,適用於小型的本地事物處理(比如在單 DB 的環境 下)和複雜的共同事物處理(比如利用 JTA 的複雜 DB 環境)。

優點:

  1. 降低了組件之間的耦合性,實現了軟件各層之間的解耦。
  2. 可以使用容器提供的衆多服務,如事務管理,消息服務等。
  3. 容器提供單例模式支持。
  4. 容器提供了AOP技術,利用它可以很容易實現一些攔截,如權限攔截,運行期監控等。
  5. 容器提供了衆多的輔助類,能夠加快應用的開發。
  6. spring對於主流的應用框架提供了很好的支持,例如mybatis等。
  7. spring屬於低入侵式設計。
  8. 獨立於各種應用服務器。
  9. spring的DI機制降低了業務對象替換的複雜性。
  10. spring的高度開放性,並不強制應用完全依賴於它,開發者可以自由選擇spring的部分或者全部。

spring的缺點:

  1. 使用了大量的反射機制,反射機制非常佔用內存。
  2. Spring版本繁多,越來越臃腫
  3. 配置繁多

3、什麼是控制反轉(IOC)?什麼是依賴注入(DI)?
控制反轉是應用於軟件工程領域中的,在運行時被裝配器對象來綁定耦合對象的一種編程技巧,對象之間耦合關係在編譯時通常是未知的。在傳統的編程方式中,業 務邏輯的流程是由應用程序中的早已被設定好關聯關係的對象來決定的。在使用控制反轉的情況下,業務邏輯的流程是由對象關係圖來決定的,該對象關係圖由裝配 器負責實例化,這種實現方式還可以將對象之間的關聯關係的定義抽象化。而綁定的過程是通過“依賴注入”實現的。

控制反轉是一種以給予應用程序中目標組件更多控制爲目的設計範式,並在我們的實際工作中起到了有效的作用。

依賴注入是在編譯階段尚未知所需的功能是來自哪個的類的情況下,將其他對象所依賴的功能對象實例化的模式。這就需要一種機制用來激活相應的組件以提供特定的功能,所以依賴注入是控制反轉的基礎。否則如果在組件不受框架控制的情況下,框架又怎麼知道要創建哪個組件?
在 Java 中倚賴注入有以下三種實現方式:
1.構造器注入
2.Setter 方法注入
3.接口注入

4、請解釋下 Spring 框架中的 IoC?
Spring 中的 org.springframework.beans 包和 org.springframework.context 包
構成了 Spring 框架 IoC 容器的基礎。
BeanFactory 接口提供了一個先進的配置機制,使得任何類型的對象的配置成爲可能。
ApplicationContex 接口對 BeanFactory(是一個子接口)進行了擴展,在 BeanFactory的基礎上添加了其他功能,比如與 Spring 的 AOP 更容易集成,也提供了處理 message resource的機制(用於國際化)、事件傳播以及應用層的特別配置,比如針對 Web 應用的WebApplicationContext。

org.springframework.beans.factory.BeanFactory 是 Spring IoC 容器的具體實現,
用來包裝和管理前面提到的各種 bean。 BeanFactory 接口是 Spring IoC 容器的核心接口。

IOC:把對象的創建、初始化、銷燬交給 spring 來管理,而不是由開發者控制,實現控制反轉。
5、 BeanFactory 和 ApplicationContext 有什麼區別?
BeanFactory 可以理解爲含有 bean 集合的工廠類。 BeanFactory 包含了種 bean 的定義,以便在接收到客戶端請求時將對應的 bean 實例化。
BeanFactory 還能在實例化對象的時生成協作類之間的關係。此舉將 bean 自身與 bean 客戶端的配置中解放出來。 BeanFactory 還包含 了 bean 生命週期的控制,調用客戶端的初始化方法(initialization methods)和銷燬方法(destruction methods)。

從表面上看, application context(加載時全部加載) 如同 bean factory(懶加載) 一樣具有 bean 定義、 bean 關聯關係的設置,根據請求分發 bean 的功能。但 applicationcontext 在此基礎上還提供了其他的功能。

  1. 提供了支持國際化的文本消息
  2. 統一的資源文件讀取方式
  3. 已在監聽器中註冊的 bean 的事件以下是三種較常見的 ApplicationContext 實現方式:
    (1) ClassPathXmlApplicationContext:從 classpath 的 XML 配置文件中讀取上下文,並生成上下文定義。應用程序上下文從程序環境變量中ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
    (2)FileSystemXmlApplicationContext :由文件系統中的 XML 配置文件讀取上下文。ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
    (3) XmlWebApplicationContext:由 Web 應用的 XML 文件讀取上下文。
    (4) AnnotationConfigApplicationContext(基於 Java 配置啓動容器)

在這裏插入圖片描述
6、 Spring 有幾種配置方式?
將 Spring 配置到應用開發中有以下三種方式:
1.基於 XML 的配置
2.基於註解的配置
3.基於 Java 的配置

7、如何用基於 XML 配置的方式配置 Spring?
在 Spring 框架中,依賴和服務需要在專門的配置文件來實現,我常用的 XML 格式的配置文件。這些配置文件的格式通常用開頭,然後一系列的 bean 定義和專門的應用配置選項組成。

SpringXML 配置的主要目的時候是使所有的 Spring 組件都可以用 xml 文件的形式來進行配置。這意味着不會出現其他的 Spring 配置類型(比如聲明的方式或基於 Java Class 的配置方式)
Spring 的 XML 配置方式是使用被 Spring 命名空間的所支持的一系列的 XML 標籤來實現的。
Spring 有以下主要的命名空間: context、 beans、 jdbc、 tx、 aop、 mvc 和 aso。
如:

<beans>
	<!-- JSON Support -->
	<bean name="viewResolver"
		class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
	<bean name="jsonTemplate"
		class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
	<bean id="restTemplate"
		class="org.springframework.web.client.RestTemplate"/>
</beans>

下面這個 web.xml 僅僅配置了 DispatcherServlet,這件最簡單的配置便能滿足應用程序配置運行時組件的需求

<web-app>
	<display-name>Archetype Created Web Application</display-name>
	<servlet>
		<servlet-name>spring</servlet-name>
		<servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

8、如何用基於 Java 配置的方式配置 Spring?
Spring 對 Java 配置的支持是由@Configuration 註解和@Bean 註解來實現的。由@Bean 註解的方法將會實例化、配置和初始化一個 新對象,這個對象將由 Spring 的 IoC 容器來管理。
@Bean 聲明所起到的作用與<bean/> 元素類似。被 @Configuration 所註解的類則表示這個類的主要目的是作爲 bean 定義的資源。被@Configuration 聲明的類可以通過在同一個類的 內部調用@bean 方法來設置嵌入 bean 的依賴關係。
最簡單的@Configuration 聲明類請參考下面的代碼:

@Configuration
public class AppConfig{
	@Bean
	public MyService myService() {
		return new MyServiceImpl();
	}
}

對於上面的@Beans 配置文件相同的 XML 配置文件如下:

<beans>
	<bean id="myService" class="com.somnus.services.MyServiceImpl"/>
</beans>

上述配置方式的實例化方式如下:利用 AnnotationConfigApplicationContext 類進行實例化

public static void main(String[] args) {
	ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
	MyService myService = ctx.getBean(MyService.class);
	myService.doStuff();
}

要使用組件組建掃描,僅需用@Configuration 進行註解即可:

@Configuration
@ComponentScan(basePackages = "com.somnus")
public class AppConfig {
	...
}

在上面的例子中, com.acme 包首先會被掃到,然後再容器內查找被@Component 聲明的類,找到後將這些類按照 Sring bean 定義進行註冊。
如果你要在你的 web 應用開發中選用上述的配置的方式的話,需要用
AnnotationConfigWebApplicationContext 類來讀 取配置文件,可以用來配置 Spring 的Servlet 監聽器 ContextLoaderListener 或者 Spring MVC 的 DispatcherServlet。

<?xml version="1.0" encoding="utf-8"?>
<web-app> 
  <!-- Configure ContextLoaderListener to use
	AnnotationConfigWebApplicationContext
	instead of the default XmlWebApplicationContext -->  
  <context-param> 
    <param-name>contextClass</param-name>  
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicatio nContext</param-value> 
  </context-param>  
  <!-- Configuration locations must consist of one or more comma- or
	space-delimited
	fully-qualified @Configuration classes. Fully-qualified
	packages may also be
	specified for component-scanning -->  
  <context-param> 
    <param-name>contextConfigLocation</param-name>  
    <param-value>com.howtodoinjava.AppConfig</param-value> 
  </context-param>  
  <!-- Bootstrap the root application context as usual using
	ContextLoaderListener -->  
  <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
  </listener>  
  <!-- Declare a Spring MVC DispatcherServlet as usual -->  
  <servlet> 
    <servlet-name>dispatcher</servlet-name>  
    <servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass>  
    <!-- Configure DispatcherServlet to use
	AnnotationConfigWebApplicationContext
	instead of the default XmlWebApplicationContext -->  
    <init-param> 
      <param-name>contextClass</param-name>  
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> 
    </init-param>  
    <!-- Again, config locations must consist of one or more commaor space-delimited
	and fully-qualified @Configuration classes -->  
    <init-param> 
      <param-name>contextConfigLocation</param-name>  
      <param-value>com.howtodoinjava.web.MvcConfig</param-value> 
    </init-param> 
  </servlet>  
  <!-- map all requests for /app/* to the dispatcher servlet -->  
  <servlet-mapping> 
    <servlet-name>dispatcher</servlet-name>  
    <url-pattern>/app/*</url-pattern> 
  </servlet-mapping> 
</web-app>

9、怎樣用註解的方式配置 Spring?
Spring 在 2.5 版本以後開始支持用註解的方式來配置依賴注入。可以用註解的方式來替代 XML 方式的 bean 描述,可以將 bean 描述轉移到組件類的 內部,只需要在相關類上、方法上或者字段聲明上使用註解即可。註解注入將會被容器在 XML 注入之前被處理,所以後者會覆蓋掉前者對於同一個屬性的處理結 果。
註解裝配在 Spring 中是默認關閉的。所以需要在 Spring 文件中配置一下才能使用基於註解的裝配模式。如果你想要在你的應用程序中使用關於註解的方法的話,請參考如下的配置。

<beans>
	<context:annotation-config/>
	<!-- bean definitions go here -->
</beans>

<context:annotation-config/>標籤配置完成以後,就可以用註解的方式在 Spring 中向屬性、方法和構造方法中自動裝配變量。
下面是幾種比較重要的註解類型:
1. @Required:該註解應用於設值方法。
2. @Autowired:該註解應用於有值設值方法、非設值方法、構造方法和變量。
3. @Qualifier:該註解和@Autowired 註解搭配使用,用於消除特定 bean 自動裝配的歧義。
4. JSR-250 Annotations: Spring 支持基於 JSR-250 註解的以下註解, @Resource、
@PostConstruct 和 @PreDestroy。

10、創建Spring Bean的流程?Spring怎樣解決循環依賴(三級緩存)?(重點) Spring Bean 的生命週期?
創建流程
1.獲取bean的名字
2.從緩存中查詢是否有這個bean
3.沒有的話就需要通過反射創建bean的實例(注意此時bean爲空,裏面東西都沒注入)
4.標記這個bean已經被創建了(此時可能會有循環依賴的問題,Spring用三級緩存來解決,提前將bean曝光)
5.遞歸獲取依賴的其他的bean
6.給當前bean綁定屬性

三級緩存(注意有大佬說這個問題是必考重點,而菜菜我竟然對這個沒有任何印象)
構造器(初始化與賦值沒法分開)與prototype(沒有實現三級緩存)會報錯
三級緩存分別爲
1.初始化完成的bean(singletonObjects)
2.實例化的bean(尚未綁定屬性,earlySingletonObjects)3.beanfactory(singletonFactories)
比如有兩個beanA和B循環依賴
在A的實例化階段標記,將自己曝光到第三級緩存中,發現自己依賴B,去初始化B,B初始化過程中發現自己依賴A,從第三級緩存中getObject拿到A(注意此時A只是實例化完成,並沒有初始化),此時B順利進行初始化,將自己放到一級緩存中,此時返回A中,A順利拿到B,完成了初始化階段,放到了一級緩存。

聲明週期
Spring Bean 的生命週期簡單易懂。在一個 bean 實例被初始化時,需要執行一系列的初始化操作以達到可用的狀態。同樣的,當一個 bean 不在被調用時需要進行相關的析構操作,並從 bean 容器中移除。

Spring bean factory 負責管理在 spring 容器中被創建的 bean 的生命週期。 Bean 的生命週期由兩組回調(call back)方法組成。
1. 初始化之後調用的回調方法。
2. 銷燬之前調用的回調方法。

Spring 框架提供了以下四種方式來管理 bean 的生命週期事件:

  • InitializingBean 和 DisposableBean 回調接口
  • 針對特殊行爲的其他 Aware 接口
  • Bean 配置文件中的 Custom init()方法和 destroy()方法
  • @PostConstruct 和@PreDestroy 註解方式

使用 customInit()和 customDestroy()方法管理 bean 生命週期的代碼樣例如
下:

<beans>
	<bean id="demoBean" class="com.somnus.task.DemoBean" 
	initmethod="customInit" destroy-method="customDestroy"></bean>
</beans>

11、 Spring Bean 的作用域之間有什麼區別?
Spring 容器中的 bean 可以分爲 5 個範圍。所有範圍的名稱都是自說明的,但是爲了避免混淆,還是讓我們來解釋一下:

  1. singleton:這種 bean 範圍是默認的,這種範圍確保不管接受到多少個請求,每個容器中只有一個bean 的實例,單例的模式由 bean factory 自身來維護。
  2. prototype:原形範圍與單例範圍相反,爲每一個 bean 請求提供一個實例。
  3. request:在請求 bean 範圍內會每一個來自客戶端的網絡請求創建一個實例,在請求完成以後,bean 會失效並被垃圾回收器回收。
  4. Session:與請求範圍類似,確保每個 session 中有一個 bean 的實例,在 session 過期後, bean會隨之失效。
  5. global- session: global-session 和 Portlet 應用相關。當你的應用部署在 Portlet 容器中工作時,它包含很多 portlet。如果 你想要聲明讓所有的 portlet 共用全局的存儲變量的話,那麼這全局變量需要存儲在 global-session 中。全局作用域與 Servlet 中的 session 作用域效果相同。

12、什麼是 Spring inner beans?
在 Spring 框架中,無論何時 bean 被使用時,當僅被調用了一個屬性。一個明智的做法是將這個bean 聲明爲內部 bean。內部 bean 可以用 setter 注入“屬性”和構造方法注入“構造參數”的方式來實現。
比如,在我們的應用程序中,一個 Customer 類引用了一個 Person 類,我們的要做的是創建一個Person 的實例,然後在 Customer 內部使用

public class Customer{
	private Person person;
	//Setters and Getters
}
public class Person{
	private String name;
	private String address;
	private int age;
	//Setters and Getters
}

內部 bean 的聲明方式如下:

<?xml version="1.0" encoding="utf-8"?>

<bean id="CustomerBean" class="com.somnus.common.Customer"> 
  <property name="person"> 
    <!-- This is inner bean -->  
    <bean class="com.howtodoinjava.common.Person"> 
      <property name="name" value="lokesh"/>  
      <property name="address" value="India"/>  
      <property name="age" value="34"/> 
    </bean> 
  </property> 
</bean>

13、 Spring 框架中的單例 Beans 是線程安全的麼?
Spring 框架並沒有對單例 bean 進行任何多線程的封裝處理。關於單例 bean 的線程安全和併發問題需要開發者自行去搞定。但實際上,大部分的 Spring bean 並沒有可變的狀態(比如 Serview 類和 DAO 類),所以在某種程度上說 Spring 的單例 bean 是線程安全的。如果你的 bean 有多種狀態的話(比如 View Model 對象),就需要自行保證線程安全。

最淺顯的解決辦法就是將多態 bean 的作用域由“singleton”變更爲“prototype”。

14、請舉例說明如何在 Spring 中注入一個 Java Collection?
Spring 提供了以下四種集合類的配置元素:
 : 該標籤用來裝配可重複的 list 值。
 : 該標籤用來裝配沒有重複的 set 值。
: 該標籤可用來注入鍵和值可以爲任何類型的鍵值對。
 : 該標籤支持注入鍵和值都是字符串類型的鍵值對。
下面看一下具體的例子:

<?xml version="1.0" encoding="utf-8"?>

<beans> 
  <!-- Definition for javaCollection -->  
  <bean id="javaCollection" class="com.howtodoinjava.JavaCollection"> 
    <!-- java.util.List -->  
    <property name="customList"> 
      <list> 
        <value>INDIA</value>  
        <value>Pakistan</value>  
        <value>USA</value>  
        <value>UK</value> 
      </list> 
    </property>  
    <!-- java.util.Set -->  
    <property name="customSet"> 
      <set> 
        <value>INDIA</value>  
        <value>Pakistan</value>  
        <value>USA</value>  
        <value>UK</value> 
      </set> 
    </property>  
    <!-- java.util.Map -->  
    <property name="customMap"> 
      <map> 
        <entry key="1" value="INDIA"/>  
        <entry key="2" value="Pakistan"/>  
        <entry key="3" value="USA"/>  
        <entry key="4" value="UK"/> 
      </map>
    </property>  
    <!-- java.util.Properties -->  
    <property name="customProperies"> 
      <props> 
        <prop key="admin">[email protected]</prop>  
        <prop key="support">[email protected]</prop> 
      </props> 
    </property> 
  </bean> 
</beans>

15、如何向 Spring Bean 中注入一個 Java.util.Properties?
第一種方法是使用如下面代碼所示的<props> 標籤:

<?xml version="1.0" encoding="utf-8"?>
<bean id="adminUser" class="com.somnus.common.Customer"> 
  <!-- java.util.Properties -->  
  <property name="emails"> 
    <props> 
      <prop key="admin">admin@nospam.com</prop>  
      <prop key="support">support@nospam.com</prop> 
    </props> 
  </property> 
</bean>

也可用”util:”命名空間來從 properties 文件中創建出一個 propertiesbean,然後利用 setter 方法注入 bean 的引用。

16、請解釋 Spring Bean 的自動裝配?
在 Spring 框架中,在配置文件中設定 bean 的依賴關係是一個很好的機制, Spring 容器還可以自動裝配合作關係 bean 之間的關聯關係。這意味着 Spring 可以通過向 Bean Factory 中注入的方式自動搞定 bean 之間的依賴關係。自動裝配可以設置在每個 bean 上,也可以設定在特定的 bean上。

下面的 XML 配置文件表明瞭如何根據名稱將一個 bean 設置爲自動裝配:

<bean id="employeeDAO" class="com.howtodoinjava.EmployeeDAOImpl"
autowire="byName" />

除了 bean 配置文件中提供的自動裝配模式,還可以使用@Autowired 註解來自動裝配指定的 bean。在使用@Autowired 註解之前需要在按照如下的配置方式在 Spring 配置文件進行配置纔可以使用。

<context:annotation-config />

也可以通過在配置文件中配置 AutowiredAnnotationBeanPostProcessor 達到相同的效果。

<bean class
="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

配置好以後就可以使用@Autowired 來標註了。

@Autowired
public EmployeeDAOImpl ( EmployeeManager manager ) {
	this.manager = manager;
}

17、請解釋自動裝配模式的區別?
在 Spring 框架中共有 5 種自動裝配,讓我們逐一分析。

  1. no:這是 Spring 框架的默認設置,在該設置下自動裝配是關閉的,開發者需要自行在 bean 定義中用標籤明確的設置依賴關係。
  2. byName:該選項可以根據 bean 名稱設置依賴關係。當向一個 bean 中自動裝配一個屬性時,容器將根據 bean 的名稱自動在在配置文件中查詢一個匹配的 bean。如果找到的話,就裝配這個屬性,如果沒找到的話就報錯。
  3. byType:該選項可以根據 bean 類型設置依賴關係。當向一個 bean 中自動裝配一個屬性時,容器將根據 bean 的類型自動在在配置文件中查詢一個匹配的 bean。如果找到的話,就裝配這個屬性,
    如果沒找到的話就報錯。
  4. constructor:構造器的自動裝配和 byType 模式類似,但是僅僅適用於與有構造器相同參數的bean,如果在容器中沒有找到與構造器參數類型一致的 bean,那麼將會拋出異常。
  5. autodetect:該模式自動探測使用構造器自動裝配或者 byType 自動裝配。首先,首先會嘗試找合適的帶參數的構造器,如果找到的話就是用構造器自動裝配,如果在 bean 內部沒有找到相應的構造器或者是無參構造器,容器就會自動選擇 byTpe 的自動裝配方式。

18、如何開啓基於註解的自動裝配?
要使用 @Autowired,需要註冊 AutowiredAnnotationBeanPostProcessor,可以
有以下兩種方式來實現:
1.引入配置文件中的<bean>下引入 <context:annotation-config>

<beans>
	<context:annotation-config />
</beans>

2.在 bean 配置文件中直接引入 AutowiredAnnotationBeanPostProcessor

<beans>
	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
</beans>

19、請舉例解釋@Required 註解?
在產品級別的應用中, IoC 容器可能聲明瞭數十萬了 bean, bean 與 bean 之間有着複雜的依賴關係。設值註解方法的短板之一就是驗證所有的屬性是否被註解是一項十分困難的操作。可以通過在<bean>中設置“dependency-check”來解決這個問題。
在應用程序的生命週期中,你可能不大願意花時間在驗證所有 bean 的屬性是否按照上下文文件正確配置。或者你寧可驗證某個 bean 的特定屬性是否被正確的設置。即使是用“dependencycheck”屬性也不能很好的解決這個問題,在這種情況下,你需要使用@Required 註解。

需要用如下的方式使用來標明 bean 的設值方法。

public class EmployeeFactoryBean extends AbstractFactoryBean<Object>{
	private String designation;
	public String getDesignation() {
		return designation;
	}
	@Required
	public void setDesignation(String designation) {
		this.designation = designation;
	}
	//more code here
}

RequiredAnnotationBeanPostProcessor 是 Spring 中的後置處理用來驗證被
@Required 註解的 bean 屬性是否被正確的設置了。在使用
RequiredAnnotationBeanPostProcesso 來驗證 bean 屬性之前,首先要在 IoC 容器中對其進行註冊:

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />

但是如果沒有屬性被用 @Required 註解過的話,後置處理器會拋出一個
BeanInitializationException 異常。
20、請舉例解釋@Autowired 註解?
@Autowired 註解對自動裝配何時何處被實現提供了更多細粒度的控制。 @Autowired 註解可以像@Required 註解、構造器一樣被用於在 bean 的設值方法上自動裝配 bean的屬性,一個參數或者帶有任意名稱或帶有多個參數的方法。
比如,可以在設值方法上使用@Autowired 註解來替代配置文件中的 元
素。當 Spring 容器在 setter 方法上找到@Autowired 註解時,會嘗試用 byType
自動裝配。
當然我們也可以在構造方法上使用@Autowired 註解。帶有@Autowired 註解的構造方法意味着在創建一個 bean 時將會被自動裝配,即便在配置文件中使用<constructor-arg> 元素。

public class TextEditor {
	private SpellChecker spellChecker;
	@Autowired
	public TextEditor(SpellChecker spellChecker){
		System.out.println("Inside TextEditor constructor." );
		this.spellChecker = spellChecker;
	}
	public void spellCheck(){
		spellChecker.checkSpelling();
	}
}

下面是沒有構造參數的配置方式:

<beans>
	<context:annotation-config/>
	<!-- Definition for textEditor bean without constructor-arg -->
	<bean id="textEditor" class="com.howtodoinjava.TextEditor"/>
	<!-- Definition for spellChecker bean -->
	<bean id="spellChecker" class="com.howtodoinjava.SpellChecker"/>
</beans>

21、請舉例說明@Qualifier 註解?
@Qualifier 註解意味着可以在被標註 bean 的字段上可以自動裝配。 Qualifier 注
解可以用來取消 Spring 不能取消的 bean 應用。
下面的示例將會在 Customer 的 person 屬性中自動裝配 person 的值。

public class Customer{
	@Autowired
	private Person person;
}

下面我們要在配置文件中來配置 Person 類。

<bean id="customer" class="com.somnus.common.Customer" />
<bean id="personA" class="com.somnus.common.Person" >
	<property name="name" value="lokesh" />
</bean>
<bean id="personB" class="com.somnus.common.Person" >
	<property name="name" value="alex" />
</bean>

Spring 會知道要自動裝配哪個 person bean 麼?不會的,但是運行上面的示例
時,會拋出下面的異常:

Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [com.howtodoinjava.common.Person] is defined:
expected single matching bean but found 2: [personA, personB]

要解決上面的問題,需要使用 @Quanlifier 註解來告訴 Spring 容器要裝配哪個 bean:

public class Customer{
@Autowired
@Qualifier("personA")
	private Person person;
}

22、構造方法注入和設值注入有什麼區別?
請注意以下明顯的區別:

  1. 在設值注入方法支持大部分的依賴注入,如果我們僅需 要注入 int、 string 和 long 型的變量,我們不要用設值的方法注入。對於基本類型,如果我們沒有注入的話,可以爲基本類型設置默認值。
    在構造方法 注入不支持大部分的依賴注入,因爲在調用構造方法中必須傳入正確的構造參數,否則的話爲報錯。
  2. 設值注入不會重寫構造方法的值。如果我們對同一個變量同時使用了構造方法注入又使用了設置方法注入的話,那麼構造方法將不能覆蓋由設值方法注入的值。很明顯,因爲構造方法盡在對象被創建時調用。
  3. 在使用設值注入時有可能還不能保證某種依賴是否已經被注入,也就是說這時對象的依賴關係有可能是不完整的。而在另一種情況下,構造器注入則不允許生成依賴關係不完整的對象。
  4. 在設值注入時如果對象 A 和對象 B 互相依賴,在創建對象 A 時 Spring 會拋出
    sObjectCurrentlyInCreationException 異常,因爲在 B 對象被創建之前 A 對
    象是不能被創建的,反之亦然。所以 Spring 用設值注入的方法解決了循環依賴的問題,因對象的設值方法是在對象被創建之前被調用的。

23、 Spring 框架中有哪些不同類型的事件?
Spring 的 ApplicationContext 提供了支持事件和代碼中監聽器的功能。
我們可以創建 bean 用來監聽在 ApplicationContext 中發佈的事件。 ApplicationEvent類和在 ApplicationContext 接口中處理的事件,如果一個 bean 實現了ApplicationListener 接口,當一個 ApplicationEvent 被髮布以後, bean 會自動被通知。

public class AllApplicationEventListener implements ApplicationListener
< ApplicationEvent >{
	@Override
	public void onApplicationEvent(ApplicationEvent applicationEvent){
		//process event
	}
}

Spring 提供了以下 5 中標準的事件:

  1. 上下文更新事件(ContextRefreshedEvent):該事件會在 ApplicationContext 被初始化或者更新時發佈。也可以在調用 ConfigurableApplicationContext 接口中的 refresh()方法時被觸發。
  2. 上下文開始事件(ContextStartedEvent):當容器調用 ConfigurableApplicationContext 的Start()方法開始/重新開始容器時觸發該事件。
  3. 上下文停止事件(ContextStoppedEvent):當容器調用 ConfigurableApplicationContext 的Stop()方法停止容器時觸發該事件。
  4. 上下文關閉事件(ContextClosedEvent):當 ApplicationContext 被關閉時觸發該事件。容器
    被關閉時,其管理的所有單例 Bean 都被銷燬。
  5. 請求處理事件(RequestHandledEvent):在 Web 應用中,當一個 http 請求(request)結束觸發該事件。
    除了上面介紹的事件以外,還可以通過擴展 ApplicationEvent 類來開發自定義的事件。
public class CustomApplicationEvent extends ApplicationEvent{
	public CustomApplicationEvent ( Object source, final String msg ){
		super(source);
		System.out.println("Created a Custom event");
	}
}

爲了監聽這個事件,還需要創建一個監聽器:

public class CustomEventListener implements ApplicationListener <CustomApplicationEvent >{
	@Override
	public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
		//handle event
	}
}

之後通過 applicationContext 接口的 publishEvent()方法來發布自定義事件。

CustomApplicationEvent customEvent = new
CustomApplicationEvent(applicationContext, "Test message");
applicationContext.publishEvent(customEvent);

24、 FileSystemResource 和 ClassPathResource 有何區別?
在 FileSystemResource 中需要給出 spring-config.xml 文件在你項目中的相對路徑或者絕對路徑。在 ClassPathResource 中 spring 會在 ClassPath 中自動搜尋配置文件,所以要把ClassPathResource 文件放在 ClassPath 下。
如果將 spring-config.xml 保存在了 src 文件夾下的話,只需給出配置文件的名稱即可,因爲src 文件夾是默認。
簡而言之, ClassPathResource 在環境變量中讀取配置文件, FileSystemResource 在配置文件中讀取配置文件。

25、 Spring 框架中都用到了哪些設計模式?
Spring 框架中使用到了大量的設計模式,下面列舉了比較有代表性的:
o 代理模式—在 AOP 和 remoting 中被用的比較多。
o 單例模式—在 spring 配置文件中定義的 bean 默認爲單例模式。o 模板方法—用來解決代碼重複的問題。比如. RestTemplate, JmsTemplate, JpaTempl
ate。
o 前端控制器—Spring 提供了 DispatcherServlet 來對請求進行分發。
o 視圖幫助(View Helper )—Spring 提供了一系列的 JSP 標籤,高效宏來輔助將分散的代碼整合在視圖裏。
o 依賴注入—貫穿於 BeanFactory / ApplicationContext 接口的核心理念。
o 工廠模式—BeanFactory 用來創建對象的實例

26、 開發中主要使用 Spring 的什麼技術 ?

  1. IOC 容器管理各層的組件
  2. 使用 AOP 配置聲明式事務
  3. 整合其他框架.

27、簡述 AOP 和 IOC 概念 AOP:
Aspect Oriented Program, 面向(方面)切面的編程;Filter(過濾器) 也是一種 AOP. AOP 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向對象編程) 的補充. AOP 的主要編程對象是切面(aspect), 而切面模塊化橫切關注點.
可以舉例通過事務說明.

IOC: Invert Of Control, 控制反轉. 也成爲 DI(依賴注入)其思想是反轉 資源獲取的方向. 傳統的資源查找方式要求組件向容器發起請求查找資源.作爲 迴應, 容器適時的返回資源. 而應用了IOC 之後, 則是容器主動地將資源推送 給它所管理的組件,組件所要做的僅是選擇一種合適的方式來接受資源. 這種行 爲也被稱爲查找的被動形式

28、 在 Spring 中如何配置 Bean ?
Bean 的配置方式: 通過全類名(反射)、通過工廠方法(靜態工廠方法 & 實 例工廠方法)、FactoryBean

29、 IOC 容器對 Bean 的生命週期:
①. 通過構造器或工廠方法創建 Bean 實例
②. 爲 Bean 的屬性設置值和對其他 Bean 的引用
③ . 將 Bean 實 例 傳 遞 給 Bean 後 置 處 理 器 的 postProcessBeforeInitialization 方法
④. 調用 Bean 的初始化方法(init-method)⑤ . 將 Bean 實 例 傳 遞 給 Bean 後 置 處 理 器 的 postProcessAfterInitialization 方法
⑦. Bean 可以使用了
⑧. 當容器關閉時, 調用 Bean 的銷燬方法(destroy-method)

29、構造器注入和 setter 依賴注入,那種方式更好?
每種方式都有它的缺點和優點。構造器注入保證所有的注入都被初始化,但是setter 注入提供更好的靈活性來設置可選依賴。如果使用 XML 來描述依賴,Setter 注入的可讀寫會更強。經驗法則是強制依賴使用構造器注入,可選依賴使用 setter 注入。

30、依賴注入和工程模式之間有什麼不同?
雖然兩種模式都是將對象的創建從應用的邏輯中分離,但是依賴注入比工程模式更清晰。通過依賴注入,你的類就是 POJO,它只知道依賴而不關心它們怎麼獲取。使用工廠模式,你的類需要通過工廠來獲取依賴。因此,使用 DI 會比使用工廠模式更容易測試。

31、講講springIOC/AOP怎麼實現的能講講原理嗎?說說Ioc容器的加載過程Spring IOC底層存儲結構 ? 自己實現IOC要怎麼做,哪些步驟?

ioc:beanfactory,在用到的時候加載到concurrenthashmap中,如果對象需要其他依賴,會遞歸實現裏面的依賴。applicationcontext就是在容器加載的時候就把全部的bean放到concurrenthashmap中
aop:切面編程,一般是將可複用的方法在切點前後執行,實現方式有aspectj(靜態織入),cglib和jdk動態代理順帶一提,dubbo中用了裝飾器把invoker包裝成wrapper

實現IOC的步驟:

  1. 定義用來描述bean的配置的Java類。
  2. 解析bean的配置,將bean的配置信息轉換爲BeanDefinition對象保存到內存中,spring中採用HashMap進行對象存儲,其中會用到一些xml的解析技術,如dom4j等。
  3. 遍歷存放BeanDefinition的HashMap對象,逐條取出BeanDefinition對象,獲取bean的配置信息,利用Java的反射機制實例化對象,將實例化後的對象保存到另外一個Map中即可。

32、aop的應用場景?
場景一: 記錄日誌
場景二: 監控方法運行時間 (監控性能)
場景三: 權限控制
場景四: 緩存優化 (第一次調用查詢數據庫,將查詢結果放入內存對象, 第二次調用, 直接從內存對象返回,不需要查詢數據庫 )
場景五: 事務管理 (調用方法前開啓事務, 調用方法後提交關閉事務 )

33、AOP的原理是什麼? 除了動態代理還有什麼?
在 Spring 中 AOP 代理使用 JDK 動態代理和 CGLIB 代理來實現,默認如果目標對象是接口,則使用 JDK 動態代理,否則使用 CGLIB 來生成代理類
具體原理可以參考 Spring 框架之 AOP 原理深度剖析!|CSDN 博文精選

注意:問到aop這個地方肯定會聯繫到 兩種動態代理,需要掌握兩種代理的區別,實現方式等知識。

34、攔截器和AOP的關係 Spring攔截器的底層是怎麼實現的
這個上題中推薦的博文有回答

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

36、Spring支持的事務管理類型有哪些?你在項目中使用哪種方式?
37、什麼是Spring的聲明式事務管理。
38、AOP 的 @transaction 是怎麼做的?爲什麼不加這個註解就不是事務?
39、spring事務傳播機制
上面幾道題可以看我寫的這篇博客 事務部分

40、如果spring的事務中拋出了IOException,會回滾嗎?
根據事務的使用規則如果將異常向上拋出而不是try catch就能實現正常的事務回滾,所以答案是會回滾

41、在spring中雙重檢查鎖有效嗎
這一題實在奇怪,是在問spring單例的實現方式能不能用雙重鎖嗎?而spring默認單例的實現是用的單例檢測表,這一題本人無能爲裏,網上只有問題沒找到答案。

42、依賴注入和工程模式之間有什麼不同?
雖然兩種模式都是將對象的創建從應用的邏輯中分離,但是依賴注入比工程模式更清晰。通過依賴注入,你的類就是 POJO,它只知道依賴而不關心它們怎麼獲取。使用工廠模式,你的類需要通過工廠來獲取依賴。因此,使用 DI 會比使用工廠模式更容易測試。

43、看過Spring源碼沒,說說Ioc容器的加載過程吧
我感覺要說沒看過就相當於直接涼了,對面可能會認爲你不愛搞技術

簡單概括:
1.刷新預處理
2.將配置信息解析,註冊到BeanFactory
3.設置bean的類加載器
4.如果有第三方想再bean加載註冊完成後,初始化前做點什麼(例如修改屬性的值,修改bean的scope爲單例或者多例。),提供了相應的模板方法,後面還調用了這個方法的實現,並且把這些個實現類註冊到對應的容器中
5.初始化當前的事件廣播器
6.初始化所有的bean
7.廣播applicationcontext初始化完成。

//來自於AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
       //進行加鎖處理
   synchronized (this.startupShutdownMonitor) {
       // 進行刷新容器的準備工作,比如設定容器開啓時間,標記容器已啓動狀態等等
       prepareRefresh();

       // 讓子類來刷新創建容器
       // 這步比較關鍵,這步完成後,配置文件就會解析成一個個 Bean 定義,註冊到 BeanFactory 中,
       // 當然,這裏說的 Bean 還沒有初始化,只是配置信息都提取出來了,
       // 註冊也只是將這些信息都保存到了註冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

       // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
       prepareBeanFactory(beanFactory);

       try {
           // 這裏需要知道 BeanFactoryPostProcessor 這個知識點,
           //Bean 如果實現了此接口,那麼在容器初始化以後,Spring 會負責調用裏面的 postProcessBeanFactory 方法。
           // 這裏是提供給子類的擴展點,到這裏的時候,所有的 Bean 都加載、註冊完成了,但是都還沒有初始化
           // 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現類或做點什麼事
           postProcessBeanFactory(beanFactory);

           // 調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
           invokeBeanFactoryPostProcessors(beanFactory);

           // 註冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
           // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
           // 兩個方法分別在 Bean 初始化之前和初始化之後得到執行。注意,到這裏 Bean 還沒初始化
           registerBeanPostProcessors(beanFactory);

           // 初始化當前 ApplicationContext 的 MessageSource
           initMessageSource();

           // 初始化當前 ApplicationContext 的事件廣播器
           initApplicationEventMulticaster();

           // 從方法名就可以知道,典型的模板方法(鉤子方法),
           // 具體的子類可以在這裏初始化一些特殊的 Bean(在初始化 singleton beans 之前)
           onRefresh();

           // 註冊事件監聽器,監聽器需要實現 ApplicationListener 接口
           registerListeners();

           // 初始化所有的 singleton beans(lazy-init 的除外)
           // 重點方法將會在下一個章節進行說明
           finishBeanFactoryInitialization(beanFactory);

           // 最後,廣播事件,ApplicationContext 初始化完成
           finishRefresh();
           }
           catch (BeansException ex) {
               if (logger.isWarnEnabled()) {
                   logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
               }
                   // 銷燬已經初始化的 singleton 的 Beans,以免有些 bean 會一直佔用資源
                   destroyBeans();

                   // Reset 'active' flag.
                   cancelRefresh(ex);

                   // 把異常往外拋
                   throw ex;
             }
             finally {
               // Reset common introspection caches in Spring's core, since we
               // might not ever need metadata for singleton beans anymore...
               resetCommonCaches();
             }
     }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章