最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

一篇文章搞懂spring aop源碼

2019-12-05 20:55 作者:P8架構(gòu)師諸葛  | 我要投稿

Aop是什么

與OOP對(duì)比,面向切面,傳統(tǒng)的OOP開(kāi)發(fā)中的代碼邏輯是自上而下的,而這些過(guò)程會(huì)產(chǎn)生一些橫切性問(wèn)題,這些橫切性的問(wèn)題和我們的主業(yè)務(wù)邏輯關(guān)系不大,這些橫切性問(wèn)題不會(huì)影響到主邏輯實(shí)現(xiàn)的,但是會(huì)散落到代碼的各個(gè)部分,難以維護(hù)。AOP是處理一些橫切性問(wèn)題,AOP的編程思想就是把這些問(wèn)題和主業(yè)務(wù)邏輯分開(kāi),達(dá)到與主業(yè)務(wù)邏輯解耦的目的。使代碼的重用性和開(kāi)發(fā)效率更高。

aop的應(yīng)用場(chǎng)景

  • 日志記錄

  • 權(quán)限驗(yàn)證

  • 效率檢查

  • 事務(wù)管理

  • exception


springAop的底層技術(shù)


springAop和AspectJ的關(guān)系

Aop是一種概念

springAop、AspectJ都是Aop的實(shí)現(xiàn),SpringAop有自己的語(yǔ)法,但是語(yǔ)法復(fù)雜,所以SpringAop借助了AspectJ的注解,但是底層實(shí)現(xiàn)還是自己的

spring?AOP提供兩種編程風(fēng)格

@AspectJ support ? ? ? ? ------------>利用aspectj的注解

Schema-based AOP support ----------->xml aop:config 命名空間

證明:spring,通過(guò)源 ?碼分析了,我們可以知道spring底層使用的是JDK或者CGLIB來(lái)完成的代理,并且在官網(wǎng)上spring給出了aspectj的文檔,和springAOP是不同的



spring Aop的概念

aspect:一定要給spring去管理 ?抽象 ?aspectj->類

pointcut:切點(diǎn)表示連接點(diǎn)的集合 ?-------------------> ? ? ? ? ? 表

(我的理解:PointCutJoinPoint的謂語(yǔ),這是一個(gè)動(dòng)作,主要是告訴通知連接點(diǎn)在哪里,切點(diǎn)表達(dá)式?jīng)Q定?JoinPoint?的數(shù)量)

Joinpoint:連接點(diǎn) ? 目標(biāo)對(duì)象中的方法?----------------> ? ?記錄

(我的理解:JoinPoint是要關(guān)注和增強(qiáng)的方法,也就是我們要作用的點(diǎn))

Weaving?:把代理邏輯加入到目標(biāo)對(duì)象上的過(guò)程叫做織入

target?目標(biāo)對(duì)象 原始對(duì)象

aop?Proxy?代理對(duì)象 ?包含了原始對(duì)象的代碼和增加后的代碼的那個(gè)對(duì)象

advice:通知 ? ?(位置 +?logic)

advice通知類型:

Before?連接點(diǎn)執(zhí)行之前,但是無(wú)法阻止連接點(diǎn)的正常執(zhí)行,除非該段執(zhí)行拋出異常

After??連接點(diǎn)正常執(zhí)行之后,執(zhí)行過(guò)程中正常執(zhí)行返回退出,非異常退出

After?throwing??執(zhí)行拋出異常的時(shí)候

After?(finally) ?無(wú)論連接點(diǎn)是正常退出還是異常退出,都會(huì)執(zhí)行

Around?advice: 圍繞連接點(diǎn)執(zhí)行,例如方法調(diào)用。這是最有用的切面方式。around通知可以在方法調(diào)用之前和之后執(zhí)行自定義行為。它還負(fù)責(zé)選擇是繼續(xù)加入點(diǎn)還是通過(guò)返回自己的返回值或拋出異常來(lái)快速建議的方法執(zhí)行。

Proceedingjoinpoint?和JoinPoint的區(qū)別:

Proceedingjoinpoint?繼承了JoinPoint,proceed()這個(gè)是aop代理鏈執(zhí)行的方法。并擴(kuò)充實(shí)現(xiàn)了proceed()方法,用于繼續(xù)執(zhí)行連接點(diǎn)。JoinPoint僅能獲取相關(guān)參數(shù),無(wú)法執(zhí)行連接點(diǎn)。

JoinPoint的方法

1.java.lang.Object[]?getArgs():獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜恚?

2.Signature?getSignature() :獲取連接點(diǎn)的方法簽名對(duì)象;?

3.java.lang.Object?getTarget() :獲取連接點(diǎn)所在的目標(biāo)對(duì)象;?

4.java.lang.Object?getThis() :獲取代理對(duì)象本身;

proceed()有重載,有個(gè)帶參數(shù)的方法,可以修改目標(biāo)方法的的參數(shù)

Introductions

perthis

使用方式如下:

@Aspect("perthis(this(com.chenss.dao.IndexDaoImpl))")

要求:

1. AspectJ對(duì)象的注入類型為prototype

2. 目標(biāo)對(duì)象也必須是prototype的

原因?yàn)椋褐挥心繕?biāo)對(duì)象是原型模式的,每次getBean得到的對(duì)象才是不一樣的,由此針對(duì)每個(gè)對(duì)象就會(huì)產(chǎn)生新的切面對(duì)象,才能產(chǎn)生不同的切面結(jié)果。


springAop支持AspectJ

1、啟用@AspectJ支持

使用Java Configuration啟用@AspectJ支持

要使用Java @Configuration啟用@AspectJ支持,請(qǐng)?zhí)砑覢EnableAspectJAutoProxy注釋

@Configuration

@EnableAspectJAutoProxy

public class AppConfig {

}



使用XML配置啟用@AspectJ支持

要使用基于xml的配置啟用@AspectJ支持,可以使用aop:aspectj-autoproxy元素

<aop:aspectj-autoproxy/>


2、聲明一個(gè)Aspect

申明一個(gè)@Aspect注釋類,并且定義成一個(gè)bean交給Spring管理。

@Component

@Aspect

public class UserAspect {

}


3、申明一個(gè)pointCut

切入點(diǎn)表達(dá)式由@Pointcut注釋表示。切入點(diǎn)聲明由兩部分組成:一個(gè)簽名包含名稱和任何參數(shù),以及一個(gè)切入點(diǎn)表達(dá)式,該表達(dá)式確定我們對(duì)哪個(gè)方法執(zhí)行感興趣。

@Pointcut("execution(* transfer(..))")// 切入點(diǎn)表達(dá)式

private?void?anyOldTransfer() {}// 切入點(diǎn)簽名


切入點(diǎn)確定感興趣的?join points(連接點(diǎn),從而使我們能夠控制何時(shí)執(zhí)行通知。Spring AOP只支持Spring bean的方法執(zhí)行?join points(連接點(diǎn),所以您可以將切入點(diǎn)看作是匹配Spring bean上方法的執(zhí)行。

/**

* 申明Aspect,并且交給spring容器管理

*/

@Component

@Aspect

public class UserAspect {

/**

? ? * 申明切入點(diǎn),匹配UserDao所有方法調(diào)用

? ? * execution匹配方法執(zhí)行連接點(diǎn)

? ? * within:將匹配限制為特定類型中的連接點(diǎn)

? ? * args:參數(shù)

? ? * target:目標(biāo)對(duì)象

? ? * this:代理對(duì)象

? ? */

@Pointcut("execution(* com.yao.dao.UserDao.*(..))")

public void pintCut(){

System.out.println("point cut");

}

4、申明一個(gè)Advice通知

advice通知與pointcut切入點(diǎn)表達(dá)式相關(guān)聯(lián),并在切入點(diǎn)匹配的方法執(zhí)行@Before之前、@After之后或前后運(yùn)行。

/**

* 申明Aspect,并且交給spring容器管理

*/

@Component

@Aspect

public class UserAspect {

/**

? ? * 申明切入點(diǎn),匹配UserDao所有方法調(diào)用

? ? * execution匹配方法執(zhí)行連接點(diǎn)

? ? * within:將匹配限制為特定類型中的連接點(diǎn)

? ? * args:參數(shù)

? ? * target:目標(biāo)對(duì)象

? ? * this:代理對(duì)象

? ? */

@Pointcut("execution(* com.yao.dao.UserDao.*(..))")

public void pintCut(){

System.out.println("point cut");

}

/**

? ? * 申明before通知,在pintCut切入點(diǎn)前執(zhí)行

? ? * 通知與切入點(diǎn)表達(dá)式相關(guān)聯(lián),

? ? * 并在切入點(diǎn)匹配的方法執(zhí)行之前、之后或前后運(yùn)行。

? ? * 切入點(diǎn)表達(dá)式可以是對(duì)指定切入點(diǎn)的簡(jiǎn)單引用,也可以是在適當(dāng)位置聲明的切入點(diǎn)表達(dá)式。

? ? */

@Before("com.yao.aop.UserAspect.pintCut()")

public?void?beforeAdvice(){

System.out.println("before");

}

}


各種連接點(diǎn)joinPoint的意義:

1 .?execution

用于匹配方法執(zhí)行?join points連接點(diǎn),最小粒度方法,在aop中主要使用。

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

這里問(wèn)號(hào)表示當(dāng)前項(xiàng)可以有也可以沒(méi)有,其中各項(xiàng)的語(yǔ)義如下

modifiers-pattern:方法的可見(jiàn)性,如public,protected;

ret-type-pattern:方法的返回值類型,如int,void等;

declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;

name-pattern:方法名類型,如buisinessService();

param-pattern:方法的參數(shù)類型,如java.lang.String;

throws-pattern:方法拋出的異常類型,如java.lang.Exception;

example:

@Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和類的任意方法

@Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和類的public方法

@Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和類的public 無(wú)方法參數(shù)的方法

@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的任意接口和類的第一個(gè)參數(shù)為String類型的方法

@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和類的只有一個(gè)參數(shù),且參數(shù)為String類型的方法

@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和類的只有一個(gè)參數(shù),且參數(shù)為String類型的方法

@Pointcut("execution(public * *(..))")//匹配任意的public方法

@Pointcut("execution(* te*(..))")//匹配任意的以te開(kāi)頭的方法

@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任意的方法

@Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法

關(guān)于這個(gè)表達(dá)式的詳細(xì)寫(xiě)法,可以腦補(bǔ)也可以參考官網(wǎng)很容易的,可以作為一個(gè)看spring官網(wǎng)文檔的入門(mén),打破你害怕看官方文檔的心理,其實(shí)你會(huì)發(fā)覺(jué)官方文檔也是很容易的

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples


由于Spring切面粒度最小是達(dá)到方法級(jí)別,而execution表達(dá)式可以用于明確指定方法返回類型,類名,方法名和參數(shù)名等與方法相關(guān)的信息,并且在Spring中,大部分需要使用AOP的業(yè)務(wù)場(chǎng)景也只需要達(dá)到方法級(jí)別即可,因而execution表達(dá)式的使用是最為廣泛的

2 .?within

?表達(dá)式的最小粒度為類

// ------------

// within與execution相比,粒度更大,僅能實(shí)現(xiàn)到包和接口、類級(jí)別。而execution可以精確到方法的返回值,參數(shù)個(gè)數(shù)、修飾符、參數(shù)類型等

@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法

@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法


3?.?args

?args表達(dá)式的作用是匹配指定參數(shù)類型和指定參數(shù)數(shù)量的方法,與包名和類名無(wú)關(guān)

/**

* args同execution不同的地方在于:

* args匹配的是運(yùn)行時(shí)傳遞給方法的參數(shù)類型

* execution(* *(java.io.Serializable))匹配的是方法在聲明時(shí)指定的方法參數(shù)類型。

*/

@Pointcut("args(java.io.Serializable)")//匹配運(yùn)行時(shí)傳遞的參數(shù)類型為指定類型的、且參數(shù)個(gè)數(shù)和順序匹配

@Pointcut("@args(com.chenss.anno.Chenss)")//接受一個(gè)參數(shù),并且傳遞的參數(shù)的運(yùn)行時(shí)類型具有@Classified


4 . this JDK代理時(shí),指向接口和代理類proxy,cglib代理時(shí) 指向接口和子類(不使用proxy)

5?.?target ?指向接口和子類

/**

* 此處需要注意的是,如果配置設(shè)置proxyTargetClass=false,或默認(rèn)為false,則是用JDK代理,否則使用的是CGLIB代理

* JDK代理的實(shí)現(xiàn)方式是基于接口實(shí)現(xiàn),代理類繼承Proxy,實(shí)現(xiàn)接口。

* 而CGLIB繼承被代理的類來(lái)實(shí)現(xiàn)。

* 所以使用target會(huì)保證目標(biāo)不變,關(guān)聯(lián)對(duì)象不會(huì)受到這個(gè)設(shè)置的影響。

* 但是使用this對(duì)象時(shí),會(huì)根據(jù)該選項(xiàng)的設(shè)置,判斷是否能找到對(duì)象。

*/

@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目標(biāo)對(duì)象,也就是被代理的對(duì)象。限制目標(biāo)對(duì)象為com.chenss.dao.IndexDaoImpl類

@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//當(dāng)前對(duì)象,也就是代理對(duì)象,代理對(duì)象時(shí)通過(guò)代理目標(biāo)對(duì)象的方式獲取新的對(duì)象,與原值并非一個(gè)

@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目標(biāo)對(duì)象中的任意方法

@Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ


這個(gè)比較難.......

proxy模式里面有兩個(gè)重要的術(shù)語(yǔ)

proxy?Class

target Class

CGLIB和JDK有區(qū)別 ? ?JDK是基于接口 ? cglib是基于繼承所有this可以在cglib作用


6 .?@annotation

這個(gè)很簡(jiǎn)單........

作用方法級(jí)別

上述所有表達(dá)式都有@ 比如@Target(里面是一個(gè)注解類xx,表示所有加了xx注解的類,和包名無(wú)關(guān))

注意:上述所有的表達(dá)式可以混合使用,|| && !

@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配帶有com.chenss.anno.Chenss注解的方法


7 .?bean

@Pointcut("bean(dao1)")//名稱為dao1的bean上的任意方法

@Pointcut("bean(dao*)")


Spring AOP XML實(shí)現(xiàn)方式的注意事項(xiàng):

  1. 在aop:config中定義切面邏輯,允許重復(fù)出現(xiàn),重復(fù)多次,以最后出現(xiàn)的邏輯為準(zhǔn),但是次數(shù)以出現(xiàn)的次數(shù)為準(zhǔn)

  2. aop:aspect ID重復(fù)不影響正常運(yùn)行,依然能夠有正確結(jié)果

  3. aop:pointcut ID重復(fù)會(huì)出現(xiàn)覆蓋,以最后出現(xiàn)的為準(zhǔn)。不同aop:aspect內(nèi)出現(xiàn)的pointcut配置,可以相互引用

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 定義開(kāi)始進(jìn)行注解掃描 -->

<context:component-scan base-package="com.chenss"></context:component-scan>

<!-- 定義AspectJ對(duì)象使用的邏輯類,類中提供切面之后執(zhí)行的邏輯方法 -->

<bean id="aspectAop" class="com.chenss.aspectj.Aspect"></bean>

<bean id="aspectAop2" class="com.chenss.aspectj.Aspect2"></bean>

<bean id="indexDao" class="com.chenss.entity.IndexDao"></bean>

<!--在Config中定義切面邏輯,允許重復(fù)出現(xiàn),重復(fù)多次,以最后出現(xiàn)的邏輯為準(zhǔn),但是次數(shù)以出現(xiàn)的次數(shù)為準(zhǔn)-->

<aop:config>

<!-- aop:aspect ID重復(fù)不影響正常運(yùn)行,依然能夠有正確結(jié)果 -->

<!-- aop:pointcut ID重復(fù)會(huì)出現(xiàn)覆蓋,以最后出現(xiàn)的為準(zhǔn)。不同aop:aspect內(nèi)出現(xiàn)的pointcut配置,可以相互引用 -->

<aop:aspect id="aspect" ref="aspectAop">

<aop:pointcut id="aspectCut" expression="execution(* com.chenss.entity.*.*())"/>

<aop:before method="before" pointcut-ref="aspectCut"></aop:before>

fffffff

<aop:pointcut id="aspectNameCut" expression="execution(* com.chenss.entity.*.*(java.lang.String, ..))"/>

<aop:before method="before2" pointcut-ref="aspectNameCut"></aop:before>

</aop:aspect>

</aop:config>

</beans>


spring AOP的源碼分析

cglib



cglib封裝了ASM這個(gè)開(kāi)源框架,對(duì)字節(jié)碼操作,完成對(duì)代理類的創(chuàng)建

主要通過(guò)集成目標(biāo)對(duì)象,然后完成重寫(xiě),再操作字節(jié)碼

具體看參考ASM的語(yǔ)法




JDK


在Proxy這個(gè)類當(dāng)中首先實(shí)例化一個(gè)對(duì)象ProxyClassFactory,然后在get方法中調(diào)用了apply方法,完成對(duì)代理類的創(chuàng)建


其中最重要的兩個(gè)方法

generateProxyClass通過(guò)反射收集字段和屬性然后生成字節(jié)

defineClass0 jvm內(nèi)部完成對(duì)上述字節(jié)的load



總結(jié):cglib是通過(guò)繼承來(lái)操作子類的字節(jié)碼生成代理類,JDK是通過(guò)接口,然后利用java反射完成對(duì)類的動(dòng)態(tài)創(chuàng)建,嚴(yán)格意義上來(lái)說(shuō)cglib的效率高于JDK的反射,但是這種效率取決于代碼功力,其實(shí)可以忽略不計(jì),畢竟JDK是JVM的親兒子........



spring5新特性

1 使用 lambda表達(dá)式定義bean


2 日志 spring4的日志是用jcl,原生的JCL,底層通過(guò)循環(huán)去加載具·體的日志實(shí)現(xiàn)技術(shù),所以有先后順序,spring5利用的是spring-jcl,其實(shí)就是spring自己改了JCL的代碼具體參考視頻當(dāng)中講的兩者的區(qū)別


新特性還有其他,但是這兩個(gè)比較重要,由于時(shí)間問(wèn)題,其他的特性可以去網(wǎng)上找到相應(yīng)資料,但是這兩個(gè)應(yīng)付面試絕對(duì)可以了,其他的特性噱頭居多,實(shí)用性可能不是很大。


如果想要深入的了解學(xué)習(xí)spring源碼,建議大家可以看這個(gè)視頻集
主要講解了spring ioc,spring aop,spring boot底層的源碼解析




一篇文章搞懂spring aop源碼的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
鸡泽县| 阜新市| 金寨县| 大同县| 都昌县| 平原县| 丹寨县| 工布江达县| 秦安县| 永顺县| 富裕县| 荃湾区| 张家界市| 陈巴尔虎旗| 东丽区| 高青县| 黎城县| 常州市| 石渠县| 同江市| 巢湖市| 舟山市| 南昌县| 公主岭市| 昭平县| 米易县| 富阳市| 塘沽区| 甘谷县| 界首市| 应用必备| 哈巴河县| 芜湖县| 贵定县| 松江区| 随州市| 安多县| 耿马| 根河市| 福清市| 石门县|