Joinpoint繼承體系-筆記


Joinpoint繼承層次圖:

由上圖可以知道的所有的接口的實現都在ReflectiveMethodInvocation這個類中。ConstructorInvocation接口只有一個方法,這個方法的實現好像是由ReflectMethodInvocation的getStaticPart()方法來實現的,具體請看源碼。

現在把上面的接口和類的源碼的看看,順便做下筆記。

一、Joinpoint

這個接口表示一個一般的運行時連接點。一個運行時連接點是一個事件,這個事件發生在一個靜態連接點(也就是在程序中的某一個位置)。例如:

 

接口的方法:

    /**
     * Proceed to the next interceptor in the chain.
     * <p>The implementation and the semantics of this method depends
     * on the actual joinpoint type (see the children interfaces).
     * @return see the children interfaces' proceed definition
     * @throws Throwable if the joinpoint throws an exception
     */
    Object proceed() throws Throwable;

開始攔截器鏈條中的下一個的攔截器。

    /**
     * Return the object that holds the current joinpoint's static part.
     * <p>For instance, the target object for an invocation.
     * @return the object (can be null if the accessible object is static)
     */
    Object getThis();

返回目標對象。

    /**
     * Return the static part of this joinpoint.
     * <p>The static part is an accessible object on which a chain of
     * interceptors are installed.
     */
    AccessibleObject getStaticPart();

返回連接點的靜態部分,其實應該就是目標方法吧。(返回連接點的靜態部分,靜態部分就是的已經安裝在攔截器鏈條上的可以訪問的方法對象。)

二、Invocation

這個接口表示程序當中的一個調用。一個調用就是一個連接點而且可以被一個攔截器攔截。

這個接口只有一個方法:

/**
 * This interface represents an invocation in the program.
 *
 * <p>An invocation is a joinpoint and can be intercepted by an
 * interceptor.
 *
 * @author Rod Johnson
 */
public interface Invocation extends Joinpoint {

    /**
     * Get the arguments as an array object.
     * It is possible to change element values within this
     * array to change the arguments.
     * @return the argument of the invocation
     */
    Object[] getArguments();

}

方法返回的是目標方法的參數。如果目標方法是無參方法,那么會返回一個Object[] 類型的長度為0的數組。

三、MethodInvocation

 

/**
 * Description of an invocation to a method, given to an interceptor
 * upon method-call.
 *
 * <p>A method invocation is a joinpoint and can be intercepted by a
 * method interceptor.
 *
 * @author Rod Johnson
 * @see MethodInterceptor
 */
public interface MethodInvocation extends Invocation {

    /**
     * Get the method being called.
     * <p>This method is a frienly implementation of the
     * {@link Joinpoint#getStaticPart()} method (same result).
     * @return the method being called
     *///返回一個已經被調用的方法,其實是目標方法。
    Method getMethod();

}

getMethod()方法返回的是目標方法對應的Method對象。

四、ProxyMethodInvocation

這個接口擴展了MethodInvocation接口。

接口的方法:

    /**
     * Return the proxy that this method invocation was made through.
     * @return the original proxy object
     */
    Object getProxy();

返回原始的對象,也就是目標對象。

 

    /**
     * Create a clone of this object. If cloning is done before {@code proceed()}
     * is invoked on this object, {@code proceed()} can be invoked once per clone
     * to invoke the joinpoint (and the rest of the advice chain) more than once.
     * @return an invocable clone of this invocation.
     * {@code proceed()} can be called once per clone.
     */
    MethodInvocation invocableClone();

淺復制

    /**
     * Set the arguments to be used on subsequent invocations in the any advice
     * in this chain.
     * @param arguments the argument array
     */
    void setArguments(Object... arguments);

設置目標方法的參數值。

    /**
     * Add the specified user attribute with the given value to this invocation.
     * <p>Such attributes are not used within the AOP framework itself. They are
     * just kept as part of the invocation object, for use in special interceptors.
     * @param key the name of the attribute
     * @param value the value of the attribute, or {@code null} to reset it
     */
    void setUserAttribute(String key, Object value);

設置用戶屬性

 五、ReflectiveMethodInvocation

使用反射類調用目標對象。子類可以覆蓋invokeJoinpoint()方法。可以通過使用invocableClone()方法淺克隆一個調用(an invocation)來重復調用proceed()方法。而且還可以使用setUserAttribute(...)方法添加上自定義的屬性到這個調用上。

這個類的屬性:

    protected final Object proxy;//代理對象。 protected final Object target;//目標對象。 protected final Method method;//目標方法對應的Method對象。 protected Object[] arguments;//目標方法參數。 private final Class<?> targetClass;//目標對象的類型 /**
     * Lazily initialized map of user-specific attributes for this invocation.
     */
    private Map<String, Object> userAttributes;

    /**
     * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
     * that need dynamic checks.
     */
    protected final List<?> interceptorsAndDynamicMethodMatchers;

    /**
     * Index from 0 of the current interceptor we're invoking.
     * -1 until we invoke: then the current interceptor.
     */
    private int currentInterceptorIndex = -1;

 這個類的方法:

這個類方法也不少,挑重要的講。

①、構造器。

    /**
     * Construct a new ReflectiveMethodInvocation with the given arguments.
     * @param proxy the proxy object that the invocation was made on
     * @param target the target object to invoke
     * @param method the method to invoke
     * @param arguments the arguments to invoke the method with
     * @param targetClass the target class, for MethodMatcher invocations
     * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
     * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
     * MethodMatchers included in this struct must already have been found to have matched
     * as far as was possibly statically. Passing an array might be about 10% faster,
     * but would complicate the code. And it would work only for static pointcuts.
     */
    protected ReflectiveMethodInvocation(
            Object proxy, Object target, Method method, Object[] arguments,
            Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = BridgeMethodResolver.findBridgedMethod(method);
        this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

這個構造器在初始化對象時,設置了代理對象、目標對象、目標對象所屬的類Calss對象、目標方法對應的Method對象、目標方法的參數、以及MethodInterceptor和InterceptorAndDynamicMethodMatcher組成的鏈條。

②、這是方法最重要。不管是通知方法還是目標方法,都是通過的這個方法來調用的。所以,連接點起到了一個調度的作用。

    @Override
    public Object proceed() throws Throwable {
        //    We start with an index of -1 and increment early.  
//currentInterceptorIndex的值等於攔截器鏈的長度的時候說明每個攔截器都被調用了。此時可以調用目標方法了。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint();//調用目標方法,不知道這里的方法名為什么叫invokeJoinpoint,明明是調用的目標方法嘛,還沒參透。 } //currentInterceptorIndex的初始值為-1,所以它是從攔截器鏈條的第一個攔截器開始調用攔截器的。先++。
Object interceptorOrInterceptionAdvice
= this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. //跳過當前的攔截器並且調用鏈條中的下一個攔截器.
return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed.
//調用攔截器。注意:這個對象把自己作為參數。這就是為什么攔截器中又可以調用這個proceed()方法的原因。
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }

現在用一張圖來解釋上面的代碼。

淺色小框表示連接點。深色大框表示攔截器(第一個深色框和最后一個深色框除外)。

箭頭標表示程序的執行方向。我畫的箭頭的起始位置是非常嚴格的,比如后置通知的那個方框,箭頭進來不是直接指向mi.proceed()那行代碼的,意思是表示它之前可能還有代碼。需要執行mi.proceed()那行代碼之前的代碼,然后才執行mi.proceed()這行,執行mi.proceed()的時候程序跳轉到了連接點。在連接點中將取出下一個攔截器,調用它的invoke方法程序進入到下一個攔截器。當程序返回時,箭頭並不是直接指向mi.proceed()這行代碼的,說明這行代碼已經執行完,現在執行mi.proceed()后面的代碼。每個方法的底部都會有一個箭頭指向上一個方框,箭頭在方框的底部說明方法執行完畢並且return了。

 

 根據上面的圖,可以看出無論攔截器鏈中各個攔截器的順序是什么樣的,前置通知總是能夠在調用目標方法之前執行,因為前置通知總是在mi.proceed()之前執行。而后置通知總是在目標方法之后執行,因為后置通知總是在mi.proceed()之后。但是返回通知和后置通知誰先執行呢???這個就跟它們在鏈條的順序有關了。如上圖,應該是返回通知先執行然后才到后置通知。

 


免責聲明!

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



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