spring AOP之proceedingjoinpoint和joinpoint區別(獲取各對象備忘)、動態代理機制及獲取原理代理對象、獲取Mybatis Mapper接口原始對象


現在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實現

  在spring aop中,advisor其實就是切入點+行為,advise是切入點,見https://www.jianshu.com/p/2250b24a3f7d

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

spring aop/aspectj修飾的bean的代理創建過程:https://segmentfault.com/a/1190000018281577


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM