• 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()之后。但是返回通知和后置通知谁先执行呢???这个就跟它们在链条的顺序有关了。如上图,应该是返回通知先执行然后才到后置通知。

  • 相关阅读:
    ExtJs自学教程(1):从一切API开始
    集成框架 javaweb开发平台ssmy_m(生成代码) java struts2 mybatis spring maven jquery
    hdu2203 KMP水的问题
    Hibernate制图(两)——许多-于─关系映射
    FPGA 异步时钟处理方
    在Magento产品页面的使用jqZoom
    struts2基本介绍
    让你提前知道软件开发(45):第一印象的代码
    struts2 模型分配问题和延迟加载问题
    性能优化(一个)Hibernate 使用缓存(一个、两、查询)提高系统性能
  • 原文地址:https://www.cnblogs.com/GooPolaris/p/8217962.html
Copyright © 2020-2023  润新知