前言
很多朋友問,如何短時間突擊 Java 通過面試?
面試前還是很有必要針對性的刷一些題,很多朋友的實戰能力很強,但是理論比較薄弱,面試前不做准備是很吃虧的。這里整理了很多面試常考的一些面試題,希望能幫助到你面試前的復習並且找到一個好的工作,也節省你在網上搜索資料的時間來學習。
整理的這些Java面試題,包括Java基礎、Java多線程與並發編程、spring、spring mvc、spring boot、mybatis。MySQL、Redis、消息中間件MQ、分布式與微服務。持續更新中…
完整版Java面試題地址:105道Java面試題總結|含答案解析
| 內容 | 地址 |
|---|---|
| Java基礎 | https://www.cnblogs.com/zhuifeng523/p/14627735.html |
| 多線程與並發 | https://www.cnblogs.com/zhuifeng523/p/14639641.html |
| Spring | 本文 |
| Spring MVC、Spring Boot | 未更新 |
| MyBatis | 未更新 |
| MySQL | 未更新 |
| Redis | 未更新 |
| 分布式與微服務 | 未更新 |
| MQ | 未更新 |
如何實現一個IOC容器
1、配置文件配置包掃描路徑
2、遞歸包掃描獲取.class文件
3、反射、確定需要交給IOC管理的類
4、對需要注入的類進行依賴注入
(1)配置文件中指定需要掃描的包路徑
(2)定義一些注解,分別表示訪問控制層、業務服務層、數據持久層、依賴注入注解、獲取配置文件注解
(3)從配置文件中獲取需要掃描的包路徑,獲取到當前路徑下的文件信息及文件夾信息,我們將當前路徑下所有以.class結尾的文件添加到一個Set集合中進行存儲
(4)遍歷這個set集合,獲取在類上有指定注解的類,並將其交給IOC容器,定義一個安全的Map用來存儲這些對象
(5)遍歷這個IOC容器,獲取到每一個類的實例,判斷里面是有有依賴其他的類的實例,然后進行遞歸注入
Spring是什么?
輕量級的開源的J2EE框架。它是一個容器框架,用來裝javabean(java對象),中間層框架(萬能膠)可以起一個連接作用,比如說把Struts和hibernate粘合在一起運用,可以讓我們的企業開發更快、更簡潔
Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器框架
(1)從大小與開銷兩方面而言Spring都是輕量級的。
(2)通過控制反轉(IoC)的技術達到松耦合的目的
(3)提供了面向切面編程的豐富支持,允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發
(4)包含並管理應用對象(Bean)的配置和生命周期,這個意義上是一個容器。
(5)將簡單的組件配置、組合成為復雜的應用,這個意義上是一個框架。
談談你對AOP的理解
系統是由許多不同的組件所組成的,每一個組件各負責一塊特定功能。除了實現自身核心功能之外,這些組件還經常承擔着額外的職責。例如日志、事務管理和安全這樣的核心服務經常融入到自身具有核心業務邏輯的組件中去。這些系統服務經常被稱為橫切關注點,因為它們會跨越系統的多個組件。
當我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關系,但並不適合定義從左到右的關系。例如日志功能。
日志代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關系。
在OOP設計中,它導致了大量代碼的重復,而不利於各個模塊的重用。
AOP:將程序中的交叉業務邏輯(比如安全,日志,事務等),封裝成一個切面,然后注入到目標對象(具體業務邏輯)中去。AOP可以對某個對象或某些對象的功能進行增強,比如對象中的方法進行增強,可以在執行某個方法之前額外的做一些事情,在某個方法執行之后額外的做一些事情。
談談你對IOC的理解
容器概念、控制反轉、依賴注入
ioc容器
實際上就是個map(key,value),里面存的是各種對象(在xml里配置的bean節點、@repository、@service、@controller、@component),在項目啟動的時候會讀取配置文件里面的bean節點,根據全限定類名使用反射創建對象放到map里、掃描到打上上述注解的類還是通過反射創建對象放到map里。
這個時候map里就有各種對象了,接下來我們在代碼里需要用到里面的對象時,再通過DI注入(autowired、resource等注解,xml里bean節點內的ref屬性,項目啟動的時候會讀取xml節點ref屬性根據id注入,也會掃描這些注解,根據類型或id注入;id就是對象名)。
控制反轉
沒有引入IOC容器之前,對象A依賴於對象B,那么對象A在初始化或者運行到某一點的時候,自己必須主動去創建對象B或者使用已經創建的對象B。無論是創建還是使用對象B,控制權都在自己手上。
引入IOC容器之后,對象A與對象B之間失去了直接聯系,當對象A運行到需要對象B的時候,IOC容器會主動創建一個對象B注入到對象A需要的地方。
通過前后的對比,不難看出來:對象A獲得依賴對象B的過程,由主動行為變為了被動行為,控制權顛倒過來了,這就是“控制反轉”這個名稱的由來。
全部對象的控制權全部上繳給“第三方”IOC容器,所以,IOC容器成了整個系統的關鍵核心,它起到了一種類似“粘合劑”的作用,把系統中的所有對象粘合在一起發揮作用,如果沒有這個“粘合劑”,對象與對象之間會彼此失去聯系,這就是有人把IOC容器比喻成“粘合劑”的由來。
依賴注入
“獲得依賴對象的過程被反轉了”。控制被反轉之后,獲得依賴對象的過程由自身管理變為了由IOC容器主動注入。依賴注入是實現IOC的方法,就是由IOC容器在運行期間,動態地將某種依賴關系注入到對象之中。
BeanFactory和ApplicationContext有什么區別?
ApplicationContext是BeanFactory的子接口
ApplicationContext提供了更完整的功能:
①繼承MessageSource,因此支持國際化。
②統一的資源文件訪問方式。
③提供在監聽器中注冊bean的事件。
④同時加載多個配置文件。
⑤載入多個(有繼承關系)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層。
BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調用getBean()),才對該Bean進行加載實例化。這樣,我們就不能發現一些存在的Spring的配置問題。如果Bean的某一個屬性沒有注入,BeanFacotry加載后,直至第一次使用調用getBean方法才會拋出異常。
ApplicationContext,它是在容器啟動時,一次性創建了所有的Bean。這樣,在容器啟動時,我們就可以發現Spring中存在的配置錯誤,這樣有利於檢查所依賴屬性是否注入。
ApplicationContext啟動后預載入所有的單實例Bean,通過預載入單實例bean ,確保當你需要的時候,你就不用等待,因為它們已經創建好了。
相對於基本的BeanFactory,ApplicationContext 唯一的不足是占用內存空間。當應用程序配置Bean較多時,程序啟動較慢。
BeanFactory通常以編程的方式被創建,ApplicationContext還能以聲明的方式創建,如使用ContextLoader。
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動注冊,而ApplicationContext則是自動注冊。
描述一下Spring Bean的生命周期?
1、解析類得到BeanDefinition
2、如果有多個構造方法,則要推斷構造方法
3、確定好構造方法后,進行實例化得到一個對象
4、對對象中的加了@Autowired注解的屬性進行屬性填充
5、回調Aware方法,比如BeanNameAware,BeanFactoryAware
6、調用BeanPostProcessor的初始化前的方法
7、調用初始化方法
8、調用BeanPostProcessor的初始化后的方法,在這里會進行AOP
9、如果當前創建的bean是單例的則會把bean放入單例池
10、使用bean
11、Spring容器關閉時調用DisposableBean中destory()方法
解釋下Spring支持的幾種bean的作用域。
singleton
默認,每個容器中只有一個bean的實例,單例的模式由BeanFactory自身來維護。該對象的生命周期是與Spring IOC容器一致的(但在第一次被注入時才會創建)。
prototype
為每一個bean請求提供一個實例。在每次注入時都會創建一個新的對象
request
bean被定義為在每個HTTP請求中創建一個單例對象,也就是說在單個請求中都會復用這一個單例對象。
session
與request范圍類似,確保每個session中有一個bean的實例,在session過期后,bean會隨之失效。
application
bean被定義為在ServletContext的生命周期中復用一個單例對象。
websocket
bean被定義為在websocket的生命周期中復用一個單例對象。
global-session
全局作用域,global-session和Portlet應用相關。當你的應用部署在Portlet容器中工作時,它包含很多portlet。如果你想要聲明讓所有的portlet共用全局的存儲變量的話,那么這全局變量需要存儲在global-session中。全局作用域與Servlet中的session作用域效果相同。
Spring框架中的單例Bean是線程安全的么?
Spring中的Bean默認是單例模式的,框架並沒有對bean進行多線程的封裝處理。
如果Bean是有狀態的 那就需要開發人員自己來進行線程安全的保證,最簡單的辦法就是改變bean的作用域 把 "singleton"改為’‘protopyte’ 這樣每次請求Bean就相當於是 new Bean() 這樣就可以保證線程的安全了
(1)有狀態就是有數據存儲功能
(2)無狀態就是不會保存數據 controller、service和dao層本身並不是線程安全的,只是如果只是調用里面的方法,而且多線程調用一個實例的方法,會在內存中復制變量,這是自己的線程的工作內存,是安全的。
Dao會操作數據庫Connection,Connection是帶有狀態的,比如說數據庫事務,Spring的事務管理器使用Threadlocal為不同線程維護了一套獨立的connection副本,保證線程之間不會互相影響(Spring是如何保證事務獲取同一個Connection的)
不要在bean中聲明任何有狀態的實例變量或類變量,如果必須如此,那么就使用ThreadLocal把變量變為線程私有的,如果bean的實例變量或類變量需要在多個線程之間共享,那么就只能使用synchronized、lock、CAS等這些實現線程同步的方法了。
Spring 框架中都用到了哪些設計模式?
簡單工廠
由一個工廠類根據傳入的參數,動態決定應該創建哪一個產品類。
Spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得Bean對象,但是否是在傳入參數后創建還是傳入參數前創建這個要根據具體情況來定。
工廠方法
實現了FactoryBean接口的bean是一類叫做factory的bean。其特點是,spring會在使用getBean()調 用獲得該bean時,會自動調用該bean的getObject()方法,所以返回的不是factory這個bean,而是這個bean.getOjbect()方法的返回值。
單例模式
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點
spring對單例的實現: spring中的單例模式完成了后半句話,即提供了全局的訪問點BeanFactory。但沒有從構造器級別去控制單例,這是因為spring管理的是任意的java對象。
適配器模式
Spring定義了一個適配接口,使得每一種Controller有一種對應的適配器實現類,讓適配器代替 controller執行相應的方法。這樣在擴展Controller時,只需要增加一個適配器類就完成了SpringMVC 的擴展了。
裝飾器模式
動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。
Spring中用到的包裝器模式在類名上有兩種表現:一種是類名中含有Wrapper,另一種是類名中含有Decorator。
動態代理
切面在應用運行的時刻被織入。一般情況下,在織入切面時,AOP容器會為目標對象創建動態的創建一個代理對象。SpringAOP就是以這種方式織入切面的。 織入:把切面應用到目標對象並創建新的代理對象過程。
觀察者模式
spring的事件驅動模型使用的是 觀察者模式 ,Spring中Observer模式常用的地方是listener的實現。
策略模式
Spring框架的資源訪問Resource接口。該接口提供了更強的資源訪問能力,Spring 框架本身大量使用了 Resource 接口來訪問底層資源。
模板方法
父類定義了骨架(調用哪些方法及順序),某些特定方法由子類實現。
最大的好處
代碼復用,減少重復代碼。除了子類要實現的特定方法,其他方法及方法調用順序都在父類中預先寫好了。
refresh方法
Spring事務的實現方式和原理以及隔離級別?
在使用Spring框架時,可以有兩種使用事務的方式,一種是編程式的,一種是申明式的,@Transactional注解就是申明式的。
首先,事務這個概念是數據庫層面的,Spring只是基於數據庫中的事務進行了擴展,以及提供了一些能讓程序員更加方便操作事務的方式。
比如我們可以通過在某個方法上增加@Transactional注解,就可以開啟事務,這個方法中所有的sql都會在一個事務中執行,統一成功或失敗。
在一個方法上加了@Transactional注解后,Spring會基於這個類生成一個代理對象,會將這個代理對象作為bean,當在使用這個代理對象的方法時,如果這個方法上存在@Transactional注解,那么代理邏輯會先把事務的自動提交設置為false,然后再去執行原本的業務邏輯方法,如果執行業務邏輯方法沒有出現異常,那么代理邏輯中就會將事務進行提交,如果執行業務邏輯方法出現了異常,那么則會將事務進行回滾。
當然,針對哪些異常回滾事務是可以配置的,可以利用@Transactional注解中的rollbackFor屬性進行配置,默認情況下會對RuntimeException和Error進行回滾。
spring事務隔離級別就是數據庫的隔離級別:外加一個默認級別
(1)read uncommitted(未提交讀)
(2)read committed(提交讀、不可重復讀)
(3)repeatable read(可重復讀)
(4)serializable(可串行化)
數據庫的配置隔離級別是Read Commited,而Spring配置的隔離級別是Repeatable Read,請問這時隔離級別是以哪一個為准? 以Spring配置的為准,如果spring設置的隔離級別數據庫不支持,效果取決於數據庫。
Spring事務傳播機制
多個事務方法相互調用時,事務如何在這些方法間傳播
方法A是一個事務的方法,方法A執行過程中調用了方法B,那么方法B有無事務以及方法B對事務的要求不同都 會對方法A的事務具體執行造成影響,同時方法A的事務對方法B的事務執行也有影響,這種影響具體是什么就 由兩個方法所定義的事務傳播類型所決定。
REQUIRED(Spring默認的事務傳播類型):如果當前沒有事務,則自己新建一個事務,如果當前存在事務,則加入這個事務
SUPPORTS:當前存在事務,則加入當前事務,如果當前沒有事務,就以非事務方法執行
MANDATORY:當前存在事務,則加入當前事務,如果當前事務不存在,則拋出異常。
REQUIRES_NEW:創建一個新事務,如果存在當前事務,則掛起該事務。
NOT_SUPPORTED:以非事務方式執行,如果當前存在事務,則掛起當前事務
NEVER:不使用事務,如果當前事務存在,則拋出異常
NESTED:如果當前事務存在,則在嵌套事務中執行,否則REQUIRED的操作一樣(開啟一個事務)
和REQUIRES_NEW的區別
REQUIRES_NEW是新建一個事務並且新開啟的這個事務與原有事務無關,而NESTED則是當前存在事務時(我們把當前事務稱之為父事務)會開啟一個嵌套事務(稱之為一個子事務)。 在NESTED情況下父事務回滾時, 子事務也會回滾,而在REQUIRES_NEW情況下,原有事務回滾,不會影響新開啟的事務。
和REQUIRED的區別
REQUIRED情況下,調用方存在事務時,則被調用方和調用方使用同一事務,那么被調用方出現異常時,由於共用一個事務,所以無論調用方是否catch其異常,事務都會回滾 而在NESTED情況下,被調用方發生異常 時,調用方可以catch其異常,這樣只有子事務回滾,父事務不受影響。
Spring事務什么時候會失效?
spring事務的原理是AOP,進行了切面增強,那么失效的根本原因是這個AOP不起作用了!常見情況有如下幾種
1、發生自調用,類里面使用this調用本類的方法(this通常省略),此時這個this對象不是代理類,而是UserService對象本身!
解決方法很簡單,讓那個this變成UserService的代理類即可!
2、方法不是public的
@Transactional 只能用於 public 的方法上,否則事務不會失效,如果要用在非 public 方法上,可以開啟AspectJ 代理模式。
3、數據庫不支持事務
4、沒有被spring管理
5、異常被吃掉,事務不會回滾(或者拋出的異常沒有被定義,默認為RuntimeException)
什么是bean的自動裝配,有哪些方式?
開啟自動裝配,只需要在xml配置文件中定義“autowire”屬性。
<bean id="cutomer" class="com.xxx.xxx.Customer" autowire="" />
autowire屬性有五種裝配的方式:
(1)no – 缺省情況下,自動配置是通過“ref”屬性手動設定 。
手動裝配:以value或ref的方式明確指定屬性值都是手動裝配。
需要通過‘ref’屬性來連接bean。
(2)byName-根據bean的屬性名稱進行自動裝配。
Cutomer的屬性名稱是person,Spring會將bean id為person的bean通過setter方法進行自動裝 配。
<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="byName"/>
<bean id="person" class="com.xxx.xxx.Person"/>
(3)byType-根據bean的類型進行自動裝配。
Cutomer的屬性person的類型為Person,Spirng會將Person類型通過setter方法進行自動裝配。
<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="byType"/>
<bean id="person" class="com.xxx.xxx.Person"/>
(4)constructor-類似byType,不過是應用於構造器的參數。如果一個bean與構造器參數的類型形
同,則進行自動裝配,否則導致異常。
Cutomer構造函數的參數person的類型為Person,Spirng會將Person類型通過構造方法進行自動裝配
<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="construtor"/>
<bean id="person" class="com.xxx.xxx.Person"/>
(5)autodetect-如果有默認的構造器,則通過constructor方式進行自動裝配,否則使用byType方式
進行自動裝配。
如果有默認的構造器,則通過constructor方式進行自動裝配,否則使用byType方式進行自動裝配。
@Autowired自動裝配bean,可以在字段、setter方法、構造函數上使用。
