題目
聊聊spring的IOC和AOP?其底層原理分別是什麼?
IOC
沒有使用spring,所有的物件均需要手動建立,相當於底層類控制了上層類,例如輪子類控制了汽車類,建立汽車物件的時候需要傳入輪子的引數。
IOC意思就是控制反轉,即上層類控制下層類,上層類的物件只需要指定需要什麼型別的下層類物件,由外部容器提前建立好並注入,上層類物件本身不負責下層類物件的建立。提高了可維護性,下層類建構函式的變化不會影響上層類的建構函式。
spring基於反射原理掃描註解,將相關類生成物件注入到容器中,在需要的地方進行物件注入。
AOP
基於動態代理的方式增強原物件的方法。Spring AOP全稱為Spring Aspect-Oriented Programming,即面向切面程式設計,是執行時織入的,那麼執行時織入到底是怎麼實現的呢?答案就是代理物件。代理物件又可以分為靜態代理和動態代理。
靜態代理:由程式設計師建立或特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。
動態代理:在程式執行時,運用反射機制動態建立而成。
Spring兩種動態代理方式
- JDK的動態代理
若目標物件實現了介面,spring預設使用JDK的動態代理。
優點:因為有介面,所以使系統更加松耦合;
缺點:為每一個目標類建立介面;
- CGLib
若目標物件沒有實現任何介面,spring使用CGLib進行動態代理
優點:因為代理類與目標類是繼承關係,所以不需要有介面的存在。
缺點:因為沒有使用介面,所以系統的耦合性沒有使用JDK的動態代理好。
若目標物件實現了介面,但是強制cglib代理,則使用cglib代理
參考:
Spring IoC和AOP的實現原理解析
bean的生命週期,迴圈引用問題spring如何處理
bean的生命週期
Spring 容器只能管理 單例(singleton) 作用域的 Bean 的完整生命週期,對於 原型(prototype) 作用域的 Bean,Spring 容器只建立 bean 的例項後便會返回給使用者,剩餘的生命週期由使用者控制。所以 Bean 的生命週期主要指的是 singleton 作用域 的 Bean。
具體生命週期參考:https://juejin.cn/post/7217693476494147641
迴圈引用問題spring如何處理
Spring 並不能解決所有 Bean 的迴圈依賴問題,Setter方法注入方式 在 Spring 中是不會產生迴圈依賴問題的,這主要是靠 三級快取 機制。構造器注入無法解決迴圈依賴,因為 構造器注入 發生在 例項化階段,而 Spring 解決迴圈依賴問題依靠的 三級快取 在 屬性注入階段,也就是說呼叫建構函式時還未能放入三級快取中,所以無法解決 構造器注入 的迴圈依賴問題。Spring 無法解決 原型作用域 出現的迴圈依賴問題,因為 Spring 不會快取 原型 作用域的 Bean,而 Spring 依靠 快取 來解決迴圈依賴問題,所以 Spring 無法解決 原型 作用域的 Bean。
首先會獲取 AService 對應的 Bean 物件。
先是呼叫 doGetBean() 中的第一個 getSingleton(beanName) 判斷是否有該 Bean 的例項,有就直接返回了。(顯然這裡沒有)
然後呼叫 doGetBean() 中的第二個 getSingleton() 方法來執行 doCreateBean() 方法。
先進行例項化操作(也就是利用建構函式例項化),此時例項化後生成的是原始物件。
將原始物件透過 lambda表示式 進行封裝成 ObjectFactory 物件,透過 addSingletonFactory 加入三級快取中。
然後再進行屬性注入,此時發現需要注入 BService 的 Bean,會透過 doGetBean() 去獲取 BService 對應的 Bean。
同樣呼叫 doGetBean() 中的第一個 getSingleton(beanName) 判斷是否有該 Bean 的例項,顯然這裡也是不會有 BService 的 Bean 的。
然後只能呼叫 doGetBean() 中的第二個 getSingleton() 方法來執行 doCreateBean() 方法來建立一個 BService 的 Bean。
同樣地先進行例項化操作,生成原始物件後封裝成 ObjectFactory 物件放入三級快取中。
然後進行屬性注入,此時發現需要注入 AService 的 Bean,此時呼叫呼叫 doGetBean() 中的第一個 getSingleton(beanName) 查詢是否有 AService 的 Bean。此時會觸發三級快取,也就是呼叫 singletonFactories.get(beanName)。
因為三級快取中有 AService 的原始物件封裝的 ObjectFactory 物件,所以可以獲取到的代理物件或原始物件,並且上移到二級快取中,提前暴露給 BService 呼叫。
所以 BService 可以完成屬性注入,然後進行初始化後,將 Bean 放入一級快取,這樣 AService 也可以完成建立。
參考:
淺談 Spring Bean 的生命週期
淺談 Spring 如何解決 Bean 的迴圈依賴問題
第二次講Spring迴圈依賴,時長16分鐘,我保證每一秒都是精華
Bean的作用域有幾種?
Spring框架支援以下五種bean的作用域:
1.singleton :bean在每個Spring ioc 容器中只有一個例項。
2.prototype:一個bean的定義可以有多個例項。
3.request:每次http請求都會建立一個bean,該作用域僅在基於web的Spring ApplicationContext情形下有效。
4.session:在一個HTTP Session中,一個bean定義對應一個例項。該作用域僅在基於web的Spring ApplicationContext情形下有效。
5.global-session:在一個全域性的HTTP Session中,一個bean定義對應一個例項。該作用域僅在基於web的Spring ApplicationContext情形下有效。
參考:
解釋Spring支援的幾種bean的作用域?
BeanFactory和ApplicationContext有什麼區別?BeanFactory和FactoryBean的區別?
BeanFactory和ApplicationContext有什麼區別
BeanFactory 介面提供了一種高階配置機制,能夠管理任何型別的物件,它是SpringIoC容器標準化超介面!
ApplicationContext 是 BeanFactory 的子介面。它擴充套件了以下功能:
更容易與 Spring 的 AOP 功能整合
訊息資源處理(用於國際化)
特定於應用程式給予此介面實現,例如Web 應用程式的 WebApplicationContext
簡而言之, BeanFactory 提供了配置框架和基本功能,而 ApplicationContext 新增了更多特定於企業的功能。 ApplicationContext 是 BeanFactory 的完整超集!
FactoryBean和BeanFactory區別
**FactoryBean **是 Spring 中一種特殊的 bean,可以在 getObject() 工廠方法自定義的邏輯建立Bean!是一種能夠生產其他 Bean 的 Bean。FactoryBean 在容器啟動時被建立,而在實際使用時則是透過呼叫 getObject() 方法來得到其所生產的 Bean。因此,FactoryBean 可以自定義任何所需的初始化邏輯,生產出一些定製化的 bean。
一般情況下,整合第三方框架,都是透過定義FactoryBean實現!!!
BeanFactory 是 Spring 框架的基礎,其作為一個頂級介面定義了容器的基本行為,例如管理 bean 的生命週期、配置檔案的載入和解析、bean 的裝配和依賴注入等。BeanFactory 介面提供了訪問 bean 的方式,例如 getBean() 方法獲取指定的 bean 例項。它可以從不同的來源(例如 Mysql 資料庫、XML 檔案、Java 配置類等)獲取 bean 定義,並將其轉換為 bean 例項。同時,BeanFactory 還包含很多子類(例如,ApplicationContext 介面)提供了額外的強大功能。
總的來說,FactoryBean 和 BeanFactory 的區別主要在於前者是用於建立 bean 的介面,它提供了更加靈活的初始化定製功能,而後者是用於管理 bean 的框架基礎介面,提供了基本的容器功能和 bean 生命週期管理。
參考:
趙偉風老師SSM框架實戰精講
Spring AOP和AspectJ AOP有什麼區別?
Spring AOP是基於Spring IoC實現的,它解決大部分常見的需求,但它並不是一個完整的AOP解決方案。對於非Spring容器管理的物件,它更沒有辦法了。而AspectJ旨在提供完整的AOP方案,因此也會更復雜。
而Spring AOP是執行時織入的,主要使用了兩種技術:JDK動態代理和CGLIB代理。對於介面使用JDK Proxy,而繼承的使用CGLIB。AspectJ是在執行前織入的,分為三類:編譯時織入、編譯後織入、載入時織入。
Aspectj不受類的特殊限制,不管方法是private、或者static、或者final的,都可以代理,spring AOP只能代理例項方法。
AspectJ效能更好,功能更強,使用更復雜。
參考:
Spring AOP與AspectJ的對比及應用
AspectJ 靜態代理實踐
Java JDK代理、CGLIB、AspectJ代理分析比較
Spring的事務有了解?
程式設計式事務是指手動編寫程式來管理事務,即透過編寫程式碼的方式直接控制事務的提交和回滾。在 Java 中,通常使用事務管理器(如 Spring 中的 PlatformTransactionManager)來實現程式設計式事務。
程式設計式事務的主要優點是靈活性高,可以按照自己的需求來控制事務的粒度、模式等等。但是,編寫大量的事務控制程式碼容易出現問題,對程式碼的可讀性和可維護性有一定影響。
宣告式事務是指使用註解或 XML 配置的方式來控制事務的提交和回滾。
開發者只需要新增配置即可, 具體事務的實現由第三方框架實現,避免我們直接進行事務操作!
使用宣告式事務可以將事務的控制和業務邏輯分離開來,提高程式碼的可讀性和可維護性。
區別:
程式設計式事務需要手動編寫程式碼來管理事務
而宣告式事務可以透過配置檔案或註解來控制事務。
參考:
趙偉風老師SSM框架實戰精講
Spring的事件型別有幾種?
Spring 提供了以下5中標準的事件:
上下文更新事件(ContextRefreshedEvent):該事件會在ApplicationContext被初始化或者更新時釋出。也可以在呼叫ConfigurableApplicationContext 介面中的refresh()方法時被觸發。
上下文開始事件(ContextStartedEvent):當容器呼叫ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發該事件。
上下文停止事件(ContextStoppedEvent):當容器呼叫ConfigurableApplicationContext的Stop()方法停止容器時觸發該事件。
上下文關閉事件(ContextClosedEvent):當ApplicationContext被關閉時觸發該事件。容器被關閉時,其管理的所有單例Bean都被銷燬。
請求處理事件(RequestHandledEvent):在Web應用中,當一個http請求(request)結束觸發該事件。
除了上面介紹的事件以外,還可以透過擴充套件ApplicationEvent 類來開發自定義的事件。
參考:
經典面試題-Spring框架中有哪些不同型別的事件?
Springmvc的流程?
參考:
趙偉風老師SSM框架實戰精講
SpringMvc的控制器是不是單例模式,如果是,有什麼問題,怎麼解決?
單例,例項變數的執行緒安全問題,可以用@Scope("prototype")改為多例的模式,或者例項變數用threadlocal變數。
參考:
SpringMVC控制器是不是單例模式
瞭解Springboot嗎?那講一下Springboot的啟動流程吧
初始化資訊
釋出啟動等事件,方便外部擴充,讀取環境配置資訊(把幾個配置檔案都掃一遍)
例項化一個上下文
準備上下文
ioc容器載入
解析啟動類,載入自動配置類
建立內嵌伺服器
參考:
小米二面:你知道SpringBoot專案是如何啟動的嗎?
講一講Spring和Springboot的區別
springboot 自動導依賴,自動版本仲裁,自動掃包(主程式所在包及其子包),自動配置,自動匯入元件
自動配置流程細節梳理:
1、匯入starter-web:匯入了web開發場景
● 1、場景啟動器匯入了相關場景的所有依賴:starter-json、starter-tomcat、springmvc
● 2、每個場景啟動器都引入了一個spring-boot-starter,核心場景啟動器。
● 3、核心場景啟動器引入了spring-boot-autoconfigure包。
● 4、spring-boot-autoconfigure裡面囊括了所有場景的所有配置。
● 5、只要這個包下的所有類都能生效,那麼相當於SpringBoot官方寫好的整合功能就生效了。
● 6、SpringBoot預設卻掃描不到 spring-boot-autoconfigure下寫好的所有配置類。(這些配置類給我們做了整合操作),預設只掃描主程式所在的包。
2、主程式:@SpringBootApplication
● 1、@SpringBootApplication由三個註解組成@SpringBootConfiguration、@EnableAutoConfiguratio、@ComponentScan
● 2、SpringBoot預設只能掃描自己主程式所在的包及其下面的子包,掃描不到 spring-boot-autoconfigure包中官方寫好的配置類
● 3、@EnableAutoConfiguration:SpringBoot 開啟自動配置的核心。
○ 1. 是由@Import(AutoConfigurationImportSelector.class)提供功能:批次給容器中匯入元件。
○ 2. SpringBoot啟動會預設載入 142個配置類。
○ 3. 這142個配置類來自於spring-boot-autoconfigure下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports檔案指定的
○ 專案啟動的時候利用 @Import 批次匯入元件機制把 autoconfigure 包下的142 xxxxAutoConfiguration類匯入進來(自動配置類)
○ 雖然匯入了142個自動配置類
● 4、按需生效:
○ 並不是這142個自動配置類都能生效
○ 每一個自動配置類,都有條件註解@ConditionalOnxxx,只有條件成立,才能生效
3、xxxxAutoConfiguration自動配置類
● 1、給容器中使用@Bean 放一堆元件。
● 2、每個自動配置類都可能有這個註解@EnableConfigurationProperties(ServerProperties.class),用來把配置檔案中配的指定字首的屬性值封裝到 xxxProperties屬性類中
● 3、以Tomcat為例:把伺服器的所有配置都是以server開頭的。配置都封裝到了屬性類中。
● 4、給容器中放的所有元件的一些核心引數,都來自於xxxProperties。xxxProperties都是和配置檔案繫結。
● 只需要改配置檔案的值,核心元件的底層引數都能修改
4、寫業務,全程無需關心各種整合(底層這些整合寫好了,而且也生效了)
核心流程總結:
1、匯入starter,就會匯入autoconfigure包。
2、autoconfigure 包裡面 有一個檔案 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,裡面指定的所有啟動要載入的自動配置類
3、@EnableAutoConfiguration 會自動的把上面檔案裡面寫的所有自動配置類都匯入進來。xxxAutoConfiguration 是有條件註解進行按需載入
4、xxxAutoConfiguration給容器中匯入一堆元件,元件都是從 xxxProperties中提取屬性值
5、xxxProperties又是和配置檔案進行了繫結
效果:匯入starter、修改配置檔案,就能修改底層行為。
參考:
尚矽谷SpringBoot3全棧指南
實現一個springbootstarter
具體不展開實踐了,原理如同上述的自動配置流程,具體案例可以參考:手把手教你手寫一個最簡單的 Spring Boot Starter
參考:
手把手教你手寫一個最簡單的 Spring Boot Starter
Springboot的如何去掉不用的配置,springboot不是開箱即用麼?怎麼把不要的配置去掉呢?
@SpringBootApplication(excludeName = {"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在yml配置檔案中新增排除配置
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
參考:
排除不需要的自動配置類