👏作者簡介:大家好,我是愛寫博客的嗯哼,愛好Java的小菜鳥
🔥如果感覺博主的文章還不錯的話,請👍三連支持👍一下博主哦
📝個人博客:敬請期待
📕系列專欄:面試寶典
- 本文引自黑馬程序員Java面試寶典
文章目錄
- 面試官:Spring框架中的單例bean是線程安全的嗎?
- 面試官:什么是AOP
- 面試官:你們項目中有沒有使用到AOP
- 面試官:Spring中的事務是如何實現的
- 面試官:Spring中事務失效的場景有哪些
- 面試官:Spring的bean的生命周期
- 面試官:Spring中的循環引用
- 面試官:那具體解決流程清楚嗎?
- 面試官:構造方法出現了循環依賴怎么解決?
- 面試官:SpringMVC的執行流程知道嘛
- 面試官:Springboot自動配置原理
- 面試官:Spring 的常見注解有哪些?
- 面試官:SpringMVC常見的注解有哪些?
- 面試官:Springboot常見注解有哪些?
- 面試官:MyBatis執行流程
- 面試官:Mybatis是否支持延遲加載?
- 面試官:延遲加載的底層原理知道嗎?
- 面試官:Mybatis的一級、二級緩存用過嗎?
- 面試官:Mybatis的二級緩存什么時候會清理緩存中的數據
面試官:Spring框架中的單例bean是線程安全的嗎?
候選人:
嗯!不是線程安全的,是這樣的
當多用戶同時請求一個服務時,容器會給每一個請求分配一個線程,這是多個線程會并發執行該請求對應的業務邏輯(成員方法),如果該處理邏輯中有對該單列狀態的修改(體現為該單例的成員屬性),則必須考慮線程同步問題。
Spring框架并沒有對單例bean進行任何多線程的封裝處理。關于單例bean的線程安全和并發問題需要開發者自行去搞定。
比如:我們通常在項目中使用的Spring bean都是不可可變的狀態(比如Service類和DAO類),所以在某種程度上說Spring的單例bean是線程安全的。
如果你的bean有多種狀態的話(比如 View Model對象),就需要自行保證線程安全。最淺顯的解決辦法就是將多態bean的作用由“singleton”變更為“prototype”。
面試官:什么是AOP
候選人:
aop是面向切面編程,在spring中用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取公共模塊復用,降低耦合,一般比如可以做為公共日志保存,事務處理等
面試官:你們項目中有沒有使用到AOP
候選人:
我們當時在后臺管理系統中,就是使用aop來記錄了系統的操作日志
主要思路是這樣的,使用aop中的環繞通知+切點表達式,這個表達式就是要找到要記錄日志的方法,然后通過環繞通知的參數獲取請求方法的參數,比如類信息、方法信息、注解、請求方式等,獲取到這些參數以后,保存到數據庫
面試官:Spring中的事務是如何實現的
候選人:
spring實現的事務本質就是aop完成,對方法前后進行攔截,在執行方法之前開啟事務,在執行完目標方法之后根據執行情況提交或者回滾事務。
面試官:Spring中事務失效的場景有哪些
候選人:
嗯!這個在項目中之前遇到過,我想想啊
第一個,如果方法上異常捕獲處理,自己處理了異常,沒有拋出,就會導致事務失效,所以一般處理了異常以后,別忘了跑出去就行了
第二個,如果方法拋出檢查異常,如果報錯也會導致事務失效,最后在spring事務的注解上,就是@Transactional上配置rollbackFor屬性為Exception,這樣別管是什么異常,都會回滾事務
第三,我之前還遇到過一個,如果方法上不是public修飾的,也會導致事務失效
嗯,就能想起來那么多
面試官:Spring的bean的生命周期
候選人:
嗯!,這個步驟還是挺多的,我之前看過一些源碼,它大概流程是這樣的
首先會通過一個非常重要的類,叫做BeanDefinition獲取bean的定義信息,這里面就封裝了bean的所有信息,比如,類的全路徑,是否是延遲加載,是否是單例等等這些信息
在創建bean的時候,第一步是調用構造函數實例化bean
第二步是bean的依賴注入,比如一些set方法注入,像平時開發用的@Autowire都是這一步完成
第三步是處理Aware接口,如果某一個bean實現了Aware接口就會重寫方法執行
第四步是bean的后置處理器BeanPostProcessor,這個是前置處理器
第五步是初始化方法,比如實現了接口InitializingBean或者自定義了方法init-method標簽或@PostContruct
第六步是執行了bean的后置處理器BeanPostProcessor,主要是對bean進行增強,有可能在這里產生代理對象
最后一步是銷毀bean
面試官:Spring中的循環引用
候選人:
嗯,好的,我來解釋一下
循環依賴:循環依賴其實就是循環引用,也就是兩個或兩個以上的bean互相持有對方,最終形成閉環。比如A依賴于B,B依賴于A
循環依賴在spring中是允許存在,spring框架依據三級緩存已經解決了大部分的循環依賴
①一級緩存:單例池,緩存已經經歷了完整的生命周期,已經初始化完成的bean對象
②二級緩存:緩存早期的bean對象(生命周期還沒走完)
③三級緩存:緩存的是ObjectFactory,表示對象工廠,用來創建某個對象的
面試官:那具體解決流程清楚嗎?
候選人:
第一,先實例A對象,同時會創建ObjectFactory對象存入三級緩存singletonFactories
第二,A在初始化的時候需要B對象,這個走B的創建的邏輯
第三,B實例化完成,也會創建ObjectFactory對象存入三級緩存singletonFactories
第四,B需要注入A,通過三級緩存中獲取ObjectFactory來生成一個A的對象同時存入二級緩存,這個是有兩種情況,一個是可能是A的普通對象,另外一個是A的代理對象,都可以讓ObjectFactory來生產對應的對象,這也是三級緩存的關鍵
第五,B通過從通過二級緩存earlySingletonObjects 獲得到A的對象后可以正常注入,B創建成功,存入一級緩存singletonObjects
第六,回到A對象初始化,因為B對象已經創建完成,則可以直接注入B,A創建成功存入一次緩存singletonObjects
第七,二級緩存中的臨時對象A清除
面試官:構造方法出現了循環依賴怎么解決?
候選人:
由于bean的生命周期中構造函數是第一個執行的,spring框架并不能解決構造函數的的依賴注入,可以使用@Lazy懶加載,什么時候需要對象再進行bean對象的創建
面試官:SpringMVC的執行流程知道嘛
候選人:
嗯,這個知道的,它分了好多步驟
1、用戶發送出請求到前端控制器DispatcherServlet,這是一個調度中心
2、DispatcherServlet收到請求調用HandlerMapping(處理器映射器)。
3、HandlerMapping找到具體的處理器(可查找xml配置或注解配置),生成處理器對象及處理器攔截器(如果有),再一起返回給DispatcherServlet。
4、DispatcherServlet調用HandlerAdapter(處理器適配器)。
5、HandlerAdapter經過適配調用具體的處理器(Handler/Controller)。
6、Controller執行完成返回ModelAndView對象。
7、HandlerAdapter將Controller執行結果ModelAndView返回給DispatcherServlet。
8、DispatcherServlet將ModelAndView傳給ViewReslover(視圖解析器)。
9、ViewReslover解析后返回具體View(視圖)。
10、DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。
11、DispatcherServlet響應用戶。
當然現在的開發,基本都是前后端分離的開發的,并沒有視圖這些,一般都是handler中使用Response直接結果返回
面試官:Springboot自動配置原理
候選人:
嗯,好的,它是這樣的。
在Spring Boot項目中的引導類上有一個注解@SpringBootApplication,這個注解是對三個注解進行了封裝,分別是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中
@EnableAutoConfiguration
是實現自動化配置的核心注解。該注解通過
@Import
注解導入對應的配置選擇器。關鍵的是內部就是讀取了該項目和該項目引用的Jar包的的classpath路徑下META-INF/spring.factories文件中的所配置的類的全類名。在這些配置類中所定義的Bean會根據條件注解所指定的條件來決定是否需要將其導入到Spring容器中。
一般條件判斷會有像
@ConditionalOnClass
這樣的注解,判斷是否有對應的class文件,如果有則加載該類,把這個配置類的所有的Bean放入spring容器中使用。
面試官:Spring 的常見注解有哪些?
候選人:
嗯,這個就很多了
第一類是:聲明bean,有@Component、@Service、@Repository、@Controller
第二類是:依賴注入相關的,有@Autowired、@Qualifier、@Resourse
第三類是:設置作用域 @Scope
第四類是:spring配置相關的,比如@Configuration,@ComponentScan 和 @Bean
第五類是:跟aop相關做增強的注解 @Aspect,@Before,@After,@Around,@Pointcut
面試官:SpringMVC常見的注解有哪些?
候選人:
嗯,這個也很多的
有@RequestMapping:用于映射請求路徑;
@RequestBody:注解實現接收http請求的json數據,將json轉換為java對象;
@RequestParam:指定請求參數的名稱;
@PathViriable:從請求路徑下中獲取請求參數(/user/{id}),傳遞給方法的形式參數;@ResponseBody:注解實現將controller方法返回對象轉化為json對象響應給客戶端。@RequestHeader:獲取指定的請求頭數據,還有像@PostMapping、@GetMapping這些。
面試官:Springboot常見注解有哪些?
候選人:
嗯~~
Spring Boot的核心注解是@SpringBootApplication , 他由幾個注解組成 :
- @SpringBootConfiguration: 組合了- @Configuration注解,實現配置文件的功能;
- @EnableAutoConfiguration:打開自動配置的功能,也可以關閉某個自動配置的選項
- @ComponentScan:Spring組件掃描
面試官:MyBatis執行流程
候選人:
好,這個知道的,不過步驟也很多
①讀取MyBatis配置文件:mybatis-config.xml加載運行環境和映射文件
②構造會話工廠SqlSessionFactory,一個項目只需要一個,單例的,一般由spring進行管理
③會話工廠創建SqlSession對象,這里面就含了執行SQL語句的所有方法
④操作數據庫的接口,Executor執行器,同時負責查詢緩存的維護
⑤Executor接口的執行方法中有一個MappedStatement類型的參數,封裝了映射信息
⑥輸入參數映射
⑦輸出結果映射
面試官:Mybatis是否支持延遲加載?
候選人:
是支持的~
延遲加載的意思是:就是在需要用到數據時才進行加載,不需要用到數據時就不加載數據。
Mybatis支持一對一關聯對象和一對多關聯集合對象的延遲加載
在Mybatis配置文件中,可以配置是否啟用延遲加載lazyLoadingEnabled=true|false,默認是關閉的
面試官:延遲加載的底層原理知道嗎?
候選人:
嗯,我想想啊
延遲加載在底層主要使用的CGLIB動態代理完成的
第一是,使用CGLIB創建目標對象的代理對象,這里的目標對象就是開啟了延遲加載的mapper
第二個是當調用目標方法時,進入攔截器invoke方法,發現目標方法是null值,再執行sql查詢
第三個是獲取數據以后,調用set方法設置屬性值,再繼續查詢目標方法,就有值了
面試官:Mybatis的一級、二級緩存用過嗎?
候選人:
嗯~~,用過的~
mybatis的一級緩存: 基于 PerpetualCache 的 HashMap 本地緩存,其存儲作用域為 Session,當Session進行flush或close之后,該Session中的所有Cache就將清空,默認打開一級緩存
關于二級緩存需要單獨開啟
二級緩存是基于namespace和mapper的作用域起作用的,不是依賴于SQL session,默認也是采用 PerpetualCache,HashMap 存儲。
如果想要開啟二級緩存需要在全局配置文件和映射文件中開啟配置才行。
面試官:Mybatis的二級緩存什么時候會清理緩存中的數據
候選人:
嗯!!
當某一個作用域(一級緩存 Session/二級緩存Namespaces)的進行了新增、修改、刪除操作后,默認該作用域下所有 select 中的緩存將被 clear。
往期文章推薦:
- Mysql相關面試題
- 趣聊WebSocket
- 關于redis的讀寫一致問題
- springsecurity加入第三方授權認證
- Java連接mysql常遇時間問題