1、JDK 動態代理和 CGLIB 代理有什么區別?
2、FactoryBean、BeanFactory、ApplicationContext 有什么區別?
3、說一說Spring Bean 的生命周期?
4、依賴注入的實現方法,以及相關注解?
5、什么是 Spring IOC ?
6、Spring IOC 容器的構建流程(初始化過程)
7、依賴注入的過程(Bean 的加載流程)?
8、Bean 的作用范圍?
9、Spring事務傳播機制有哪些?
10、Spring 的事務隔離級別有哪些?
11、AOP 是什么?AOP有哪些應用場景?
12、AOP 的相關注解有哪些?
13、AOP 的相關術語有什么?
14、總結
JDK 動態代理主要是針對類實現了某個接口,AOP 則會使用 JDK 動態代理。他基于反射的機制實現,生成一個實現同樣接口的一個代理類,然后通過重寫方法的方式,實現對代碼的增強。
而如果某個類沒有實現接口,AOP 則會使用 CGLIB 代理。他的底層原理是基于 ASM 第三方框架,通過修改字節碼生成一個子類,然后重寫父類的方法,實現對代碼的增強。
詳細分析參考:【Java萌新】面試常問設計模式——代理模式
BeanFactory:是一個 Bean 工廠,使用簡單工廠模式,是 Spring IoC 容器頂級接口,是用于管理 Bean 的工廠,最核心的功能是通過 getBean()
方法加載 Bean 對象,通常我們不會直接使用該接口,而是使用其子接口 ApplicationContext。
FactoryBean:是一個工廠 Bean,使用了工廠方法模式,實現該接口的類可以自己定義要創建的 Bean 實例,只需要實現它的 getObject()
方法即可。
ApplicationConext:是 BeanFactory 的子接口,擴展了 BeanFactory 的功能(高級 IOC 容器)。
Spring Bean 生命周期簡單概括為 5 個階段:
Bean 的實例化階段:創建一個 Bean 對象。
Bean 實例的屬性填充階段:為 Bean 實例的屬性賦值。
Bean 實例的初始化階段:對 Bean 實例進行初始化。
Bean 實例的正常使用階段。
Bean 實例的銷毀階段:容器關閉后,將 Bean 實例銷毀。
構造方法注入、Setter 方法注入、接口注入 三種。
依賴注入的相關注解
@Autowired:自動按類型注入,如果有多個匹配則按照指定 Bean 的 id 查找,查找不到會報錯。
@Qualifier:在自動按照類型注入的基礎上再按照 Bean 的 id 注入,給變量注入時必須搭配@Autowired,給方法注入時可單獨使用。
@Resource :直接按照 Bean 的 id 注入,只能注入 Bean 類型。
@Value :用于注入基本數據類型和 String 類型。
IOC 即控制反轉,簡單來說就是把原來代碼里需要實現的對象創建、依賴反轉給容器來幫忙實現,Spring 中管理對象及其依賴關系都是通過 Spring 的 IOC 容器實現的。
IOC 的實現方式有依賴注入和依賴查找,由于依賴查找使用的很少,因此 IOC 也叫做依賴注入。
我們之前在創建一個對象的時候都是直接 new
一個對象實例,而有了 IOC ,對象實例的創建都交給容器去實現即可。
我們以 XML 方式的容器初始化為例:
通過 ClassPathXmlApplicationContext
,去創建 ApplicationContext
容器對象:
ApplicationContextcontext=newClassPathXmlApplicationContext("spring-config.xml");
ClassPathXmlApplicationContext
創建容器對象時,構造方法做了如下兩件事:
① 調用父容器的構造方法為容器先設置好 Bean 資源加載器。
② 調用父類的 setConfigLocations() 方法設置 Bean 配置信息的定位路徑
③ 調用父類 AbstractApplicationContext 的 refresh() 方法啟動整個 IOC 容器對 Bean 的載入,在創建 IOC 容器前如果已有容器存在,需要把已有的容器銷毀,保證在 refresh() 方法后使用的是新創建的 IOC 容器。
容器創建完成后,通過 loadBeanDefinitions()
方法加載 Bean 配置資源,該方法在加載資源時,首先解析配置文件路徑,讀取配置文件的內容,然后通過 XML 解析器將 Bean 的配置信息轉換成文檔對象,之后按照 Spring Bean 的定義規則將文檔對象解析為 BeanDefinition 對象。
接下來,將解析得到的 BeanDefinition 對象存入本地緩存(一個 HashMap 集合,key 是字符串,值是 BeanDefinition)中。
最后,實例化所有的 Bean 實例(非懶加載):包括實例的創建,實例的屬性填充,實例的初始化。
源碼分析可以參考我的文章:Spring源碼分析——Bean的加載
先來看下面幾行代碼:
publicclassBeanFactoryTest{publicstaticvoidmain(String[]args){//加載與解析XML配置文件,獲得BeanFactory:BeanFactorybeanFactory=newXmlBeanFactory(newClassPathResource("spring-bf.xml"));//從BeanFactory中加載Bean對象Objecta=beanFactory.getBean("componentA");Objectb=beanFactory.getBean("componentB");System.out.println(a);//com.myspring.test.xmltest.ComponentA@1c93084cSystem.out.println(b);//com.myspring.test.xmltest.ComponentB@6ef888f6}}
首先通過 BeanFactory/ApplicationContext 調用getBean()
方法,來獲取 Bean 實例,該方法中,真正獲取 Bean 實例的是其內層方法 doGetBean()
方法(真正實現從 IOC 容器獲取 Bean ,也是觸發依賴注入的地方)。
在 doGetBean()
方法中,主要做了以下幾件事:
① beanName 的轉換方法 transformedBeanName(name),
該方法的作用是,根據傳入的 name 參數,獲取真正的 Bean 對應的 beanName。該方法的 name 參數,有可能是一個別名(alias 屬性設置的別名),也有可能是一個&開頭的 name (工廠 Bean 對象)。
② 嘗試從緩存中加載 Bean 的單實例,根據上面transformedBeanName
方法轉換 name 后得到的真實 beanName,getSingleton(beanName)
方法直接嘗試從緩存中獲取 Bean 的共享單實例,這時候獲取的是初始狀態,尚未實例化。(從緩存中加載的流程就是,根據 beanName 依次從一級緩存、二級緩存、三級緩存中嘗試獲取,通過三級緩存機制也可以有效避免循環依賴)
③ Bean 的實例化,getSingleton(beanName)
方法執行后,從緩存中得到了 Bean 的原始狀態,接下來需要對該 Bean 進行實例化。
④ Bean 的初始化:尋找依賴(循環依賴檢查、依賴注入),因為 Bean 的初始化過程中很可能會用到某些屬性,而某些屬性很可能是動態配置的,并且配置的成依賴于其他的 Bean,那么此時應該先加載依賴的 Bean。所以在流程中,Spring初始化一個 Bean,會先初始化其依賴的所有的其他 Bean。
⑤ 根據不同的 scope 作用域創建 Bean,調用doCreateBean()
方法創建 Bean。
⑥ 類型轉換,根據 scope 創建完 Bean 成功后,一般可以直接返回即可。但當傳入 doGetBean
方法中的 requireType
參數不為空時,意味著我們對最后返回的 Bean 有著類型上的要求。Spring 通過 類型轉換器 將第 ⑤ 步創建完成的 Bean 轉換為 requireType
指定的類型。
通過 scope 屬性指定 Bean 的作用范圍,包括:
① singleton
:單例模式,是默認作用域,不管收到多少 Bean 請求每個容器中只有一個唯一的 Bean 實例。
② prototype
:原型模式,和 singleton 相反,每次 Bean 請求都會創建一個新的實例。
③ request
:每次 HTTP 請求都會創建一個新的 Bean 并把它放到 request 域中,在請求完成后 Bean 會失效并被垃圾收集器回收。
④ session
:和 request 類似,確保每個 session 中有一個 Bean 實例,session 過期后 bean 會隨之失效。
⑤ global session
:當應用部署在 Portlet 容器時,如果想讓所有 Portlet 共用全局存儲變量,那么該變量需要存儲在 global session 中。
① REQUIRED:Spring 默認的事務傳播級別,如果上下文中已經存在事務,那么就加入到事務中執行,如果當前上下文中不存在事務,則新建事務執行。
② REQUIRES_NEW:每次都會新建一個事務,如果上下文中有事務,則將上下文的事務掛起,當新建事務執行完成以后,上下文事務再恢復執行。
③ SUPPORTS:如果上下文存在事務,則加入到事務執行,如果沒有事務,則使用非事務的方式執行。
④ MANDATORY:上下文中必須要存在事務,否則就會拋出異常。
⑤ NOT_SUPPORTED :如果上下文中存在事務,則掛起事務,執行當前邏輯,結束后恢復上下文的事務。
⑥ NEVER:上下文中不能存在事務,否則就會拋出異常。
⑦ ESTED:嵌套事務。如果上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務。
Spring 的事務隔離級別底層其實是基于數據庫的,Spring 并沒有自己的一套隔離級別。
DEFAULT:使用數據庫的默認隔離級別。
READ_UNCOMMITTED:讀未提交,最低的隔離級別,會讀取到其他事務還未提交的內容,存在臟讀。
READ_COMMITTED:讀已提交,讀取到的內容都是已經提交的,可以解決臟讀,但是存在不可重復讀。
REPEATABLE_READ:可重復讀,在一個事務中多次讀取時看到相同的內容,可以解決不可重復讀,但是存在幻讀。
SERIALIZABLE:串行化,最高的隔離級別,對于同一行記錄,寫會加寫鎖,讀會加讀鎖。在這種情況下,只有讀讀能并發執行,其他并行的讀寫、寫讀、寫寫操作都是沖突的,需要串行執行??梢苑乐古K讀、不可重復度、幻讀,沒有并發事務問題。
AOP 概念: 即面向切面編程,使用動態代理技術,在不修改源碼的基礎上對目標方法進行增強。
Spring 中的 AOP 目前支持 JDK 動態代理和 Cglib 代理。如果被代理對象實現了接口,則使用 JDK 動態代理,否則使用 Cglib 代理。另外,也可以通過指定 proxyTargetClass=true
來實現強制走 Cglib 代理。
應用場景:
權限認證
日志打印
事務
…
@Aspect
:切面,聲明被注解標注的類是一個切面 Bean。
@Aspect@ComponentpublicclassLogAspect{...}
@Pointcut
:切入點,可以通過 @Pointcut("execution(* top.csp1999.service.impl.*.*(..))")
去指定要切入的目標對象,并對其符合表達式要求的方法進行增強。
@Pointcut("execution(*top.csp1999.service.impl.*.*(..))")publicvoidoperationLog(){}
@Before
:前置通知,指在某個連接點之前執行的通知。
@Before("operationLog()")publicvoiddoBeforeAdvice(JoinPointjoinPoint){System.out.println("進入方法前執行.....");}
@After
:后置通知,指某個連接點退出時執行的通知(不論正常返回還是異常退出)。
@After("operationLog()")publicvoidafter(JoinPointjp){System.out.println("方法最后執行.....");}
@AfterReturning
:后置返回通知,指某連接點正常完成之后執行的通知,返回值可以在返回后通知方法里接收。
@AfterReturning(returning="ret",pointcut="operationLog()")publicvoiddoAfterReturning(Objectret){System.out.println("方法的返回值:"+ret);}
@AfterThrowing
:后置異常通知,指方法拋出異常導致退出時執行的通知,和@AfterReturning只會有一個執行,異常使用 throwing 屬性接收。
@AfterThrowing(throwing="jp",pointcut="operationLog()")publicvoidthrowss(JoinPointjp){System.out.println("方法異常時執行.....");}
@Around
:環繞通知,可以用來在調用一個具體方法前和調用后來完成一些具體的任務。
@Around("operationLog()")publicObjectrun2(ProceedingJoinPointjoinPoint)throwsThrowable{//獲取方法參數值數組Object[]args=joinPoint.getArgs();//得到其方法簽名MethodSignaturemethodSignature=(MethodSignature)joinPoint.getSignature();//獲取方法參數類型數組Class[]paramTypeArray=methodSignature.getParameterTypes();if(EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length-1])){//如果方法的參數列表最后一個參數是entityManager類型,則給其賦值args[args.length-1]=entityManager;}logger.info("請求參數為{}",args);//動態修改其參數//注意,如果調用joinPoint.proceed()方法,則修改的參數值不會生效,必須調用joinPoint.proceed(Object[]args)Objectresult=joinPoint.proceed(args);logger.info("響應結果為{}",result);//如果這里不返回result,則目標對象實際返回值會被置為nullreturnresult;}
Aspect
:切面,一個關注點的模塊化,這個關注點可能會橫切多個對象。
Joinpoint
:連接點,程序執行過程中的某一行為,即業務層中的所有方法。。
Advice
:通知,指切面對于某個連接點所產生的動作,包括前置通知、后置通知、返回后通知、異常通知和環繞通知。
Pointcut
:切入點,指被攔截的連接點,切入點一定是連接點,但連接點不一定是切入點。
Proxy
:代理,Spring AOP 中有 JDK 動態代理和 CGLib 代理,目標對象實現了接口時采用 JDK 動態代理,反之采用 CGLib 代理。
Target
:代理的目標對象,指一個或多個切面所通知的對象。
Weaving
:織入,指把增強應用到目標對象來創建代理對象的過程。
“spring的含義以及作用是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注本站網站,小編將為大家輸出更多高質量的實用文章!
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
c語言中正確的字符常量是用一對單引號將一個字符括起表示合法的字符常量。例如‘a’。數值包括整型、浮點型。整型可用十進制,八進制,十六進制。八進制前面要加0,后面...
2022年天津專場考試原定于3月19日舉行,受疫情影響確定延期,但目前延期后的考試時間推遲。 符合報名條件的考生,須在規定時間登錄招考資訊網(www.zha...
:喜歡聽,樂意看。指很受歡迎?!巴卣官Y料”喜聞樂見:[ xǐ wén lè jiàn ]詳細解釋1. 【解釋】:喜歡聽,樂意看。指很受歡迎。2. 【示例】:這是...
【資料圖】最近小編看到大家都在討論如何贈送q幣相關的事情,對此呢小編也是非常的感應興趣,那么這件事究竟是怎么發生的呢?具體又是怎么回事呢?下面就是小編搜索到的關于如何贈送q幣事件的相關信息,我們一起來看一下吧!1、進入騰訊充值中心,點擊【Q幣充值】。2、點擊【更換】選中要贈送的好友。3、填寫贈送數量,選擇【付款方式】,等待付款成功即可。4、Q幣是由騰訊推出的一種虛擬貨幣,可以用來支付QQ的QQ行號...
招商銀行滯納金能減免嘛?招商銀行滯納金是不可以減免的。滯納金是招商銀行對于信用卡逾期行為的一種罰款,因此是不可以減免的。除此之外,信用卡逾期還款除了會產生滯納金,還會產生利息。招商銀行滯納金會一直收嗎?至于滯納金,則不會像逾期利息那樣一直計收,招行信用卡12萬逾期一個月而是每逾期一次,就按照最低還款額未還部分的5%來計收一次滯納金。 大家還需要注意,在計收利息的時候。 三個月沒收入可不可以申請救助...
(資料圖片僅供參考)提起如何辦光大銀行信用卡大家在熟悉不過了,被越來越多的人所熟知,那你知道如何辦光大銀行信用卡嗎?快和小編一起去了解一下吧!辦理光大銀行信用卡的方法有:1、官網申請:申請人可登錄光大銀行信用卡官網,在線選擇合適的卡種,然后填寫申請資料,提交審核即可,審核通過即可下卡。2、柜臺申請:申請人可到光大銀行營業廳柜臺申請,提交相關資料后,接受銀行工作人員的提問,審批通過后即可下卡。...