1. 程式人生 > 實用技巧 >AOP之proceedingjoinpoint和joinpoint區別

AOP之proceedingjoinpoint和joinpoint區別

現在AOP的場景越來越多,所以我們有必要理解下和AOP相關的一些概念和機制。
基礎知識和原理類大家搜尋spring aop/aspectj,有大量現成的可以參考,基本上只要理解了jdk動態代理、cglib位元組碼動態生成代理就足夠了,
而且必須知道這個代理類是spring託管的(如果是自己建立的代理類,是無法被攔截的,此時只能使用過濾器/攔截器機制,他們本身是鏈式的,跟代理無關)。
import org.aspectj.lang.reflect.SourceLocation;  
public interface JoinPoint {  
   String toString();         
//連線點所在位置的相關資訊 String toShortString(); //連線點所在位置的簡短相關資訊 String toLongString(); //連線點所在位置的全部相關資訊 Object getThis(); //返回AOP代理物件,也就是com.sun.proxy.$Proxy18 Object getTarget(); //返回目標物件,一般我們都需要它或者(也就是定義方法的介面或類,為什麼會是介面呢?這主要是在目標物件本身是動態代理的情況下,例如Mapper。所以返回的是定義方法的物件如aoptest.daoimpl.GoodDaoImpl或com.b.base.BaseMapper<T, E, PK>)
Object[] getArgs(); //返回被通知方法引數列表 Signature getSignature(); //返回當前連線點簽名 其getName()方法返回方法的FQN,如void aoptest.dao.GoodDao.delete()或com.b.base.BaseMapper.insert(T)(需要注意的是,很多時候我們定義了子類繼承父類的時候,我們希望拿到基於子類的FQN,這直接可拿不到,要依賴於AopUtils.getTargetClass(point.getTarget())獲取原始代理物件,下面會詳細講解) SourceLocation getSourceLocation();//
返回連線點方法所在類檔案中的位置 String getKind(); //連線點型別 StaticPart getStaticPart(); //返回連線點靜態部分 } public interface ProceedingJoinPoint extends JoinPoint { public Object proceed() throws Throwable; public Object proceed(Object[] args) throws Throwable; }

JoinPoint.StaticPart:提供訪問連線點的靜態部分,如被通知方法簽名、連線點型別等:

public interface StaticPart {  
   Signature getSignature();    //返回當前連線點簽名  
   String getKind();          //連線點型別  
   int getId();               //唯一標識  
   String toString();         //連線點所在位置的相關資訊  
   String toShortString();     //連線點所在位置的簡短相關資訊  
   String toLongString();     //連線點所在位置的全部相關資訊  
}

環繞通知 ProceedingJoinPoint執行proceed方法的作用是讓目標方法執行,這也是環繞通知和前置、後置通知方法的一個最大區別。

Proceedingjoinpoint 繼承了 JoinPoint。是在JoinPoint的基礎上暴露出 proceed 這個方法。proceed很重要,這個是aop代理鏈執行的方法。

  暴露出這個方法,就能支援 aop:around 這種切面(而其他的幾種切面只需要用到JoinPoint,這跟切面型別有關), 能決定是否走代理鏈還是走自己攔截的其他邏輯。建議看一下 JdkDynamicAopProxy的invoke方法,瞭解一下代理鏈的執行原理。 典型的用法如下:
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
// AopUtils.getTargetClass(point.getTarget())獲取原始物件,例如對於Mapper而言,它獲取的是具體代理的Mapper如com.b.mapper.DefaultDsMapper(如果前者繼承了後者的話)而不是定義該方法的Mapper如com.b.base.BaseMapper<Info, InfoExample, InfoKey>,如下圖 Type[] types = AopUtils.getTargetClass(point.getTarget()).getGenericInterfaces(); // getGenericInterfaces方法能夠獲取類/介面實現的所有介面 Annotation nologgingAnno = ((Class)types[0]).getAnnotation(Nologging.class); // type是所有型別的父介面 MethodSignature methodSignature = (MethodSignature)signature; Method targetMethod = methodSignature.getMethod();

現在來補充下Java中Type介面與Class類的區別聯絡。

package java.lang.reflect;

/**
 * Type is the common superinterface for all types in the Java
 * programming language. These include raw types, parameterized types,
 * array types, type variables and primitive types.
 *
 * @since 1.5
 */
public interface Type {
    /**
     * Returns a string describing this type, including information
     * about any type parameters.
     *
     * @implSpec The default implementation calls {@code toString}.
     *
     * @return a string describing this type
     * @since 1.8
     */
    default String getTypeName() {
        return toString();
    }
}

其主要的子類包括:

總結來說:

  • Type是一個介面。
  • Type是Java中所有型別的父介面,有一些子類,如上所示。
  • Type包括:raw type(原始型別,對應Class),parameterized types(引數化型別), array types(陣列型別), type variables(型別變數) and primitive types(基本型別,對應Class).
  • Type是JDK1.5引入的,主要是為了泛型。

Type介面與Class類的區別聯絡

  • Type是Class的父介面。
  • Class是Type的子類。

  提示:因為AOP是基於動態代理生成,如果想要仔細研究生成的代理類長什麼樣,可以設定系統引數-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,這樣就會儲存所有自動生成的代理類(注:生產環境嚴禁使用)。

參考:https://blog.csdn.net/cxh5060/article/details/45151863(攔截自定義註解,需要注意的是,標準的AOP表示式@annotation(com.xxx.yyy.annotation.CustomAnnotation)只能攔截實現類方法上的註解,無法攔截介面上的註解,如有需根據介面方法上的註解攔截的需求,需使用spring bean生命週期的BeanPostProcessor動態生成代理,而不是採用簡單的AOP實現)

https://www.cnblogs.com/akaneblog/p/6720513.html(jdk動態代理手工編寫,一般框架使用,比如spring aop、mybatis中logger也使用了動態代理)

https://www.cnblogs.com/haiq/p/4304615.html、https://blog.csdn.net/xlgen157387/article/details/82497594(cglib vs jdk動態代理效能參考)

https://blog.csdn.net/u010061691/article/details/50857798(進一步加了解釋,實際上InvocationHandler是要被明確呼叫的,只不過在AOP中通常被框架呼叫了,如果是應用自己編寫的話,則需要程式碼中通過InvocationHandler.getProxy,然後強轉、再呼叫。https://dzone.com/articles/java-dynamic-proxies)

https://dzone.com/articles/cglib-missing-manual(cglib手冊)

spring aop支援(https://www.cnblogs.com/V1haoge/p/9560803.html