您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“spring的含義以及作用是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
1、JDK 動(dòng)態(tài)代理和 CGLIB 代理有什么區(qū)別?
2、FactoryBean、BeanFactory、ApplicationContext 有什么區(qū)別?
3、說一說Spring Bean 的生命周期?
4、依賴注入的實(shí)現(xiàn)方法,以及相關(guān)注解?
5、什么是 Spring IOC ?
6、Spring IOC 容器的構(gòu)建流程(初始化過程)
7、依賴注入的過程(Bean 的加載流程)?
8、Bean 的作用范圍?
9、Spring事務(wù)傳播機(jī)制有哪些?
10、Spring 的事務(wù)隔離級(jí)別有哪些?
11、AOP 是什么?AOP有哪些應(yīng)用場(chǎng)景?
12、AOP 的相關(guān)注解有哪些?
13、AOP 的相關(guān)術(shù)語有什么?
14、總結(jié)
JDK 動(dòng)態(tài)代理主要是針對(duì)類實(shí)現(xiàn)了某個(gè)接口,AOP 則會(huì)使用 JDK 動(dòng)態(tài)代理。他基于反射的機(jī)制實(shí)現(xiàn),生成一個(gè)實(shí)現(xiàn)同樣接口的一個(gè)代理類,然后通過重寫方法的方式,實(shí)現(xiàn)對(duì)代碼的增強(qiáng)。
而如果某個(gè)類沒有實(shí)現(xiàn)接口,AOP 則會(huì)使用 CGLIB 代理。他的底層原理是基于 ASM 第三方框架,通過修改字節(jié)碼生成一個(gè)子類,然后重寫父類的方法,實(shí)現(xiàn)對(duì)代碼的增強(qiáng)。
詳細(xì)分析參考:【Java萌新】面試常問設(shè)計(jì)模式——代理模式
BeanFactory:是一個(gè) Bean 工廠,使用簡(jiǎn)單工廠模式,是 Spring IoC 容器頂級(jí)接口,是用于管理 Bean 的工廠,最核心的功能是通過 getBean()
方法加載 Bean 對(duì)象,通常我們不會(huì)直接使用該接口,而是使用其子接口 ApplicationContext。
FactoryBean:是一個(gè)工廠 Bean,使用了工廠方法模式,實(shí)現(xiàn)該接口的類可以自己定義要?jiǎng)?chuàng)建的 Bean 實(shí)例,只需要實(shí)現(xiàn)它的 getObject()
方法即可。
ApplicationConext:是 BeanFactory 的子接口,擴(kuò)展了 BeanFactory 的功能(高級(jí) IOC 容器)。
Spring Bean 生命周期簡(jiǎn)單概括為 5 個(gè)階段:
Bean 的實(shí)例化階段:創(chuàng)建一個(gè) Bean 對(duì)象。
Bean 實(shí)例的屬性填充階段:為 Bean 實(shí)例的屬性賦值。
Bean 實(shí)例的初始化階段:對(duì) Bean 實(shí)例進(jìn)行初始化。
Bean 實(shí)例的正常使用階段。
Bean 實(shí)例的銷毀階段:容器關(guān)閉后,將 Bean 實(shí)例銷毀。
構(gòu)造方法注入、Setter 方法注入、接口注入 三種。
依賴注入的相關(guān)注解
@Autowired:自動(dòng)按類型注入,如果有多個(gè)匹配則按照指定 Bean 的 id 查找,查找不到會(huì)報(bào)錯(cuò)。
@Qualifier:在自動(dòng)按照類型注入的基礎(chǔ)上再按照 Bean 的 id 注入,給變量注入時(shí)必須搭配@Autowired,給方法注入時(shí)可單獨(dú)使用。
@Resource :直接按照 Bean 的 id 注入,只能注入 Bean 類型。
@Value :用于注入基本數(shù)據(jù)類型和 String 類型。
IOC 即控制反轉(zhuǎn),簡(jiǎn)單來說就是把原來代碼里需要實(shí)現(xiàn)的對(duì)象創(chuàng)建、依賴反轉(zhuǎn)給容器來幫忙實(shí)現(xiàn),Spring 中管理對(duì)象及其依賴關(guān)系都是通過 Spring 的 IOC 容器實(shí)現(xiàn)的。
IOC 的實(shí)現(xiàn)方式有依賴注入和依賴查找,由于依賴查找使用的很少,因此 IOC 也叫做依賴注入。
我們之前在創(chuàng)建一個(gè)對(duì)象的時(shí)候都是直接 new
一個(gè)對(duì)象實(shí)例,而有了 IOC ,對(duì)象實(shí)例的創(chuàng)建都交給容器去實(shí)現(xiàn)即可。
我們以 XML 方式的容器初始化為例:
通過 ClassPathXmlApplicationContext
,去創(chuàng)建 ApplicationContext
容器對(duì)象:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ClassPathXmlApplicationContext
創(chuàng)建容器對(duì)象時(shí),構(gòu)造方法做了如下兩件事:
① 調(diào)用父容器的構(gòu)造方法為容器先設(shè)置好 Bean 資源加載器。
② 調(diào)用父類的 setConfigLocations() 方法設(shè)置 Bean 配置信息的定位路徑
③ 調(diào)用父類 AbstractApplicationContext 的 refresh() 方法啟動(dòng)整個(gè) IOC 容器對(duì) Bean 的載入,在創(chuàng)建 IOC 容器前如果已有容器存在,需要把已有的容器銷毀,保證在 refresh() 方法后使用的是新創(chuàng)建的 IOC 容器。
容器創(chuàng)建完成后,通過 loadBeanDefinitions()
方法加載 Bean 配置資源,該方法在加載資源時(shí),首先解析配置文件路徑,讀取配置文件的內(nèi)容,然后通過 XML 解析器將 Bean 的配置信息轉(zhuǎn)換成文檔對(duì)象,之后按照 Spring Bean 的定義規(guī)則將文檔對(duì)象解析為 BeanDefinition 對(duì)象。
接下來,將解析得到的 BeanDefinition 對(duì)象存入本地緩存(一個(gè) HashMap 集合,key 是字符串,值是 BeanDefinition)中。
最后,實(shí)例化所有的 Bean 實(shí)例(非懶加載):包括實(shí)例的創(chuàng)建,實(shí)例的屬性填充,實(shí)例的初始化。
源碼分析可以參考我的文章:Spring源碼分析——Bean的加載
先來看下面幾行代碼:
public class BeanFactoryTest { public static void main(String[] args) { // 加載與解析XML配置文件,獲得BeanFactory: BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bf.xml")); // 從BeanFactory中加載Bean對(duì)象 Object a = beanFactory.getBean("componentA"); Object b = beanFactory.getBean("componentB"); System.out.println(a);// com.myspring.test.xmltest.ComponentA@1c93084c System.out.println(b);// com.myspring.test.xmltest.ComponentB@6ef888f6 } }
首先通過 BeanFactory/ApplicationContext 調(diào)用getBean()
方法,來獲取 Bean 實(shí)例,該方法中,真正獲取 Bean 實(shí)例的是其內(nèi)層方法 doGetBean()
方法(真正實(shí)現(xiàn)從 IOC 容器獲取 Bean ,也是觸發(fā)依賴注入的地方)。
在 doGetBean()
方法中,主要做了以下幾件事:
① beanName 的轉(zhuǎn)換方法 transformedBeanName(name),
該方法的作用是,根據(jù)傳入的 name 參數(shù),獲取真正的 Bean 對(duì)應(yīng)的 beanName。該方法的 name 參數(shù),有可能是一個(gè)別名(alias 屬性設(shè)置的別名),也有可能是一個(gè)&開頭的 name (工廠 Bean 對(duì)象)。
② 嘗試從緩存中加載 Bean 的單實(shí)例,根據(jù)上面transformedBeanName
方法轉(zhuǎn)換 name 后得到的真實(shí) beanName,getSingleton(beanName)
方法直接嘗試從緩存中獲取 Bean 的共享單實(shí)例,這時(shí)候獲取的是初始狀態(tài),尚未實(shí)例化。(從緩存中加載的流程就是,根據(jù) beanName 依次從一級(jí)緩存、二級(jí)緩存、三級(jí)緩存中嘗試獲取,通過三級(jí)緩存機(jī)制也可以有效避免循環(huán)依賴)
③ Bean 的實(shí)例化,getSingleton(beanName)
方法執(zhí)行后,從緩存中得到了 Bean 的原始狀態(tài),接下來需要對(duì)該 Bean 進(jìn)行實(shí)例化。
④ Bean 的初始化:尋找依賴(循環(huán)依賴檢查、依賴注入),因?yàn)?Bean 的初始化過程中很可能會(huì)用到某些屬性,而某些屬性很可能是動(dòng)態(tài)配置的,并且配置的成依賴于其他的 Bean,那么此時(shí)應(yīng)該先加載依賴的 Bean。所以在流程中,Spring初始化一個(gè) Bean,會(huì)先初始化其依賴的所有的其他 Bean。
⑤ 根據(jù)不同的 scope 作用域創(chuàng)建 Bean,調(diào)用doCreateBean()
方法創(chuàng)建 Bean。
⑥ 類型轉(zhuǎn)換,根據(jù) scope 創(chuàng)建完 Bean 成功后,一般可以直接返回即可。但當(dāng)傳入 doGetBean
方法中的 requireType
參數(shù)不為空時(shí),意味著我們對(duì)最后返回的 Bean 有著類型上的要求。Spring 通過 類型轉(zhuǎn)換器 將第 ⑤ 步創(chuàng)建完成的 Bean 轉(zhuǎn)換為 requireType
指定的類型。
通過 scope 屬性指定 Bean 的作用范圍,包括:
① singleton
:?jiǎn)卫J?,是默認(rèn)作用域,不管收到多少 Bean 請(qǐng)求每個(gè)容器中只有一個(gè)唯一的 Bean 實(shí)例。
② prototype
:原型模式,和 singleton 相反,每次 Bean 請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
③ request
:每次 HTTP 請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 Bean 并把它放到 request 域中,在請(qǐng)求完成后 Bean 會(huì)失效并被垃圾收集器回收。
④ session
:和 request 類似,確保每個(gè) session 中有一個(gè) Bean 實(shí)例,session 過期后 bean 會(huì)隨之失效。
⑤ global session
:當(dāng)應(yīng)用部署在 Portlet 容器時(shí),如果想讓所有 Portlet 共用全局存儲(chǔ)變量,那么該變量需要存儲(chǔ)在 global session 中。
① REQUIRED:Spring 默認(rèn)的事務(wù)傳播級(jí)別,如果上下文中已經(jīng)存在事務(wù),那么就加入到事務(wù)中執(zhí)行,如果當(dāng)前上下文中不存在事務(wù),則新建事務(wù)執(zhí)行。
② REQUIRES_NEW:每次都會(huì)新建一個(gè)事務(wù),如果上下文中有事務(wù),則將上下文的事務(wù)掛起,當(dāng)新建事務(wù)執(zhí)行完成以后,上下文事務(wù)再恢復(fù)執(zhí)行。
③ SUPPORTS:如果上下文存在事務(wù),則加入到事務(wù)執(zhí)行,如果沒有事務(wù),則使用非事務(wù)的方式執(zhí)行。
④ MANDATORY:上下文中必須要存在事務(wù),否則就會(huì)拋出異常。
⑤ NOT_SUPPORTED :如果上下文中存在事務(wù),則掛起事務(wù),執(zhí)行當(dāng)前邏輯,結(jié)束后恢復(fù)上下文的事務(wù)。
⑥ NEVER:上下文中不能存在事務(wù),否則就會(huì)拋出異常。
⑦ ESTED:嵌套事務(wù)。如果上下文中存在事務(wù),則嵌套事務(wù)執(zhí)行,如果不存在事務(wù),則新建事務(wù)。
Spring 的事務(wù)隔離級(jí)別底層其實(shí)是基于數(shù)據(jù)庫(kù)的,Spring 并沒有自己的一套隔離級(jí)別。
DEFAULT:使用數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。
READ_UNCOMMITTED:讀未提交,最低的隔離級(jí)別,會(huì)讀取到其他事務(wù)還未提交的內(nèi)容,存在臟讀。
READ_COMMITTED:讀已提交,讀取到的內(nèi)容都是已經(jīng)提交的,可以解決臟讀,但是存在不可重復(fù)讀。
REPEATABLE_READ:可重復(fù)讀,在一個(gè)事務(wù)中多次讀取時(shí)看到相同的內(nèi)容,可以解決不可重復(fù)讀,但是存在幻讀。
SERIALIZABLE:串行化,最高的隔離級(jí)別,對(duì)于同一行記錄,寫會(huì)加寫鎖,讀會(huì)加讀鎖。在這種情況下,只有讀讀能并發(fā)執(zhí)行,其他并行的讀寫、寫讀、寫寫操作都是沖突的,需要串行執(zhí)行??梢苑乐古K讀、不可重復(fù)度、幻讀,沒有并發(fā)事務(wù)問題。
AOP 概念: 即面向切面編程,使用動(dòng)態(tài)代理技術(shù),在不修改源碼的基礎(chǔ)上對(duì)目標(biāo)方法進(jìn)行增強(qiáng)。
Spring 中的 AOP 目前支持 JDK 動(dòng)態(tài)代理和 Cglib 代理。如果被代理對(duì)象實(shí)現(xiàn)了接口,則使用 JDK 動(dòng)態(tài)代理,否則使用 Cglib 代理。另外,也可以通過指定 proxyTargetClass=true
來實(shí)現(xiàn)強(qiáng)制走 Cglib 代理。
應(yīng)用場(chǎng)景:
權(quán)限認(rèn)證
日志打印
事務(wù)
…
@Aspect
:切面,聲明被注解標(biāo)注的類是一個(gè)切面 Bean。
@Aspect @Component public class LogAspect { ... }
@Pointcut
:切入點(diǎn),可以通過 @Pointcut("execution(* top.csp1999.service.impl.*.*(..))")
去指定要切入的目標(biāo)對(duì)象,并對(duì)其符合表達(dá)式要求的方法進(jìn)行增強(qiáng)。
@Pointcut("execution(* top.csp1999.service.impl.*.*(..))") public void operationLog(){}
@Before
:前置通知,指在某個(gè)連接點(diǎn)之前執(zhí)行的通知。
@Before("operationLog()") public void doBeforeAdvice(JoinPoint joinPoint){ System.out.println("進(jìn)入方法前執(zhí)行....."); }
@After
:后置通知,指某個(gè)連接點(diǎn)退出時(shí)執(zhí)行的通知(不論正常返回還是異常退出)。
@After("operationLog()") public void after(JoinPoint jp){ System.out.println("方法最后執(zhí)行....."); }
@AfterReturning
:后置返回通知,指某連接點(diǎn)正常完成之后執(zhí)行的通知,返回值可以在返回后通知方法里接收。
@AfterReturning(returning = "ret", pointcut = "operationLog()") public void doAfterReturning(Object ret) { System.out.println("方法的返回值 : " + ret); }
@AfterThrowing
:后置異常通知,指方法拋出異常導(dǎo)致退出時(shí)執(zhí)行的通知,和@AfterReturning只會(huì)有一個(gè)執(zhí)行,異常使用 throwing 屬性接收。
@AfterThrowing(throwing = "jp", pointcut = "operationLog()") public void throwss(JoinPoint jp){ System.out.println("方法異常時(shí)執(zhí)行....."); }
@Around
:環(huán)繞通知,可以用來在調(diào)用一個(gè)具體方法前和調(diào)用后來完成一些具體的任務(wù)。
@Around("operationLog()") public Object run2(ProceedingJoinPoint joinPoint) throws Throwable { // 獲取方法參數(shù)值數(shù)組 Object[] args = joinPoint.getArgs(); // 得到其方法簽名 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); // 獲取方法參數(shù)類型數(shù)組 Class[] paramTypeArray = methodSignature.getParameterTypes(); if (EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length - 1])) { // 如果方法的參數(shù)列表最后一個(gè)參數(shù)是entityManager類型,則給其賦值 args[args.length - 1] = entityManager; } logger.info("請(qǐng)求參數(shù)為{}",args); // 動(dòng)態(tài)修改其參數(shù) // 注意,如果調(diào)用joinPoint.proceed()方法,則修改的參數(shù)值不會(huì)生效,必須調(diào)用joinPoint.proceed(Object[] args) Object result = joinPoint.proceed(args); logger.info("響應(yīng)結(jié)果為{}",result); // 如果這里不返回result,則目標(biāo)對(duì)象實(shí)際返回值會(huì)被置為null return result; }
Aspect
:切面,一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì)橫切多個(gè)對(duì)象。
Joinpoint
:連接點(diǎn),程序執(zhí)行過程中的某一行為,即業(yè)務(wù)層中的所有方法。。
Advice
:通知,指切面對(duì)于某個(gè)連接點(diǎn)所產(chǎn)生的動(dòng)作,包括前置通知、后置通知、返回后通知、異常通知和環(huán)繞通知。
Pointcut
:切入點(diǎn),指被攔截的連接點(diǎn),切入點(diǎn)一定是連接點(diǎn),但連接點(diǎn)不一定是切入點(diǎn)。
Proxy
:代理,Spring AOP 中有 JDK 動(dòng)態(tài)代理和 CGLib 代理,目標(biāo)對(duì)象實(shí)現(xiàn)了接口時(shí)采用 JDK 動(dòng)態(tài)代理,反之采用 CGLib 代理。
Target
:代理的目標(biāo)對(duì)象,指一個(gè)或多個(gè)切面所通知的對(duì)象。
Weaving
:織入,指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來創(chuàng)建代理對(duì)象的過程。
“spring的含義以及作用是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。