本人收集了一些在你們在面試時被常常問及的關於Spring的主要問題,這些問題有可能在你下次面試時就會被問到。對於本文中未說起的Spring其餘模塊,我會單獨分享面試的問題和答案。html
歡迎你們向我推薦你在面試過程當中遇到關於Spring的問題。我會把你們推薦的問題添加到下面的Spring經常使用面試題清單中供你們參考。前端
Spring框架是一個爲Java應用程序的開發提供了綜合、普遍的基礎性支持的Java平臺。Spring幫助開發者解決了開發中基礎性的問題,使得開發人員能夠專一於應用程序的開發。Spring框架自己亦是按照設計模式精心打造,這使得咱們能夠在開發環境中安心的集成Spring框架,沒必要擔憂Spring是如何在後臺進行工做的。java
Spring框架至今已集成了20多個模塊。這些模塊主要被分以下圖所示的核心容器、數據訪問/集成,、Web、AOP(面向切面編程)、工具、消息和測試模塊。web
更多信息:Spring 框架教程。面試
下面列舉了一些使用Spring框架帶來的主要好處:spring
控制反轉是應用於軟件工程領域中的,在運行時被裝配器對象來綁定耦合對象的一種編程技巧,對象之間耦合關係在編譯時一般是未知的。在傳統的編程方式中,業務邏輯的流程是由應用程序中的早已被設定好關聯關係的對象來決定的。在使用控制反轉的狀況下,業務邏輯的流程是由對象關係圖來決定的,該對象關係圖由裝配器負責實例化,這種實現方式還能夠將對象之間的關聯關係的定義抽象化。而綁定的過程是經過「依賴注入」實現的。shell
控制反轉是一種以給予應用程序中目標組件更多控制爲目的設計範式,並在咱們的實際工做中起到了有效的做用。編程
依賴注入是在編譯階段還沒有知所需的功能是來自哪一個的類的狀況下,將其餘對象所依賴的功能對象實例化的模式。這就須要一種機制用來激活相應的組件以提供特定的功能,因此依賴注入是控制反轉的基礎。不然若是在組件不受框架控制的狀況下,框架又怎麼知道要建立哪一個組件?json
在Java中依然注入有如下三種實現方式:設計模式
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 容器的核心接口。
BeanFactory 能夠理解爲含有bean集合的工廠類。BeanFactory 包含了種bean的定義,以便在接收到客戶端請求時將對應的bean實例化。
BeanFactory還能在實例化對象的時生成協做類之間的關係。此舉將bean自身與bean客戶端的配置中解放出來。BeanFactory還包含了bean生命週期的控制,調用客戶端的初始化方法(initialization methods)和銷燬方法(destruction methods)。
從表面上看,application context如同bean factory同樣具備bean定義、bean關聯關係的設置,根據請求分發bean的功能。但application context在此基礎上還提供了其餘的功能。
如下是三種較常見的 ApplicationContext 實現方式:
一、ClassPathXmlApplicationContext:從classpath的XML配置文件中讀取上下文,並生成上下文定義。應用程序上下文從程序環境變量中取得。
1
|
ApplicationContext context =
new
ClassPathXmlApplicationContext(「bean.xml」);
|
二、FileSystemXmlApplicationContext :由文件系統中的XML配置文件讀取上下文。
1
|
ApplicationContext context =
new
FileSystemXmlApplicationContext(「bean.xml」);
|
三、XmlWebApplicationContext:由Web應用的XML文件讀取上下文。
將Spring配置到應用開發中有如下三種方式:
在Spring框架中,依賴和服務須要在專門的配置文件來實現,我經常使用的XML格式的配置文件。這些配置文件的格式一般用<beans>
開頭,而後一系列的
bean
定義和專門的應用配置選項組成。
SpringXML配置的主要目的時候是使全部的Spring組件均可以用xml文件的形式來進行配置。這意味着不會出現其餘的Spring配置類型(好比聲明的方式或基於Java Class的配置方式)
Spring的XML配置方式是使用被Spring命名空間的所支持的一系列的XML標籤來實現的。Spring有如下主要的命名空間:context、beans、jdbc、tx、aop、mvc和aso。
1
2
3
4
5
6
7
8
9
|
<
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,這件最簡單的配置便能知足應用程序配置運行時組件的需求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<
web-app
>
<
display-name
>Archetype Created Web Application</
display-name
>
<
servlet
>
<
servlet-name
>spring</
servlet-name
>
<
servlet-class
>
org.springframework.web.servlet.DispatcherServlet
</
servlet-class
>
<
load-on-startup
>1</
load-on-startup
>
</
servlet
>
<
servlet-mapping
>
<
servlet-name
>spring</
servlet-name
>
<
url-pattern
>/</
url-pattern
>
</
servlet-mapping
>
</
web-app
>
|
Spring對Java配置的支持是由@Configuration註解和@Bean註解來實現的。由@Bean註解的方法將會實例化、配置和初始化一個新對象,這個對象將由Spring的IoC容器來管理。@Bean聲明所起到的做用與<bean/> 元素相似。被@Configuration所註解的類則表示這個類的主要目的是做爲bean定義的資源。被@Configuration聲明的類能夠經過在同一個類的內部調用@bean方法來設置嵌入bean的依賴關係。
最簡單的@Configuration 聲明類請參考下面的代碼:
1
2
3
4
5
6
7
8
|
@Configuration
public
class
AppConfig
{
@Bean
public
MyService myService() {
return
new
MyServiceImpl();
}
}
|
對於上面的@Beans配置文件相同的XML配置文件以下:
1
2
3
|
<
beans
>
<
bean
id
=
"myService"
class
=
"com.howtodoinjava.services.MyServiceImpl"
/>
</
beans
>
|
上述配置方式的實例化方式以下:利用AnnotationConfigApplicationContext 類進行實例化
1
2
3
4
5
|
public
static
void
main(String[] args) {
ApplicationContext ctx =
new
AnnotationConfigApplicationContext(AppConfig.
class
);
MyService myService = ctx.getBean(MyService.
class
);
myService.doStuff();
}
|
要使用組件組建掃描,僅需用@Configuration進行註解便可:
1
2
3
4
5
|
@Configuration
@ComponentScan
(basePackages =
"com.howtodoinjava"
)
public
class
AppConfig {
...
}
|
在上面的例子中,com.acme包首先會被掃到,而後再容器內查找被@Component 聲明的類,找到後將這些類按照Sring bean定義進行註冊。
若是你要在你的web應用開發中選用上述的配置的方式的話,須要用AnnotationConfigWebApplicationContext 類來讀取配置文件,能夠用來配置Spring的Servlet監聽器ContrextLoaderListener或者Spring MVC的DispatcherServlet。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<
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.AnnotationConfigWebApplicationContext
</
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
>
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet</
servlet-class
>
<!-- 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 comma- or 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
>
|
Spring在2.5版本之後開始支持用註解的方式來配置依賴注入。能夠用註解的方式來替代XML方式的bean描述,能夠將bean描述轉移到組件類的內部,只須要在相關類上、方法上或者字段聲明上使用註解便可。註解注入將會被容器在XML注入以前被處理,因此後者會覆蓋掉前者對於同一個屬性的處理結果。
註解裝配在Spring中是默認關閉的。因此須要在Spring文件中配置一下才能使用基於註解的裝配模式。若是你想要在你的應用程序中使用關於註解的方法的話,請參考以下的配置。
1
2
3
4
5
6
|
<
beans
>
<
context:annotation-config
/>
<!-- bean definitions go here -->
</
beans
>
|
在 <context:annotation-config/>標籤配置完成之後,就能夠用註解的方式在Spring中向屬性、方法和構造方法中自動裝配變量。
下面是幾種比較重要的註解類型:
Spring Bean的生命週期簡單易懂。在一個bean實例被初始化時,須要執行一系列的初始化操做以達到可用的狀態。一樣的,當一個bean不在被調用時須要進行相關的析構操做,並從bean容器中移除。
Spring bean factory 負責管理在spring容器中被建立的bean的生命週期。Bean的生命週期由兩組回調(call back)方法組成。
Spring框架提供瞭如下四種方式來管理bean的生命週期事件:
使用customInit()
和 customDestroy()
方法管理
bean
生命週期的代碼樣例以下:
1
2
3
4
|
<
beans
>
<
bean
id
=
"demoBean"
class
=
"com.howtodoinjava.task.DemoBean"
init-method
=
"customInit"
destroy-method
=
"customDestroy"
></
bean
>
</
beans
>
|
更多內容請參考:Spring生命週期Spring Bean Life Cycle。
Spring容器中的bean能夠分爲5個範圍。全部範圍的名稱都是自說明的,可是爲了不混淆,仍是讓咱們來解釋一下:
全局做用域與Servlet中的session做用域效果相同。
更多內容請參考 : Spring Bean Scopes。
在Spring框架中,不管什麼時候bean被使用時,當僅被調用了一個屬性。一個明智的作法是將這個bean聲明爲內部bean。內部bean能夠用setter注入「屬性」和構造方法注入「構造參數」的方式來實現。
好比,在咱們的應用程序中,一個Customer類引用了一個Person類,咱們的要作的是建立一個Person的實例,而後在Customer內部使用。
1
2
3
4
5
6
|
public
class
Customer
{
private
Person person;
//Setters and Getters
}
|
1
2
3
4
5
6
7
8
|
public
class
Person
{
private
String name;
private
String address;
private
int
age;
//Setters and Getters
}
|
內部bean的聲明方式以下:
1
2
3
4
5
6
7
8
9
10
|
<
bean
id
=
"CustomerBean"
class
=
"com.howtodoinjava.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
>
|
Spring框架並無對單例bean進行任何多線程的封裝處理。關於單例bean的線程安全和併發問題須要開發者自行去搞定。但實際上,大部分的Spring bean並無可變的狀態(好比Serview類和DAO類),因此在某種程度上說Spring的單例bean是線程安全的。若是你的bean有多種狀態的話(好比 View Model 對象),就須要自行保證線程安全。
最淺顯的解決辦法就是將多態bean的做用域由「singleton」變動爲「prototype」。
Spring提供瞭如下四種集合類的配置元素:
下面看一下具體的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
<
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"
>admin@nospam.com</
prop
>
<
prop
key
=
"support"
>support@nospam.com</
prop
>
</
props
>
</
property
>
</
bean
>
</
beans
>
|
第一種方法是使用以下面代碼所示的<props> 標籤:
1
2
3
4
5
6
7
8
9
10
11
|
<
bean
id
=
"adminUser"
class
=
"com.howtodoinjava.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的引用。
在Spring框架中,在配置文件中設定bean的依賴關係是一個很好的機制,Spring容器還能夠自動裝配合做關係bean之間的關聯關係。這意味着Spring能夠經過向Bean Factory中注入的方式自動搞定bean之間的依賴關係。自動裝配能夠設置在每一個bean上,也能夠設定在特定的bean上。
下面的XML配置文件代表瞭如何根據名稱將一個bean設置爲自動裝配:
1
|
<
bean
id
=
"employeeDAO"
class
=
"com.howtodoinjava.EmployeeDAOImpl"
autowire
=
"byName"
/>
|
除了bean配置文件中提供的自動裝配模式,還可使用@Autowired
註解來自動裝配指定的
bean
。在使用
@Autowired
註解以前須要在按照以下的配置方式在
Spring
配置文件進行配置纔可使用。
1
|
<
context:annotation-config
/>
|
也能夠經過在配置文件中配置AutowiredAnnotationBeanPostProcessor
達到相同的效果。
1
|
<
bean
class
=
"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"
/>
|
配置好之後就可使用@Autowired
來標註了。
1
2
3
4
|
@Autowired
public
EmployeeDAOImpl ( EmployeeManager manager ) {
this
.manager = manager;
}
|
在Spring框架中共有5種自動裝配,讓咱們逐一分析。
要使用 @Autowired
,須要註冊
AutowiredAnnotationBeanPostProcessor
,能夠有如下兩種方式來實現:
一、引入配置文件中的<bean>下引入 <context:annotation-config>
1
2
3
|
<
beans
>
<
context:annotation-config
/>
</
beans
>
|
二、在bean配置文件中直接引入AutowiredAnnotationBeanPostProcessor
1
2
3
|
<
beans
>
<
bean
class
=
"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"
/>
</
beans
>
|
在產品級別的應用中,IoC容器可能聲明瞭數十萬了bean,bean與bean之間有着複雜的依賴關係。設值註解方法的短板之一就是驗證全部的屬性是否被註解是一項十分困難的操做。能夠經過在<bean>中設置「dependency-check」來解決這個問題。
在應用程序的生命週期中,你可能不大願意花時間在驗證全部bean的屬性是否按照上下文文件正確配置。或者你寧肯驗證某個bean的特定屬性是否被正確的設置。即便是用「dependency-check」屬性也不能很好的解決這個問題,在這種狀況下,你須要使用@Required
註解。
須要用以下的方式使用來標明bean的設值方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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
容器中對其進行註冊:
1
|
<
bean
class
=
"org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"
/>
|
可是若是沒有屬性被用 @Required
註解過的話,後置處理器會拋出一個BeanInitializationException
異常。
@Autowired註解對自動裝配什麼時候何處被實現提供了更多細粒度的控制。@Autowired
註解能夠像
@Required
註解、構造器同樣被用於在
bean
的設值方法上自動裝配
bean
的屬性,一個參數或者帶有任意名稱或帶有多個參數的方法。
好比,能夠在設值方法上使用@Autowired
註解來替代配置文件中的
<property>
元素。當
Spring
容器在
setter
方法上找到
@Autowired
註解時,會嘗試用
byType 自動裝配。
固然咱們也能夠在構造方法上使用@Autowired
註解。帶有@Autowired
註解的構造方法意味着在建立一個bean時將會被自動裝配,即使在配置文件中使用<constructor-arg>
元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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();
}
}
|
下面是沒有構造參數的配置方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
beans
>
<
context:annotation-config
/>
<!-- Definition for textEditor bean without constructor-arg -->
<
bean
id
=
"textEditor"
class
=
"com.howtodoinjava.TextEditor"
>
</
bean
>
<!-- Definition for spellChecker bean -->
<
bean
id
=
"spellChecker"
class
=
"com.howtodoinjava.SpellChecker"
>
</
bean
>
</
beans
>
|
@Qualifier
註解意味着能夠在被標註
bean
的字段上能夠自動裝配。
Qualifier註解能夠用來取消Spring不能取消的bean應用。
下面的示例將會在Customer的person屬性中自動裝配person的值。
1
2
3
4
5
|
public
class
Customer
{
@Autowired
private
Person person;
}
|
下面咱們要在配置文件中來配置Person類。
1
2
3
4
5
6
7
8
9
|
<
bean
id
=
"customer"
class
=
"com.howtodoinjava.common.Customer"
/>
<
bean
id
=
"personA"
class
=
"com.howtodoinjava.common.Person"
>
<
property
name
=
"name"
value
=
"lokesh"
/>
</
bean
>
<
bean
id
=
"personB"
class
=
"com.howtodoinjava.common.Person"
>
<
property
name
=
"name"
value
=
"alex"
/>
</
bean
>
|
Spring
會知道要自動裝配哪一個person bean麼?不會的,可是運行上面的示例時,
會拋出下面的異常:
1
2
3
|
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:
1
2
3
4
5
6
|
public
class
Customer
{
@Autowired
@Qualifier
(
"personA"
)
private
Person person;
}
|
請注意如下明顯的區別:
ObjectCurrentlyInCreationException異常,由於在B對象被建立以前A對象是不能被建立的,反之亦然。因此Spring用設值注入的方法解決了循環依賴的問題,因對象的設值方法是在對象被建立以前被調用的。
Spring的ApplicationContext
提供了支持事件和代碼中監聽器的功能。
咱們能夠建立bean用來監聽在ApplicationContext
中發佈的事件。ApplicationEven
t類和在ApplicationContext
接口
中處理的事件,若是一個bean實現了ApplicationListener
接口,當一個ApplicationEvent
被髮布之後,bean會自動被通知。
1
2
3
4
5
6
7
8
|
public
class
AllApplicationEventListener
implements
ApplicationListener < ApplicationEvent >
{
@Override
public
void
onApplicationEvent(ApplicationEvent applicationEvent)
{
//process event
}
}
|
Spring 提供瞭如下5中標準的事件:
除了上面介紹的事件之外,還能夠經過擴展ApplicationEvent
類來開發自定義的事件。
1
2
3
4
5
6
7
8
|
public
class
CustomApplicationEvent
extends
ApplicationEvent
{
public
CustomApplicationEvent ( Object source,
final
String msg )
{
super
(source);
System.out.println(
"Created a Custom event"
);
}
}
|
爲了監聽這個事件,還須要建立一個監聽器:
1
2
3
4
5
6
7
|
public
class
CustomEventListener
implements
ApplicationListener < CustomApplicationEvent >
{
@Override
public
void
onApplicationEvent(CustomApplicationEvent applicationEvent) {
//handle event
}
}
|
以後經過applicationContext接口的publishEvent()方法來發布自定義事件。
1
2
|
CustomApplicationEvent customEvent =
new
CustomApplicationEvent(applicationContext,
"Test message"
);
applicationContext.publishEvent(customEvent);
|
在FileSystemResource
中須要給出spring-config.xml
文件在你項目中的相對路徑或者絕對路徑。在ClassPathResource
中spring會在ClassPath中自動搜尋配置文件,因此要把ClassPathResource
文件放在ClassPath下。
若是將spring-config.xml
保存在了src文件夾下的話,只需給出配置文件的名稱便可,由於src文件夾是默認。
簡而言之,ClassPathResource在環境變量中讀取配置文件,FileSystemResource在配置文件中讀取配置文件。
Spring框架中使用到了大量的設計模式,下面列舉了比較有表明性的:
JmsTemplate
, JpaTemplate。
DispatcherServlet
來對請求進行分發。
BeanFactory
/ ApplicationContext
接口的核心理念。