AOP各种表达式,aspectj的关系
Aop是什么
與OOP對比,面向切面,傳統(tǒng)的OOP開發(fā)中的代碼邏輯是自上而下的,而這些過程會產(chǎn)生一些橫切性問題,這些橫切性的問題和我們的主業(yè)務(wù)邏輯關(guān)系不大,這些橫切性問題不會影響到主邏輯實(shí)現(xiàn)的,但是會散落到代碼的各個部分,難以維護(hù)。AOP是處理一些橫切性問題,AOP的編程思想就是把這些問題和主業(yè)務(wù)邏輯分開,達(dá)到與主業(yè)務(wù)邏輯解耦的目的。使代碼的重用性和開發(fā)效率更高。
aop的應(yīng)用場景
1.?日志記錄
2.?權(quán)限驗證
3.?效率檢查
4.?事務(wù)管理
5.?exception
springAop的底層技術(shù)
| ? | JDK動態(tài)代理 | CGLIB代理 ? |
| 編譯時期的織入還是運(yùn)行時期的織入? | 運(yùn)行時期織入 | 運(yùn)行時期織入 |
| 初始化時期織入還是獲取對象時期織入? | 初始化時期織入 | 初始化時期織入 |
springAop和AspectJ的關(guān)系
Aop是一種概念
springAop、AspectJ都是Aop的實(shí)現(xiàn),SpringAop有自己的語法,但是語法復(fù)雜,所以SpringAop借助了AspectJ的注解,但是底層實(shí)現(xiàn)還是自己的
spring AOP提供兩種編程風(fēng)格 @AspectJ support ------------>利用aspectj的注解 Schema-based AOP support ----------->xml aop:config 命名空間證明:spring,通過源 碼分析了,我們可以知道spring底層使用的是JDK或者CGLIB來完成的代理,并且在官網(wǎng) 上spring給出了aspectj的文檔,和springAOP是不同的spring Aop的概念
aspect:一定要給spring去管理 抽象 aspectj->類
pointcut:切點(diǎn)表示連接點(diǎn)的集合 -------------------> 表
(我的理解:PointCut是JoinPoint的謂語,這是一個動作,主要是告訴通知連接點(diǎn)在哪里,切點(diǎn)表達(dá)式?jīng)Q定 JoinPoint 的數(shù)量)
Joinpoint:連接點(diǎn) 目標(biāo)對象中的方法 ----------------> 記錄
(我的理解:JoinPoint是要關(guān)注和增強(qiáng)的方法,也就是我們要作用的點(diǎn))
Weaving :把代理邏輯加入到目標(biāo)對象上的過程叫做織入
target 目標(biāo)對象 原始對象
aop Proxy 代理對象 包含了原始對象的代碼和增加后的代碼的那個對象
advice:通知 (位置 + logic)
?
?
advice通知類型:
Before 連接點(diǎn)執(zhí)行之前,但是無法阻止連接點(diǎn)的正常執(zhí)行,除非該段執(zhí)行拋出異常
After 連接點(diǎn)正常執(zhí)行之后,執(zhí)行過程中正常執(zhí)行返回退出,非異常退出
After throwing 執(zhí)行拋出異常的時候
After (finally) 無論連接點(diǎn)是正常退出還是異常退出,都會執(zhí)行
Around advice: 圍繞連接點(diǎn)執(zhí)行,例如方法調(diào)用。這是最有用的切面方式。around通知可以在方法調(diào)用之前和之后執(zhí)行自定義行為。它還負(fù)責(zé)選擇是繼續(xù)加入點(diǎn)還是通過返回自己的返回值或拋出異常來快速建議的方法執(zhí)行。
?
?
Proceedingjoinpoint?和JoinPoint的區(qū)別:
Proceedingjoinpoint?繼承了JoinPoint,proceed()這個是aop代理鏈執(zhí)行的方法。并擴(kuò)充實(shí)現(xiàn)了proceed()方法,用于繼續(xù)執(zhí)行連接點(diǎn)。JoinPoint僅能獲取相關(guān)參數(shù),無法執(zhí)行連接點(diǎn)。
JoinPoint的方法
1.java.lang.Object[] getArgs():獲取連接點(diǎn)方法運(yùn)行時的入?yún)⒘斜?#xff1b;?
2.Signature getSignature() :獲取連接點(diǎn)的方法簽名對象;?
3.java.lang.Object getTarget() :獲取連接點(diǎn)所在的目標(biāo)對象;?
4.java.lang.Object getThis() :獲取代理對象本身;
proceed()有重載,有個帶參數(shù)的方法,可以修改目標(biāo)方法的的參數(shù)
?
Introductions
perthis
使用方式如下:
@Aspect("perthis(this(com.chenss.dao.IndexDaoImpl))")
要求:
1. AspectJ對象的注入類型為prototype
2. 目標(biāo)對象也必須是prototype的
原因為:只有目標(biāo)對象是原型模式的,每次getBean得到的對象才是不一樣的,由此針對每個對象就會產(chǎn)生新的切面對象,才能產(chǎn)生不同的切面結(jié)果。
?
?Aspect Instantiation Models
By default, there is a single instance of each aspect within the application context. AspectJ calls this the singleton instantiation model. It is possible to define aspects with alternate lifecycles. Spring supports AspectJ’s?perthis?and?pertarget?instantiation models (?percflow, percflowbelow,?and?pertypewithin?are not currently supported).
You can declare a?perthis?aspect by specifying a?perthis?clause in the?@Aspect?annotation. Consider the following example:
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") public class MyAspect {private int someState;@Before(com.xyz.myapp.SystemArchitecture.businessService())public void recordServiceUsage() {// ...}}springAop支持AspectJ
1、啟用@AspectJ支持
使用Java Configuration啟用@AspectJ支持
要使用Java @Configuration啟用@AspectJ支持,請?zhí)砑?#64;EnableAspectJAutoProxy注釋
@Configuration @EnableAspectJAutoProxy public class AppConfig {}使用XML配置啟用@AspectJ支持
要使用基于xml的配置啟用@AspectJ支持,可以使用aop:aspectj-autoproxy元素
<aop:aspectj-autoproxy/>2、聲明一個Aspect
申明一個@Aspect注釋類,并且定義成一個bean交給Spring管理。
@Component @Aspect public class UserAspect { }3、申明一個pointCut
切入點(diǎn)表達(dá)式由@Pointcut注釋表示。切入點(diǎn)聲明由兩部分組成:一個簽名包含名稱和任何參數(shù),以及一個切入點(diǎn)表達(dá)式,該表達(dá)式確定我們對哪個方法執(zhí)行感興趣。
@Pointcut("execution(* transfer(..))")// 切入點(diǎn)表達(dá)式 private void anyOldTransfer() {}// 切入點(diǎn)簽名切入點(diǎn)確定感興趣的?join points(連接點(diǎn)),從而使我們能夠控制何時執(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)對象* this:代理對象*/@Pointcut("execution(* com.yao.dao.UserDao.*(..))")public void pintCut(){System.out.println("point cut");}4、申明一個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)對象* this:代理對象*/@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á)式可以是對指定切入點(diǎn)的簡單引用,也可以是在適當(dāng)位置聲明的切入點(diǎn)表達(dá)式。*/@Before("com.yao.aop.UserAspect.pintCut()")public void beforeAdvice(){System.out.println("before");} }各種連接點(diǎn)joinPoint的意義:
execution
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param- pattern) throws-pattern?) 這里問號表示當(dāng)前項可以有也可以沒有,其中各項的語義如下 modifiers-pattern:方法的可見性,如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 無方法參數(shù)的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的 任意接口和類的第一個參數(shù)為String類型的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意 接口和類的只有一個參數(shù),且參數(shù)為String類型的方法 @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意 接口和類的只有一個參數(shù),且參數(shù)為String類型的方法 @Pointcut("execution(public * *(..))")//匹配任意的public方法 @Pointcut("execution(* te*(..))")//匹配任意的以te開頭的方法 @Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任 意的方法 @Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法關(guān)于這個表達(dá)式的詳細(xì)寫法,可以腦補(bǔ)也可以參考官網(wǎng)很容易的,可以作為一個看spring官網(wǎng)文檔的入門,打破你 害怕看官方文檔的心理,其實(shí)你會發(fā)覺官方文檔也是很容易的 https://docs.spring.io/spring-framework/docs/current/spring-framework- reference/core.html#aop-pointcuts-examples由于Spring切面粒度最小是達(dá)到方法級別,而execution表達(dá)式可以用于明確指定方法返回類型,類名,方法名和參數(shù)名等與方法相關(guān)的信息,并且在Spring中,大部分需要使用AOP的業(yè)務(wù)場景也只需要達(dá)到方法級別即可,因而execution表達(dá)式的使用是最為廣泛的
within
表達(dá)式的最小粒度為類 // ------------ // within與execution相比,粒度更大,僅能實(shí)現(xiàn)到包和接口、類級別。而execution可以精確到方法的返回 值,參數(shù)個數(shù)、修飾符、參數(shù)類型等 @Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法 @Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法args
args表達(dá)式的作用是匹配指定參數(shù)類型和指定參數(shù)數(shù)量的方法,與包名和類名無關(guān)/*** args同execution不同的地方在于:* args匹配的是運(yùn)行時傳遞給方法的參數(shù)類型* execution(* *(java.io.Serializable))匹配的是方法在聲明時指定的方法參數(shù)類型。*/ @Pointcut("args(java.io.Serializable)")//匹配運(yùn)行時傳遞的參數(shù)類型為指定類型的、且參數(shù)個數(shù)和順序匹配 @Pointcut("@args(com.chenss.anno.Chenss)")//接受一個參數(shù),并且傳遞的參數(shù)的運(yùn)行時類型具有@Classifiedthis
JDK代理時,指向接口和代理類proxy,cglib代理時 指向接口和子類(不使用proxy)
target
指向接口和子類
/*** 此處需要注意的是,如果配置設(shè)置proxyTargetClass=false,或默認(rèn)為false,則是用JDK代理,否則使用 的是CGLIB代理* JDK代理的實(shí)現(xiàn)方式是基于接口實(shí)現(xiàn),代理類繼承Proxy,實(shí)現(xiàn)接口。* 而CGLIB繼承被代理的類來實(shí)現(xiàn)。* 所以使用target會保證目標(biāo)不變,關(guān)聯(lián)對象不會受到這個設(shè)置的影響。* 但是使用this對象時,會根據(jù)該選項的設(shè)置,判斷是否能找到對象。*/ @Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目標(biāo)對象,也就是被代理的對象。限制目標(biāo)對象為 com.chenss.dao.IndexDaoImpl類 @Pointcut("this(com.chenss.dao.IndexDaoImpl)")//當(dāng)前對象,也就是代理對象,代理對象時通過代理目 標(biāo)對象的方式獲取新的對象,與原值并非一個 @Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目標(biāo)對象中的任意方法 @Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ 這個比較難....... proxy模式里面有兩個重要的術(shù)語 proxy Class target Class CGLIB和JDK有區(qū)別 JDK是基于接口 cglib是基于繼承所有this可以在cglib作用@annotation
這個很簡單........ 作用方法級別上述所有表達(dá)式都有@ 比如@Target(里面是一個注解類xx,表示所有加了xx注解的類,和包名無關(guān))注意:上述所有的表達(dá)式可以混合使用,|| && !@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配帶有com.chenss.anno.Chenss注解的方法bean
@Pointcut("bean(dao1)")//名稱為dao1的bean上的任意方法 @Pointcut("bean(dao*)")?
Declaring Advice
Before Advice
You can declare before advice in an aspect by using the?@Before?annotation:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;@Aspect public class BeforeExample {@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doAccessCheck() {// ...}}After Returning Advice
After returning advice runs when a matched method execution returns normally. You can declare it by using the?@AfterReturning?annotation:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning;@Aspect public class AfterReturningExample {@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doAccessCheck() {// ...}}After Throwing Advice
After throwing advice runs when a matched method execution exits by throwing an exception. You can declare it by using the?@AfterThrowing?annotation, as the following example shows:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing;@Aspect public class AfterThrowingExample {@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doRecoveryActions() {// ...}}After (Finally) Advice
After (finally) advice runs when a matched method execution exits. It is declared by using the?@After?annotation. After advice must be prepared to handle both normal and exception return conditions. It is typically used for releasing resources and similar purposes. The following example shows how to use after finally advice:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.After;@Aspect public class AfterFinallyExample {@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doReleaseLock() {// ...}}Around Advice
The following example shows how to use around advice:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.ProceedingJoinPoint;@Aspect public class AroundExample {@Around("com.xyz.myapp.SystemArchitecture.businessService()")public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {// start stopwatchObject retVal = pjp.proceed();// stop stopwatchreturn retVal;}}Schema-based AOP Support
If you prefer an XML-based format, Spring also offers support for defining aspects using the new?aop?namespace tags. The exact same pointcut expressions and advice kinds as when using the @AspectJ style are supported. Hence, in this section we focus on the new syntax and refer the reader to the discussion in the previous section (@AspectJ support) for an understanding of writing pointcut expressions and the binding of advice parameters.
Declaring an Aspect
When you use the schema support, an aspect is a regular Java object defined as a bean in your Spring application context. The state and behavior are captured in the fields and methods of the object, and the pointcut and advice information are captured in the XML.
You can declare an aspect by using the <aop:aspect> element, and reference the backing bean by using the?ref?attribute, as the following example shows:
<aop:config><aop:aspect id="myAspect" ref="aBean">...</aop:aspect> </aop:config><bean id="aBean" class="...">... </bean>The bean that backs the aspect (aBean?in this case) can of course be configured and dependency injected just like any other Spring bean.
Declaring a Pointcut
You can declare a named pointcut inside an?<aop:config>?element, letting the pointcut definition be shared across several aspects and advisors.
A pointcut that represents the execution of any business service in the service layer can be defined as follows:
<aop:config><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/></aop:config>In much the same way as an @AspectJ aspect, pointcuts declared by using the schema based definition style can collect join point context. For example, the following pointcut collects the?this?object as the join point context and passes it to the advice:
<aop:config><aop:aspect id="myAspect" ref="aBean"><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/><aop:before pointcut-ref="businessService" method="monitor"/>...</aop:aspect></aop:config>?Introductions
<aop:aspect id="usageTrackerAspect" ref="usageTracking"><aop:declare-parentstypes-matching="com.xzy.myapp.service.*+"implement-interface="com.xyz.myapp.service.tracking.UsageTracked"default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/><aop:beforepointcut="com.xyz.myapp.SystemArchitecture.businessService()and this(usageTracked)"method="recordUsage"/></aop:aspect>?Spring AOP or Full AspectJ?
Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ, as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, Spring AOP is the right choice. If you need to advise objects not managed by the Spring container (such as domain objects, typically), you need to use AspectJ. You also need to use AspectJ if you wish to advise join points other than simple method executions (for example, field get or set join points and so on).
When you use AspectJ, you have the choice of the AspectJ language syntax (also known as the “code style”) or the @AspectJ annotation style. Clearly, if you do not use Java 5+, the choice has been made for you: Use the code style. If aspects play a large role in your design, and you are able to use the?AspectJ Development Tools (AJDT)?plugin for Eclipse, the AspectJ language syntax is the preferred option. It is cleaner and simpler because the language was purposefully designed for writing aspects. If you do not use Eclipse or have only a few aspects that do not play a major role in your application, you may want to consider using the @AspectJ style, sticking with regular Java compilation in your IDE, and adding an aspect weaving phase to your build script.
@AspectJ or XML for Spring AOP?
If you have chosen to use Spring AOP, you have a choice of @AspectJ or XML style. There are various tradeoffs to consider.
The XML style may most familiar to existing Spring users, and it is backed by genuine POJOs. When using AOP as a tool to configure enterprise services, XML can be a good choice (a good test is whether you consider the pointcut expression to be a part of your configuration that you might want to change independently). With the XML style, it is arguably clearer from your configuration which aspects are present in the system.
The XML style has two disadvantages. First, it does not fully encapsulate the implementation of the requirement it addresses in a single place. The DRY principle says that there should be a single, unambiguous, authoritative representation of any piece of knowledge within a system. When using the XML style, the knowledge of how a requirement is implemented is split across the declaration of the backing bean class and the XML in the configuration file. When you use the @AspectJ style, this information is encapsulated in a single module: the aspect. Secondly, the XML style is slightly more limited in what it can express than the @AspectJ style: Only the “singleton” aspect instantiation model is supported, and it is not possible to combine named pointcuts declared in XML. For example, in the @AspectJ style you can write something like the following:
?
總結(jié)
以上是生活随笔為你收集整理的AOP各种表达式,aspectj的关系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模拟spring IOC、源码分析
- 下一篇: 动态代理源码分析,实现自己的动态代理