文章目錄
- 一:常見面試題
- 1:什么是Spring框架?
- 1.1:spring官網中文
- 1.2:spring官網英文
- 2:談談自己對于Spring IOC和AOP的理解
- 2.1:IOC
- Spring Bean 的生命周期主要包括以下步驟:
- 2.2:AOP
- JDK 動態代理:
- CGLIB 動態代理:
- 3:Spring中的bean的作用域有哪些?
- 4:Spring中的單例bean的線程安全問題了解嗎?
- 4.1:解決單例Bean的線程安全問題
- 5:說說自己對于Spring MVC的了解?
- 5.1:流程說明:
- 6:Spring框架中用到了哪些設計模式
- 7:@Component和@Bean的區別是什么
- 7.1:@Bean注解的使用示例:
- 8:將一個類聲明為Spring的bean的注解有哪些?
- 9:Spring事務管理的方式有幾種?
- 9.1:編程式事務:在代碼中硬編碼(不推薦使用)。
- 9.2:聲明式事務:在配置文件中配置(推薦使用),分為基于XML的聲明式事務和基于注解的聲明式事務。
- 9.3:spring事務總結
- 10:高并發下數據影響
- 10.1:臟讀:
- 10.2:丟失修改:
- 10.3:不可重復讀:
- 10.4:幻讀:
- 11:4種事務特性
- 11.1:原子性 (atomicity):
- 11.2:一致性 (consistency):
- 11.3:隔離性 (isolation):
- 11.4:持久性(durability) :
- 12:設置事務隔離級別(5種)
- 12.1:讀未提交(read uncommited) :
- 12.2:讀已提交 (read commited):
- 12.3:可重復讀 (repeatable read) :
- 12.4:串行化的 (serializable) :
- 13:事務的傳播行為
- 13.1: REQUIRED(默認):
- 13.2:SUPPORTS:
- 13.3:MANDATORY:
- 13.4.:REQUIRES_NEW:
- 13.5.:NOT_SUPPORTED:
- 13.6: NEVER:
- 13.7.:NESTED:
- 14:解釋自動裝配的各種模式?
- 14.1:no
- 14.2:byName
- 14.3:byType
- 14.4:constructor
- 14.5:autodetect
- 15:有哪些不同類型的IOC(依賴注入)?
- 15.1:構造器依賴注入
- 15.2:Setter方法依賴注入
- 16:循環依賴
- 16.1:Spring采用了三級緩存
- 16.2:什么是早期暴露的對象
- 16.3:三個步驟:
- 17:@Autowired 與@Resource的區別
- 17.1:來源
- 17.2. 裝配方式
- 17.3. 可選性
- 17.4. 適用范圍
- 18:AOP常用注解
- 18.1:AOP(面向切面編程)在Spring中常用的注解包括
一:常見面試題
1:什么是Spring框架?
1.1:spring官網中文
1.2:spring官網英文
- Spring是一種輕量級框架,旨在提高開發人員的開發效率以及系統的可維護性。
- 我們一般說的Spring框架就是Spring Framework,它是很多模塊的集合,使用這些模塊可以很方便地協助我們進行開發。這些模塊是核心容器、數據訪問/集成、Web、AOP(面向切面編程)、工具、消息和測試模塊。
- 比如Core Container中的Core組件是Spring所有組件的核心,Beans組件和Context組件是實現IOC和DI的基礎,AOP組件用來實現面向切面編程。
- 核心技術:依賴注入(DI),AOP,事件(Events),資源,i18n,驗證,數據綁定,類型轉換,SpEL。
- 測試:模擬對象,TestContext框架,Spring MVC測試,WebTestClient。
- 數據訪問:事務,DAO支持,JDBC,ORM,編組XML。
- Web支持:Spring MVC和Spring WebFlux Web框架。
- 集成:遠程處理,JMS,JCA,JMX,電子郵件,任務,調度,緩存。
- 語言:Kotlin,Groovy,動態語言。
2:談談自己對于Spring IOC和AOP的理解
2.1:IOC
- (Inversion Of Controll,控制反轉 )是一種設計思想,就是將原本在程序中手動創建對象的控制權,交由給Spring框架來管理。IOC在其他語言中也有應用,并非Spring特有。
- IOC容器是Spring用來實現IOC的載體,IOC容器實際上就是一個Map(key, value),Map中存放的是各種對象。
- 將對象之間的相互依賴關系交給IOC容器來管理,并由IOC容器完成對象的注入。
- 在沒有引入IOC容器之前,對象A依賴于對象B,那么對象A在初始化或者運行到某一點的時候,自己必須主動去創建對象B或者使用已經創建的對象B。無論是創建還是使用對象B,控制權都在自己手上。
- 在引入IOC容器之后,這種情形就完全改變了,由于IOC容器的加入,對象A與對象B之間失去了直接聯系,所以,當對象A運行到需要對象B的時候,IOC容器會主動創建一個對象B注入到對象A需要的地方。
- 所以IOC也叫依賴注入(DI),IOC是控制反轉“獲得依賴對象的過程被反轉了”。所謂依賴注入,就是由IOC容器在運行期間,動態地將某種依賴關系注入到對象之中。
- 并且IOC容器還可以管理bean的生命周期
Spring Bean 的生命周期主要包括以下步驟:
- 實例化 Bean:Spring IoC 容器首先根據 Bean 的定義信息(如 XML 配置、注解或 Java 配置)創建 Bean 的實例。
- 設置 Bean 屬性:Spring IoC 容器通過反射機制調用 Bean 的 setter 方法或使用構造函數來設置 Bean 的屬性。
- 調用 BeanNameAware 的 setBeanName 方法:如果 Bean 實現了 BeanNameAware 接口,Spring 會調用它的 setBeanName 方法,傳入 Bean 的 ID。
- 調用 BeanFactoryAware 的 setBeanFactory 方法:如果 Bean 實現了 BeanFactoryAware 接口,Spring 會調用它的 setBeanFactory 方法,傳入當前的 BeanFactory。
- 調用 ApplicationContextAware 的 setApplicationContext 方法:如果 Bean 實現了 ApplicationContextAware 接口,Spring 會調用它的 setApplicationContext 方法,傳入當前的 ApplicationContext。
- 調用 BeanPostProcessor 的 postProcessBeforeInitialization 方法:Spring 會調用所有注冊的 BeanPostProcessor 的 postProcessBeforeInitialization 方法,這是一個擴展點,可以在 Bean 初始化前添加自定義邏輯。
- 調用 InitializingBean 的 afterPropertiesSet 方法:如果 Bean 實現了 InitializingBean 接口,Spring 會調用它的 afterPropertiesSet 方法。
- 調用自定義的 init 方法:如果在 Bean 的定義信息中配置了 init-method,Spring 會調用這個方法。
- 調用 BeanPostProcessor 的 postProcessAfterInitialization 方法:Spring 會調用所有注冊的 BeanPostProcessor 的 postProcessAfterInitialization 方法,這是一個擴展點,可以在 Bean 初始化后添加自定義邏輯。
以上步驟完成后,Bean 就已經準備好了,可以被應用程序使用。
當容器關閉時,Bean 的銷毀過程如下:
- 調用 DisposableBean 的 destroy 方法:如果 Bean 實現了 DisposableBean 接口,Spring 會調用它的 destroy 方法。
- 調用自定義的 destroy 方法:如果在 Bean 的定義信息中配置了 destroy-method,Spring 會調用這個方法。
2.2:AOP
- (Aspect-Oriented Programming,面向切面編程)能夠將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任(例如事務處理、日志管理、權限控制等)封裝起來,便于減少系統的重復代碼,降低模塊間的耦合度,并有利于未來的可擴展性和可維護性。
- Spring AOP是基于動態代理的,如果要代理的對象實現了某個接口,那么Spring AOP就會使用JDK動態代理去創建代理對象;而對于沒有實現接口的對象,就無法使用JDK動態代理,轉而使用CGlib動態代理。
JDK 動態代理:
- JDK 動態代理主要通過實現接口的方式進行代理,因此它只能代理實現了接口的類。
- JDK 動態代理的實現原理是,通過實現 InvocationHandler 接口和使用 Proxy 類的 newProxyInstance 方法生成代理對象。在 InvocationHandler 的 invoke 方法中,我們可以添加自定義的邏輯。
public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在目標方法執行前后添加自定義邏輯System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result;}
}// 使用示例
MyInterface target = new MyInterfaceImpl();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new MyInvocationHandler(target)
);
proxy.myMethod();
CGLIB 動態代理:
- CGLIB 動態代理不需要實現接口,它主要通過繼承的方式進行代理,因此無法代理 final 類。
- CGLIB 動態代理的實現原理是,通過繼承被代理類,并在子類中覆蓋父類的方法實現代理。在覆蓋的方法中,我們可以添加自定義的邏輯。
public class MyMethodInterceptor implements MethodInterceptor {private Object target;public MyMethodInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在目標方法執行前后添加自定義邏輯System.out.println("Before method");Object result = proxy.invokeSuper(obj, args);System.out.println("After method");return result;}
}// 使用示例
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
enhancer.setCallback(new MyMethodInterceptor(new MyClass()));
MyClass proxy = (MyClass) enhancer.create();
proxy.myMethod();
3:Spring中的bean的作用域有哪些?
1.singleton:唯一bean實例,Spring中的bean默認都是單例的。
2.prototype:每次請求都會創建一個新的bean實例。
3.request:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效。
4.session:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP session內有效。
5.global-session:全局session作用域,僅僅在基于Portlet的Web應用中才有意義,Spring5中已經沒有了。Portlet是能夠生成語義代碼(例如HTML)片段的小型Java Web插件。它們基于Portlet容器,可以像Servlet一樣處理HTTP請求。但是與Servlet不同,每個Portlet都有不同的會話。
4:Spring中的單例bean的線程安全問題了解嗎?
- Spring的單例Bean默認是線程安全的,因為Spring容器會確保在多線程環境下正確地管理單例Bean的創建和使用。但是,如果單例Bean中包含可變狀態,可能會引發線程安全問題。
4.1:解決單例Bean的線程安全問題
- 可以采取以下幾種方式:
- 使用不可變對象:盡量將單例Bean設計為不可變對象,避免包含可變狀態。
- 使用局部變量:在方法中使用局部變量而不是實例變量,以避免多個線程共享狀態。
- 同步關鍵部分:如果無法避免可變狀態,可以使用synchronized關鍵字或者ReentrantLock等機制來保護關鍵部分的代碼,確保線程安全。
- 使用線程安全的集合:如果單例Bean需要維護集合等數據結構,可以使用ConcurrentHashMap、CopyOnWriteArrayList等線程安全的集合類。
- 使用ThreadLocal:對于需要線程本地副本的數據,可以使用ThreadLocal來保證線程安全。
通過以上方式,可以確保Spring的單例Bean在多線程環境下能夠安全地被使用。
5:說說自己對于Spring MVC的了解?
- MVC是一種設計模式,Spring MVC是一款很優秀的MVC框架。Spring MVC可以幫助我們進行更簡潔的Web層的開發,并且它天生與Spring框架集成。Spring MVC下我們一般把后端項目分為Service層(處理業務)、Dao層(數據庫操作)、Entity層(實體類)、Controller層(控制層,返回數據給前臺頁面)。
5.1:流程說明:
1.客戶端(瀏覽器)發送請求,直接請求到DispatcherServlet。
2.DispatcherServlet根據請求信息調用HandlerMapping,解析請求對應的Handler。
3.解析到對應的Handler(也就是我們平常說的Controller控制器)。
4.HandlerAdapter會根據Handler來調用真正的處理器來處理請求和執行相對應的業務邏輯。
5.處理器處理完業務后,會返回一個ModelAndView對象,Model是返回的數據對象,View是邏輯上的View。
6.ViewResolver會根據邏輯View去查找實際的View。
7.DispatcherServlet把返回的Model傳給View(視圖渲染)。
8.把View返回給請求者(瀏覽器)。
6:Spring框架中用到了哪些設計模式
1.工廠設計模式:Spring使用工廠模式通過BeanFactory和ApplicationContext創建bean對象。
2.代理設計模式:Spring AOP功能的實現。
3.單例設計模式:Spring中的bean默認都是單例的。
4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template結尾的對數據庫操作的類,它們就使用到了模板模式。
5.包裝器設計模式:我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。這種模式讓我們可以根據客戶的需求能夠動態切換不同的數據源。
6.觀察者模式:Spring事件驅動模型就是觀察者模式很經典的一個應用。
7.適配器模式:Spring AOP的增強或通知(Advice)使用到了適配器模式、Spring MVC中也是用到了適配器模式適配Controller。
7:@Component和@Bean的區別是什么
- 作用對象不同。@Component注解作用于類,而@Bean注解作用于方法。
- @Component注解通常是通過類路徑掃描來自動偵測以及自動裝配到Spring容器中(我們可以使用@ComponentScan注解定義要掃描的路徑)。@Bean注解通常是在標有該注解的方法中定義產生這個bean,告訴Spring這是某個類的實例,當我需要用它的時候還給我。
- @Bean注解比@Component注解的自定義性更強,而且很多地方只能通過@Bean注解來注冊bean。比如當引用第三方庫的類需要裝配到Spring容器的時候,就只能通過@Bean注解來實現。
7.1:@Bean注解的使用示例:
@Configuration
public class AppConfig {@Beanpublic TransferService transferService() {return new TransferServiceImpl();}
}
- 上面的代碼相當于下面的XML配置:
<beans><bean id="transferService" class="com.yanggb.TransferServiceImpl"/>
</beans>
8:將一個類聲明為Spring的bean的注解有哪些?
- @Repository,這個注解和@Component、@Controller和我們最常見的@Service注解是一個作用,都可以將一個類聲明為一個Spring的Bean。它們的區別到不在于具體的語義上,更多的是在于注解的定位上。之前說過,企業級應用注重分層開發的概念,因此,對這四個相似的注解應當有以下的理解:
- @Repository注解,對應的是持久層即Dao層,其作用是直接和數據庫交互,通常來說一個方法對應一條具體的Sql語句
- @Service注解,對應的是服務層即Service層,其作用是對單條/多條Sql語句進行組合處理,當然如果簡單的話就直接調用Dao層的某個方法了
- @Controller注解,對應的是控制層即MVC設計模式中的控制層,其作用是接收用戶請求,根據請求調用不同的Service取數據,并根據需求對數據進行組合、包裝返回給前端
- @Component注解,這個更多對應的是一個組件的概念,如果一個Bean不知道屬于拿個層,可以使用@Component注解標注
9:Spring事務管理的方式有幾種?
9.1:編程式事務:在代碼中硬編碼(不推薦使用)。
9.2:聲明式事務:在配置文件中配置(推薦使用),分為基于XML的聲明式事務和基于注解的聲明式事務。
9.3:spring事務總結
Spring事務和數據庫事務都是為了保證數據的一致性和完整性。它們之間的關系可以從以下幾個方面來理解:
- 封裝:Spring事務是對數據庫事務的封裝。Spring提供了一套抽象的事務管理接口,使得我們可以在不考慮底層數據庫具體實現的情況下,進行事務管理。
- 管理:Spring事務管理器可以管理多個數據庫事務,實現分布式事務管理。這是數據庫事務本身無法做到的。
- 傳播行為:Spring事務提供了豐富的事務傳播行為,如REQUIRED、REQUIRES_NEW、NESTED等,這些都是數據庫事務所不具備的。
- 隔離級別:Spring事務可以設置不同的隔離級別,如READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE等,這些都是基于數據庫事務的隔離級別。
- 異常處理:Spring事務對于運行時異常和檢查型異常都會進行回滾,而數據庫事務只對運行時異常進行回滾。
總的來說,Spring事務是對數據庫事務的一種高級封裝和擴展,使得我們在進行事務管理時,更加方便
10:高并發下數據影響
10.1:臟讀:
當一個事務正在訪問數據并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時另外一個事務也訪問了這個數據。
10.2:丟失修改:
指在一個事務讀取一個數據時,另外一個事務也訪問了該數據,那么在第一個事務中修改了這個數據后,第二個事務也修改了這個數據。這樣第一個事務內的修改結果就被丟失,因此稱為丟失修改。
10.3:不可重復讀:
一個事務內多次讀同一數據。在這個事務還沒有結束時,另一個事務也訪問該數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改導致第一個事務兩次讀取的數據可能不太一樣。
10.4:幻讀:
一個事務(T1)讀取了幾行數據,接著另一個并發事務(T2)插入了一些數據時。在隨后的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄,
11:4種事務特性
11.1:原子性 (atomicity):
強調事務的不可分割
11.2:一致性 (consistency):
事務的執行的前后數據的完整性保持一致.
11.3:隔離性 (isolation):
一個事務執行的過程中,不應該受到其他事務的干擾
11.4:持久性(durability) :
事務一旦結束,數據就持久到數據庫
12:設置事務隔離級別(5種)
- DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
12.1:讀未提交(read uncommited) :
臟讀,不可重復讀,虛讀都有可能發生
12.2:讀已提交 (read commited):
避免臟讀。但是不可重復讀和虛讀有可能發生
12.3:可重復讀 (repeatable read) :
避免臟讀和不可重復讀.但是虛讀有可能發生.
12.4:串行化的 (serializable) :
避免以上所有讀問題.
Mysql 默認:可重復讀
Oracle 默認:讀已提交
13:事務的傳播行為
Spring事務的傳播機制是指在一個方法調用另一個方法時,如何處理事務的傳播。以下是Spring事務傳播機制的一些要點:
13.1: REQUIRED(默認):
如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
13.2:SUPPORTS:
如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式執行。
13.3:MANDATORY:
如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
13.4.:REQUIRES_NEW:
創建一個新的事務,如果當前存在事務,則掛起當前事務。
13.5.:NOT_SUPPORTED:
以非事務方式執行操作,如果當前存在事務,則掛起當前事務。
13.6: NEVER:
以非事務方式執行操作,如果當前存在事務,則拋出異常。
13.7.:NESTED:
如果當前存在事務,則在嵌套事務內執行;如果當前沒有事務,則創建一個新的事務。
- 這些傳播行為可以通過@Transactional注解的propagation屬性來指定。
14:解釋自動裝配的各種模式?
自動裝配提供五種不同的模式供Spring容器用來自動裝配beans之間的依賴注入:
14.1:no
默認模式,不進行自動裝配,需要手動指定依賴注入
14.2:byName
根據屬性名自動裝配,Spring容器會自動裝配和Bean的屬性名相同的Bean。
14.3:byType
根據屬性類型自動裝配,Spring容器會自動裝配和Bean屬性類型相同的Bean。
14.4:constructor
根據構造函數自動裝配,Spring容器會自動裝配構造函數參數類型相同的Bean。
14.5:autodetect
如果有默認的構造方法,通過 construct的方式自動裝配,否則使用 byType的方式自動裝配。
15:有哪些不同類型的IOC(依賴注入)?
15.1:構造器依賴注入
構造器依賴注入在容器觸發構造器的時候完成,該構造器有一系列的參數,每個參數代表注入的對象。
15.2:Setter方法依賴注入
首先容器會觸發一個無參構造函數或無參靜態工廠方法實例化對象,之后容器調用bean中的setter方法完成Setter方法依賴注入。
16:循環依賴
- 循環依賴是什么?
- 這個就是兩個Bean之間循環依賴導致的棧溢出,原因是IOC創建 A的時候,發現需要new B ,于是又去創建B,創建B的時候,發現又需要A…
16.1:Spring采用了三級緩存
- 1,第一級緩存:用于存放完全初始化好的單例Bean實例。
- 2,第二級緩存:用于存放早期暴露的單例Bean實例,即在實例化過程中,尚未完成屬性填充的Bean。
- 3,第三級緩存:用于存放Bean工廠,即用于創建Bean實例的工廠。
16.2:什么是早期暴露的對象
- 所謂的早提提前暴露的對象就是說,你是一個不完整的對象,你的屬性還沒有值,你的對象也沒有被初始化。這就是早期暴露的對象,只是提前拿出來給你認識認識。但他非常重要。這是多級緩存解決循環依賴問題的一個巧妙的地方。
16.3:三個步驟:
- 1:實例化2:填充屬性3:初始化
1:創建A,第一步就在三級緩存中實例化,分配內存空間,產生內存地址引用,但是這個時候還是null。
2:幫A進行填充屬性,發現有需要B,于是去容器中找B的引用。
3:當然找不到B,所以要創建B。
4:創建B跟創建A步驟一樣,先實例化,分配內存,保存引用。
5:發現填充B需要A,于是回去容器找A。
6:一級緩存沒有,二級緩存沒有,三級緩存找到了A的引用,于是返回(此時的A還是null,但是總比沒有好),同時把A移到二級緩存中,此時B的引用已經及找到并且可以給到A,所以這個沒問題。
7:B拿著A的引用,首先成功初始化。然后返回。
8:A獲得初始化成功的B,也成功初始化。
9:至此全流程結束,循環引用問題完美解決~
17:@Autowired 與@Resource的區別
@Autowired 和 @Resource 的區別如下:
17.1:來源
- @Autowired 是 Spring 框架提供的注解。
- @Resource 是 Java EE 提供的注解,也被 Spring 框架支持。
17.2. 裝配方式
- @Autowired 根據類型進行自動裝配。
- @Resource 根據名稱進行自動裝配。
17.3. 可選性
- @Autowired 是非必需的,如果找不到匹配的 Bean,屬性可以為 null。
- @Resource 是必需的,如果找不到匹配的 Bean,會拋出異常。
17.4. 適用范圍
- @Autowired 適用于構造函數、setter 方法、字段和配置方法。
- @Resource 主要用于字段和配置方法。
總的來說,@Autowired 更靈活,而 @Resource 更嚴格。
18:AOP常用注解
使用AOP注解的步驟如下:
-
創建一個切面類,使用@Aspect注解標記。
-
在切面類中定義切點,使用@Pointcut注解標記。
-
定義通知方法,使用@Before、@After、@Around等注解標記。
-
在Spring配置文件中啟用AOP,通常使用aop:aspectj-autoproxy標簽。
-
在需要應用切面的Bean上添加相應的切點注解,如@Before、@After等。
這樣就可以使用AOP注解來實現面向切面編程。
18.1:AOP(面向切面編程)在Spring中常用的注解包括
-
@Aspect:用于定義切面,結合其他注解定義切點和通知。
-
@Pointcut:定義切點,指定在哪些方法上應用通知。
-
@Before:在目標方法執行前執行通知。
-
@After:在目標方法執行后執行通知。
-
@Around:在目標方法執行前后執行通知,可以控制目標方法的執行。
-
@AfterReturning:在目標方法正常返回后執行通知。
-
@AfterThrowing:在目標方法拋出異常后執行通知。