說說你對Spring的IoC機制的理解?
沒有Spring之前:
寫一套系統,web服務器,tomcat,一旦啟動之后,他就可以監聽一個端口號的http請求,然后可以把請求轉交給你的servlet,jsp,配合起來使用的,servlet處理請求。
比如在我們的一個tomcat + servlet的這樣的一個系統里,有幾十個地方,都是直接用MyService myService = new MyServiceImpl(),直接創建、引用和依賴了一個MyServiceImpl這樣的一個類的對象。
這個系統里,有幾十個地方,都跟MyServiceImpl類直接耦合在一起了。
如果我現在不想要用MyServiceImpl了,我們希望用的是NewServiceManagerImpl,implements MyService這個接口的,所有的實現邏輯都不同了,此時我們很麻煩,我們需要在系統里,幾十個地方,都去修改對應的MyServiceImpl這個類,切換為NewServiceManagerImpl這個類。
改動代碼成本很大,改動完以后的測試的成本很大,改動的過程中可能很復雜,出現一些bug,此時就會很痛苦。
歸根結底,代碼里,各種類之間完全耦合在一起,出現任何一丁點的變動,都需要改動大量的代碼,重新測試,可能還會有bug。
有Spring之后:
Spring IoC, Spring容器,根據XML配置或注解,去實例化你的一些bean對象,然后根據XML或注解,去對bean對象之間的引用關系,去進行依賴注入。
底層核心技術是反射。通過反射技術,直接根據你的類去自己構建對應的對象出來。
好處: 系統的類與類之間徹底解耦合。
比如原來是注解myService,幾十個地方都依賴這個類,如果要升級或修改這個實現類為newServiceManager。只需要把myService頭上的注解去掉,把@Service注解放在newServiceManager上面,其他引用的地方不變。
說說你對Spring的AOP機制的理解?
Spring核心框架,兩個機制,IoC和AOP。
Spring在運行時,AOP的核心技術,是動態代理。會給你的類生成動態代理類。
做一個切面,如何定義呢?MyServiceXXXX的這種類,在這些類的所有方法中,都去織入一些代碼,在所有這些方法剛開始運行的時候,都先去開啟一個事務,在所有這些方法運行完畢之后,去根據是否拋出異常來判斷一下,如果拋出異常,就回滾事務,如果沒有異常,就提交事務 => AOP。
AOP總結:面向切面編程,通過動態代理的方式來生成其代理對象在方法執行前后加入自己的邏輯
代理方式:jdk動態代理(接口) cglib動態代理(基於類)
相關名詞:
1. JoinPoint連接點:攔截的接口的方法
2. Pointcut切入點:對哪些連接點進行攔截
3. Advice通知:比如前置通知 后置通知 環繞通知
4. aspect切面:切入點和通知組成
切入點 execution表達式 1. execution 權限修飾符 返回值類型 包名.類名.方法名(參數)
通知類型 :
1. 前置通知:方法執行之前
2. 后置通知:在方法正常執行完畢后(提交事務)
3. 最終通知:在方法正常執行完畢或者出現異常
4. 異常通知:執行過程中出現異常(事物回滾)
5. 環繞通知:方法執行前后,目標方法默認不執行需自己調用方法
了解Cglib動態代理?它跟jdk動態代理的區別?
優先是jdk動態代理,其次是cglib動態代理。
動態代理就是動態的創建一個代理類出來,創建這個代理類的實例對象,在這個里面引用你真正自己寫的類,所有的方法的調用,都是先走代理類的對象,他負責做一些代碼上的增強,再去調用你寫的那個類。
Spring里使用aop,比如說你對一批類和他們的方法做了一個切面,定義好了要在這些類的方法里增強的代碼,Spring必然要對那些類生成動態代理,在動態代理中去執行你定義的一些增強代碼。
如果你的類是實現了某個接口的,spring aop會使用jdk動態代理,生成一個跟你實現同樣接口的一個代理類,構造一個實例對象出來,jdk動態代理,他其實是在你的類有接口的時候,就會來使用。
很多時候我們可能某個類是沒有實現接口的,spring aop會改用cglib來生成動態代理,他是生成你的類的一個子類,他可以動態生成字節碼,覆蓋你的一些方法,在方法里加入增強的代碼。
Spring中的Bean是線程安全的嗎?
線程不安全!
Spring容器中的bean可以分為5個范圍:
(1)singleton:默認,每個容器中只有一個bean的實例
(2)prototype:為每一個bean請求提供一個實例
一般來說下面幾種作用域,在開發的時候一般都不會用,99.99%的時候都是用singleton單例作用域
(3)request:為每一個網絡請求創建一個實例,在請求完成以后,bean會失效並被垃圾回收器回收
(4)session:與request范圍類似,確保每個session中有一個bean的實例,在session過期后,bean會隨之失效
(5)global-session
spring bean默認來說,singleton,都是線程不安全的,java web系統,一般來說很少在spring bean里放一些實例變量,一般來說他們都是多個組件互相調用,最終去訪問數據庫的。
Spring的事務實現原理是什么?能聊聊你對事務傳播機制的理解嗎?
主要考察1. 事務的實現原理,2. 事務的傳播機制
1. 實現原理:加一個@Transactional注解,Spring會使用AOP,對這個方法在執行前,先開啟事務,在執行完畢后,根據方法是否報錯,來決定是回滾還是提交事務。
2. 傳播機制:
1. PROPAGATION_REQUIRED(默認): 如果當前沒有事務,就創建一個新事務,如果當前存在事務,就加入該事務,該設置是最常用的設置。
2. PROPAGATION_SUPPORTS(少用): 支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。‘
3. PROPAGATION_MANDATORY(很少用): 支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就拋出異常。
4. PROPAGATION_REQUIRES_NEW(常用): 創建新事務,無論當前存不存在事務,都創建新事務。
5. PROPAGATION_NOT_SUPPORTED(很少用):以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
6. PROPAGATION_NEVER(很少用): 以非事務方式執行,如果當前存在事務,則拋出異常。
7. PROPAGATION_NESTED(用): 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則按REQUIRED屬性執行。
能畫一張圖說說Spring Boot的核心架構嗎?
背景:簡化繁瑣的Spring配置
spring框架,mybatis,spring mvc,去做一些開發,打包部署到線上的tomcat里去,tomcat啟動了,他就會接收http請求,轉發給spring mvc框架,調用controller -> service -> dao -> mybatis(sql語句)。
基於spring boot直接進行開發,里面還是使用spring + spring mvc + mybatis一些框架,我們可以一定程度上來簡化我們之前的開發流程。
spring boot內嵌一個tomcat去直接讓我們一下子就可以把寫好的java web系統給啟動起來,直接運行一個main方法,spring boot就直接把tomcat服務器給跑起來,把我們的代碼運行起來了。
自動裝配,比如說我們可以引入mybatis,我其實主要引入一個starter依賴,他會一定程度上個自動完成mybatis的一些配置和定義,不需要我們手工去做大量的配置了,一定程度上簡化我們搭建一個工程的成本。
只要引入一個starter,他會自動給你引入需要的一些jar包,做非常簡單的、必須的一些配置,比如數據庫的地址,幾乎就不用你做太多的其他額外的配置了,他會自動幫你去進行一些配置,定義和生成對應的bean生成的bean自動注入比如你的dao里去,讓你免去一些手工配置+定義bean的一些工作。
能畫一張圖說說Spring的核心架構嗎?
Spring生命周期: 創建 -> 使用 -> 銷毀
首先用xml或注解,定義一堆bean.
1. 實例化bean
通過反射創建bean對象實例。
2. 設置對象屬性(依賴注入)
實例化后的對象被封裝在BeanWrapper對象中,Spring根據BeanDefinition中的信息以及通過BeanWrapper提供的設置屬性接口完成依賴注入。
這個bean依賴了誰,把依賴的bean也創建出阿里,給你進行一個注入,比如通過構造函數或setter方法。
3. 處理Aware接口
Spring檢查該對象是否實現了xxxAware接口,並將相關的xxxAware實例注入給Bean。
3.1. 如果這個Bean實現了BeanNameAware接口,則調用它的實現setBeanName(String beanid)方法,傳遞就是Spring配置文件中的Bean的id值;
3.2. 如果這個Bean實現了BeanFactoryAware接口,則調用它實現的setBeanFactory()方法,傳遞的是Spring工廠自身;
3.3. 如果這個Bean實現了ApplicationContextAware接口,則調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文;
4. BeanPostProcessor
如果想在bean實例構建之后,在這個時間點對bean進行自定義處理,則可以讓bean實現BeanPostProcessor接口,
會調用postProcessBeforeInitialiazation(Object obj, String s)方法。
5. InitalizingBean與init-method
如果bean在Spring配置文件中配置了init-method屬性,則會自動調用其配置的初始化方法。
6. BeanPostProcessor
在bean初始化完成后,如果這個bean實現了BeanPostProcessor接口,會調用postProcessAfterInitialization(Object obj, String s)方法。
7. DisposableBean
當bean不再需要時,如果bean實現了DisposableBean接口,會調用其他實現的destroy()方法。
8. destroy-method
如果配置了destroy-method屬性,會調用配置的銷毀方法。
能說說Spring中都使用了哪些設計模式嗎?
工廠模式、單例模式、代理模式
工廠模式:把一個對象的創建過程放入一個具體工廠類中。當需要使用時通過工廠把這個對象實例取出來。
Spring ioc核心的設計模式的思想提現,他自己就是一個大的工廠,把所有的bean實例都給放在了spring容器里(大工廠),如果你要使用bean,就找spring容器就可以了,你自己不用創建對象了。
單例模式:
Spring默認來說,對每個bean走的都是一個單例模式,確保說你的一個類在系統運行期間只有一個實例對象,只有一個bean,用到了一個單例模式的思想,保證了每個bean都是單例的。
雙重校驗鎖:
代理模式:
如果說你要對一些類的方法切入一些增強的代碼,會創建一些動態代理的對象,讓你對那些目標對象的訪問,先經過動態代理對象,動態代理對象先做一些增強的代碼,調用你的目標對象。
能畫一張圖說說Spring Web MVC的核心架構嗎?
Tomcat + Spring架構圖:
1. Tomcat的工作線程將請求轉交給spring mvc框架的DispatcherServlet
2. DispatcherServlet查找@Controller注解的controller,我們一般會給controller加上你@RequestMapping的注解,標注說哪些controller用來處理哪些請求,此時根據請求的uri,去定位到哪個controller來進行處理。
3. 根據@RequestMapping去查找,使用這個controller內的哪個方法來進行請求的處理,對每個方法一般也會加@RequestMapping的注解。
4. 會直接調用我們的controller里面的某個方法來進行請求的處理。
5. 我們的controller的方法會有一個返回值,以前的時候,一般來說還是走jsp、模板技術,我們會把前端頁面放在后端的工程里面,返回一個頁面模板的名字,spring mvc的框架使用模板技術,對html頁面做一個渲染;
現在一般返回一個json串,前后端分離,可能前端發送一個請求過來,我們只要返回json數據。
能畫一張圖說說Spring Cloud的核心架構嗎?
Spring Cloud主要由eureka、ribbon、feign、zuul、hystrix、鏈路追蹤等組件組成。
參考資料:
互聯網Java工程師面試突擊(第三季)-- 中華石杉