面試準備:Spring/Spring MVC常見面試題彙總

1.Spring框架有什麼優點?

  1. 輕量:低侵入式設計,代碼污染極低
  2. Spring的DI機制和容器實現了對象的管理和裝配,提高了組件之間的解耦,方便集成,通過配置和簡單的對象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro…
  3. Spring的AOP支持允許將一些通用任務如安全、事務、日誌等進行集中式管理,從而提供了更好的複用
  4. Spring的ORM和DAO提供了與第三方持久層框架的良好整合,並簡化了底層的數據庫訪問
  5. Spring並不強制應用完全依賴於Spring,開發者可自由選用Spring框架的部分或全部

2.什麼是AOP?

AOP:Aspect Oriented Programming,面向切面編程。
"切面"就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,利於可操作性和可維護性。

3. 實現AOP的方式(AOP織入的三種時期)?Spring AOP是怎麼實現的?

  • 靜態AOP:

在編譯期,切面直接以字節碼的形式編譯到目標字節碼文件中。 在編譯的時候將AOP邏輯織入到代碼中,需要專有的編譯器和織入器。

優點:被織入的類性能不受影響。
缺點:不夠靈活

  • 動態AOP(JDK動態代理):

在運行期,目標類加載後,爲接口動態生成代理類,將切面植入到代理類中。Java從1.3引入動態代理。實現原理是爲被代理的業務接口生成代理類,將AOP邏輯寫入到代理類中,在運行時動態織入AOP,使用反射執行織入的邏輯。
主要實現方式依賴java.lang.reflect包下的InvocationHandler和Proxy類。

優點:Java標準庫原生支持,使用簡單,無需引用額外的包。相對於靜態AOP更靈活。
缺點:帶代理的類必須是接口,靈活性受到一些限制;使用反射會影響一些性能。

  • 動態代碼字節生成(CGLib):

在運行期,目標類加載後,動態構建字節碼文件生成目標類的子類,將切面邏輯加入到子類中。
CGLib是動態代碼字節生成的實現,它封裝字節碼生成工具Asm,原理是在運行期間目標字節碼加載後,生成目標類的子類,將切面邏輯加入到子類中,所以使用Cglib實現AOP 不需要基於接口
CGLIB動態代理主要用到攔截方法的技術,主要涉及的類:Enhancer和MethodInceptor接口

優點:沒有接口也可以織入,靈活性高。
缺點:擴展類的實例方法爲final時,則無法進行織入


Spring AOP 中的代理使用邏輯:

  • 如果目標對象實現了接口,默認情況下會採用 JDK 的動態代理實現 AOP;
  • 如果目標對象沒有實現接口,則採用 CGLIB 庫,Spring 會自動在 JDK 動態代理和 CGLIB 動態代理之間轉換。

4.動態代理實現方式?

就是通過讓target類和代理類實現同一接口,代理類通過反射的方式持有target對象,然後調用target對象的方法,並在方法前後實現織入的邏輯,來達到方法攔截的作用。

參考:Java設計模式——代理模式

爲什麼動態代理僅支持接口,是因爲Java不支持多繼承。
具體可以查看Proxy類的實現方式

那些動態生成的代理類的繼承關係圖,它們已經註定有一個共同的父類叫Proxy。Java的繼承機制註定了這些動態代理類們無法實現對class的動態代理,原因是多繼承在Java中本質上就行不通。

5.PageHelper實現方式?

參考結合源碼理解PageHelper

6.什麼是IoC?什麼是DI?

IoC,Inversion of Control(控制反轉)。

控制反轉(IOC)是一種設計原則,把傳統上由程序代碼直接操控的對象的調用權交給容器,通過容器來實現對象組件的裝配和管理。這個控制權從從程序代碼本身轉移到了外部容器。

這樣對象與對象之間是松耦合、便於測試、功能可複用(減少對象的創建和內存消耗),使得程序的整個體系結構可維護性、靈活性、擴展性變高。
優點:

  1. 依賴注入把應用的代碼量降低。
  2. 它使應用容易測試,單元測試不再需要單例和JNDI查找機制。
  3. 最小的代價和最小的侵入性使鬆散耦合得以實現。
  4. IOC容器支持加載服務時的餓漢式初始化和懶加載。
  • spring 提供了三種主要的方式來配置 IoC 容器中的 bean
    基於 XML 文件配置
    基於註解配置
    基於註解 + java 代碼顯式配置

DI依賴注入,是 IoC 實現的一種方式(還有一種是依賴查找)。通過依賴注入機制,簡單的配置即可注入需要的資源,完成自身的業務邏輯,不需要關心資源的出處和具體實現。

DI注入的方式:
spring支持setter注入和構造器注入,通常是由構造器注入來注入必須的依賴關係,對於可選的依賴關係,則setter注入是更好的選擇。setter注入需要類提供無參構造器或者無參的靜態工廠來創建對象。

7.IoC實現原理?

參考Java架構直通車——@SpringApplication自動裝配原理這節。

IoC的實現原理就是工廠模式加反射機制

8.spring有哪些主要模塊?

Spring框架的七大模塊

1.Spring Core
框架的最基礎部分,提供 IoC 容器,對 bean 進行管理。

2.Spring Context
基於 bean,提供上下文信息,擴展出JNDI、EJB、電子郵件、國際化、校驗和調度等功能。

3.Spring DAO
提供了JDBC的抽象層,它可消除冗長的JDBC編碼和解析數據庫廠商特有的錯誤代碼,還提供了聲明性事務管理方法。

4.Spring ORM
提供了常用的“對象/關係”映射APIs的集成層。 其中包括JPA、JDO、Hibernate、MyBatis 等。

5.Spring AOP
提供了符合AOP Alliance規範的面向方面的編程實現。

6.Spring Web
提供了基礎的 Web 開發的上下文信息,可與其他 web 進行集成。

7.Spring Web MVC
提供了 Web 應用的 Model-View-Controller 全功能實現。

9.spring中的bean是線程安全的嗎?

Spring 不保證 bean 的線程安全。
默認 spring 容器中的 bean 是單例的。當單例中存在競態條件,即有線程安全問題。

10.spring支持幾種bean的作用域?

  • singleton:單例模式,在整個Spring IoC容器中,使用 singleton 定義的 bean 只有一個實例
  • prototype:原型模式,每次通過容器的getbean方法獲取 prototype 定義的 bean 時(將其注入到另一個bean中,或者以程序的方式調用容器的getBean()方法),都產生一個新的 bean 實例

只有在 Web 應用中使用Spring時,request、session、global-session 作用域纔有效

  • request:對於每次 HTTP 請求,使用 request 定義的 bean 都將產生一個新實例,即每次 HTTP 請求將會產生不同的 bean 實例。
  • session:同一個 Session 共享一個 bean 實例。
  • global-session:同 session 作用域不同的是,所有的Session共享一個Bean實例。

什麼時候使用prototype,什麼時候使用singleton,主要看是看bean是有狀態bean還是無狀態bean:

有狀態bean,如果配置爲singleton,會出現線程安全問題,比如下面的代碼:

package com.test;    
public class TestServiceImpl implements TestService{  
   private User user;
   public void test1(User u) throws Exception {  
       this.user = u;                          //1  
       test2();  
   }  
   
   public void test2() throws Exception {  
       System.out.println(user.getId());       //2 
   }     
}

如果該Bean配置爲singleton,在併發訪問下如果某一個線程修改了user的值,那麼另外的線程可能會出錯的。
實際應該是這個例子不應該用實例變量,這樣就使得這個Bean由無狀態變成了有狀態Bean。
如果用有狀態的bean,就要用prototype模式,每次在注入的時候就重新創建一個bean,在多線程中互不影響。

11.spring mvc運行流程?

參考:DispatchServlet處理流程
參考:Rest處理流程

12.@RequestMapping的作用是什麼?

@RequestMapping 是一個註解,用來標識 http 請求地址與 Controller 類的方法之間的映射。

13.@Autowired的作用是什麼?

@Autowired 是一個註解,它可以對類成員變量、方法及構造函數進行標註,讓 spring 完成 bean 自動裝配的工作。
@Autowired 默認是按照類去匹配,配合 @Qualifier 指定按照名稱去裝配 bean。

14.什麼是spring boot?爲什麼要用?

spring boot 基於 spring 框架的快速開發整合包。
好處:

編碼變得簡單
配置變得簡單
部署變得簡單
監控變得簡單

15.spring boot核心配置文件是什麼?

Spring Boot 有兩種類型的配置文件,application 和 bootstrap 文件
Spring Boot會自動加載classpath目前下的這兩個文件,文件格式爲 properties 或 yml 格式

*.properties 文件是 key=value 的形式
*.yml 是 key: value 的形式
*.yml 加載的屬性是有順序的,但不支持 @PropertySource 註解來導入配置,一般推薦用yml文件,看下來更加形象

bootstrap 配置文件是系統級別的,用來加載外部配置,如配置中心的配置信息,也可以用來定義系統不會變化的屬性.bootstatp 文件的加載先於application文件
application 配置文件是應用級別的,是當前應用的配置文件

16.spring boot有哪些方式可以實現熱部署?

以後再詳解。

17.什麼是Spring的內部bean?

當一個bean僅被用作另一個bean的屬性時,它能被聲明爲一個內部bean,內部bean通常是匿名的,它們的Scope一般是prototype。

如:

	<!-- 利用setter注入Dog -->  
    <bean id="d1" class="com.yang.Dog">  
        <property name="name" value="lala"></property>  
        <property name="age" value="9"></property>  
        <property name="style" value="Hashiqi"></property>  
    </bean>  
    <!--利用構造器注入person-->  
    <bean id="d2" class="com.yang.person">  
        <constructor-arg index="0" value="andreny"></constructor-arg>  
        <constructor-arg index="1" value="15454"></constructor-arg>  
        <constructor-arg index="2" value="teacher"></constructor-arg>  
    </bean>  
    
    <bean id="m1" class="com.yang.myshow">  
        <!--內部bean-->
        <property name="dog" ref="d1"></property>  
        <!--內部bean-->
        <property name="p" ref="d2"></property>  
    </bean>  

18.描述Spring框架中bean的生命週期?

1、Spring容器 從XML 文件中讀取bean的定義,並實例化bean。
2、Spring根據bean的定義填充所有的屬性。
3、如果bean實現了BeanNameAware 接口,Spring 傳遞bean 的ID 到 setBeanName方法。
4、如果Bean 實現了 BeanFactoryAware 接口, Spring傳遞beanfactory 給setBeanFactory 方法。
5、如果有任何與bean相關聯的BeanPostProcessors,Spring會在postProcesserBeforeInitialization()方法內調用它們。
6、如果bean實現IntializingBean了,調用它的afterPropertySet方法,如果bean聲明瞭初始化方法,調用此初始化方法。
7、如果有BeanPostProcessors 和bean 關聯,這些bean的postProcessAfterInitialization() 方法將被調用。
8、如果bean實現了 DisposableBean,它將調用destroy()方法。

19.spring中如何注入數組/集合?

Spring提供以下幾種集合的配置元素:
<list>類型用於注入一列值,允許有相同的值。
<set>類型用於注入一組值,不允許有相同的值。
<map> 類型用於注入一組鍵值對,鍵和值都可以爲任意類型。
<props>類型用於注入一組鍵值對,鍵和值都只能爲String類型。

<beans>
  <bean id="at" class="test.array.ArrayTest">
    <property name="names">
      <list>
        <value>aaa</value>
        <value>bbb</value>
      </list>
    </property>
  </bean>
</beans>

也可以通過註解的方式注入:

public interface InjectService extends InitializingBean{
    public void inject();
}

@Service
public class InjectServiceImpl implements InjectService {@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("begin class:" + this);
    }@Override
    public void inject() {
        System.out.println("this is inject service");
    }
}

@Controller
public class InjectController {@Autowired
    private List<InjectService> list;
    @Autowired
    private Map<String, InjectService> map;
    @Autowired
    private Set<InjectService> set;@RequestMapping("inject.html")
    public void injectUrl() {
        for (InjectService service : list) {
            System.out.println("inject list service class:" + service);
        }
        for (String key : map.keySet()) {
            System.out.println("inject map service class:" + map.get(key));
        }
        for (InjectService service : set) {
            System.out.println("inject map service class:" + service);
        }
    }
}

對於list、set填入的是注入類型Spring管理的實例,對於map,Spring會將service的名字作爲key,對象作爲value封裝進入Map。

20.Spring中有哪幾種方法獲取HttpSession對象?

	@RequestMapping("/session")
    public Map test(HttpSession session, String otherParam) {
        ...
    }

或者

    @RequestMapping("/session")
    public String setSession(HttpServletRequest request, HttpServletResponse response){
        HttpSession session = request.getSession();
        ...
   }

21.ApplicationContext有哪幾種實現方式?

有以下3種實現方式:

1、FileSystemXmlApplicationContext :從文件系統加載IoC配置文件
2、ClassPathXmlApplicationContext:從文件系統加載IoC配置文件
3、WebXmlApplicationContext:通過WebApplicationContextUtils獲取

        //加載單個xml文件
        ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
        //加載多個xml文件
        String[] locations = new String[]{"bean1.xml", "bean2.xml", "bean3.xml"};
        ctx = new FileSystemXmlApplicationContext(locations);
 
        //加載單個xml文件
        ctx = new ClassPathXmlApplicationContext("bean.xml");
        //加載多個xml文件
        locations = new String[]{"bean1.xml", "bean2.xml", "bean3.xml"};
        ctx = new ClassPathXmlApplicationContext(locations);
 
        
        ServletContext servletContext = request.getSession().getServletContext();
        ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);

22.Spring裏面用了哪些設計模式?

工廠模式、單例模式、代理模式、適配器模式、觀察者模式…
參考:https://www.cnblogs.com/hwaggLee/p/4510687.html

23.列舉幾種spring的自動裝配方式?

有五種自動裝配的方式,可以用來指導Spring容器用自動裝配方式來進行依賴注入。
(1)no:默認的方式是不進行自動裝配,通過顯式設置ref 屬性來進行裝配。
(2)byName:通過參數名 自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,之後容器試圖匹配、裝配和該bean的屬性具有相同名字的bean。
(3)byType:通過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,之後容器試圖匹配、裝配和該bean的屬性具有相同類型的bean。如果有多個bean符合條件,則拋出錯誤。
(4)constructor:這個方式類似於byType, 但是要提供給構造器參數,如果沒有確定的帶參數的構造器參數類型,將會拋出異常。
(5)autodetect:首先嚐試使用constructor來自動裝配,如果無法工作,則使用byType方式。

24.Spring的初始化過程?

參考:Java架構直通車——SpringApplication的初始化過程

25.如何理解理解Spring容器、BeanFactory和ApplicationContext?

spring容器可以理解爲對於對象的管理和裝配的地方,它負責了對象的整個生命週期–創建、裝配、銷燬。而這裏對象的創建管理的控制權都交給了Spring容器,所以這是一種控制權的反轉,稱爲IOC容器。

BeanFactory和ApplicationContext是Spring的兩大核心接口,而其中ApplicationContext是BeanFactory的子接口。它們都可以當做Spring的容器,Spring容器是生成Bean實例的工廠,並管理容器中的Bean。

BeanFactory和ApplicationContext之間的關係:

  1. BeanFactory。
    這是最簡單的容器,只能提供基本的DI功能和對象生命週期的管理,所以不太常用。
  2. ApplicationContext
    繼承自BeanFactory,它能提供更多企業級的服務,比如AOP,事件監聽機制等。

另外,BeanFactory使用的是懶加載。而ApplicationContext默認是初始化的時候加載所有的Singleton Bean,在系統創建前期會有較大的系統開銷,之後性能會有較好的改善。

26.Spring中的事務傳播級別

1)@Transactional(propagation=Propagation.REQUIRED):默認的spring事務傳播級別,使用該級別的特點是,如果上下文中已經存在事務,那麼就加入到事務中執行,如果當前上下文中不存在事務,則新建事務執行,所以這個級別通常能滿足處理大多數的業務場景。

2)@Transactional(propagation=PROPAGATION.SUPPORTS):從字面意思就知道,supports(支持),該傳播級別的特點是,如果上下文存在事務,則支持當前事務,加入到事務執行,如果沒有事務,則使用非事務的方式執行。所以說,並非所有的包在transactionTemplate.execute中的代碼都會有事務支持。這個通常是用來處理那些並非原子性的非核心業務邏輯操作,應用場景較少。

3)@Transactional(propagation=PROPAGATION.MANDATORY):該級別的事務要求上下文中必須要存在事務,否則就會拋出異常!配置該方式的傳播級別是有效的控制上下文調用代碼遺漏添加事務控制的保證手段。比如一段代碼不能單獨被調用執行,但是一旦被調用,就必須有事務包含的情況,就可以使用這個傳播級別。

4)@Transactional(propagation=PROPAGATION.REQUIRES_NEW):從字面即可知道,每次都要一個新的事務,該傳播級別的特點是,每次都會新建一個事務,並且同時將上下文中的事務掛起,當新建事務執行完成以後,上下文事務再恢復執行。

這是一個很有用的傳播級別,舉一個應用場景:現在有一個發送100個紅包的操作,在發送之前,要做一些系統的初始化、驗證、數據記錄操作,然後發送100封紅包,然後再記錄發送日誌,發送日誌要求100%的準確,如果日誌不準確,那麼整個父事務邏輯需要回滾。
怎麼處理整個業務需求呢?就是通過這個PROPAGATION.REQUIRES_NEW 級別的事務傳播控制就可以完成。發送紅包的子事務不會直接影響到父事務的提交和回滾。

5)@Transactional(propagation=PROPAGATION.NOT_SUPPORTED) :這個也可以從字面得知,not supported(不支持),當前級別的特點是,如果上下文中存在事務,
則掛起事務,執行當前邏輯,結束後恢復上下文的事務。

這個級別有什麼好處?可以幫助你將事務極可能的縮小。我們知道一個事務越大,它存在的風險也就越多。所以在處理事務的過程中,要保證儘可能的縮小範圍。比如一段代碼,是每次邏輯操作都必須調用的,比如循環1000次的某個非核心業務邏輯操作。這樣的代碼如果包在事務中,勢必造成事務太大,導致出現一些難以考慮周全的異常情況。所以這個事務這個級別的傳播級別就派上用場了,用當前級別的事務模板抱起來就可以了。

6)@Transactional(propagation=PROPAGATION.NEVER):該事務更嚴格,上面一個事務傳播級別只是不支持而已,有事務就掛起,而PROPAGATION_NEVER傳播級別要求上下文中不能存在事務,一旦有事務,就拋出runtime異常,強制停止執行!

7)@Transactional(propagation=PROPAGATION.NESTED):字面也可知道,nested,嵌套級別事務。該傳播級別特徵是,如果上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務。

PROPAGATION_NESTED 開始一個 “嵌套的” 事務, 它是已經存在事務的一個真正的子事務. 嵌套事務開始執行時, 它將取得一個 savepoint。如果這個嵌套事務失敗, 我們將回滾到此 savepoint。嵌套事務是外部事務的一部分, 只有外部事務結束後它纔會被提交.

27.Spring的@Transactional如何實現的?

實現@Transactional原理是基於spring aop的。

  1. 自動提交
    默認情況下,數據庫處於自動提交模式。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,如果執行成功則隱式的提交事務,如果執行失敗則隱式的回滾事務。
    事務管理,是一組相關的操作處於一個事務之中,因此必須關閉數據庫的自動提交模式。這點,Spring會在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中將底層連接的自動提交特性設置爲false
// switch to manual commit if necessary。 this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already)。if (con。getautocommit()) 
{
    txobject.setmustrestoreautocommit(true);
    if (logger.isdebugenabled()) 
    {
        logger.debug("switching jdbc connection [" + con + "] to manual commit");
    }
    //首先將自動提交屬性改爲false
    con.setautocommit(false);
}
  1. spring事務回滾規則
    Spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常。Spring事務管理器會捕捉任何未處理的異常,然後依據規則決定是否回滾拋出異常的事務。
    默認配置下,Spring只有在拋出的異常爲運行時unchecked異常時纔回滾該事務,也就是拋出的異常爲RuntimeException的子類(Errors也會導致事務回滾)。而拋出checked異常則不會導致事務回滾。
    Spring也支持明確的配置在拋出哪些異常時回滾事務,包括checked異常。也可以明確定義哪些異常拋出時不回滾事務。
    還可以編程性的通過setRollbackOnly()方法來指示一個事務必須回滾,在調用完setRollbackOnly()後你所能執行的唯一操作就是回滾。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章