intercept 订阅
Intercept 展开全文
Intercept
信息
操作系统
未知
开发语言
开源协议
未知
Intercept
This plugin simplifies the use of Event Delegation with multiple different descendants. This concept is very useful for pages with dynamic content (f.e: AHAH) where DOM elements are created and removed constantly, requiring re-binding. This method also saves a lot of resources, as it uses less event handlers to achieve the same objective. It can be used in 2 ways:(I will exemplify with a table) $('table') .intercept('click', 'tr', function(e){...}) .intercept('click', 'td.happy', function(e){...}) .intercept('click', '#something', function(e){...}); or $('table').intercept('click', { tr: function(e){...}, 'td.happy': function(e){...}, '#something': function(e){...} }); By calling intercept on the same element/event, the new handler/s are appended to the old list. jQuery.Intercept won't have such a good perfomance when many different selectors are registered to one element, this can be noted for events that are triggered very often, like mouseover. If you need more scalability, and you can handle your problem with simpler selectors. Then you should check jQuery.Listen instead, for a similar approach. Since 1.1.2, "absolute" selectors are supported, that is, selectors that specify parents, descendants, siblings. Just use it the way it's normally used. Thanks to Michael Grosser for bringing up the idea.
收起全文
精华内容
下载资源
问答
  • 2022-05-10 17:17:47

    如何使用CGLIB创建动态代理,网上已经有很多资料,这里就不再赘述。

    直接说结论。

    当我们使用自定义类

    private static class MethodInterceptorImpl implements MethodInterceptor {
        private  Object  target;
        public MethodInterceptorImpl(Object  object){
            this.target= object;
        }
        @Override
        public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxyMethod) throws Throwable {
            proxyMethod 和  obj
           1、method.invoke(obj) 
    
           2、method.invoke(target)
    
           3、proxyMethod.invoke(obj)
    
           4、proxyMethod.invokeSuper(obj)
    
           5、proxyMethod.invoke(target)
    
           6、proxyMethod.invokeSuper(target)
        }
    }

    这里主要讲proxyMethod 和 obj 使用方式。有以下几种调用情况:

    obj是cglib创建的代理对象,target是动态代理的被代理对象,我们生成之前的一般会传入进来的对象

    1、method.invoke(obj) 

    2、method.invoke(target)

    3、proxyMethod.invoke(obj)

    4、proxyMethod.invokeSuper(obj)

    5、proxyMethod.invoke(target)

    6、proxyMethod.invokeSuper(target)

    结论是

    2、4、5 可以执行成功。

    1、3 死循环stack overflow,6类型转换错误。

    原因

    cglib可以理解为继承子类的方式创建动态代理,那么我们可以理解为 target 是父类,obj 是动态代理生成的子类。

    proxyMethod.invoke方法是调用被代理对象(父类)方法,所以入参只能传入 target  ;传入 代理对象 obj 就会死循环。

    proxyMethod.invokeSuper方法是调用 代理对象的 方法,入参只能传入 obj ; 传入 target,会报类型错误。

    同理,method本来就是父类的方法,所以入参只能传入 target  。传入 代理对象 obj 就会死循环。

    更多相关内容
  • const intercept = require ( 'stream-intercept' ) ; var unhook = intercept . write ( process . stdout , ( ... args ) => { return args [ 0 ] + new Date ( ) ; //appends the date after every line on ...
  • intercept函数.xls

    2021-09-18 22:15:41
    intercept函数.xls
  • ip_intercept 使用非常简单: 步骤1。 将ip区复制到“ip_area_generate.php”中你要拦截的地方! 并运行: php ip_area_generate.php 第2步。 测试。 php ip_area_test.php 或将它们复制到 Web 项目并访问它。
  • Intent Intercept Android app Summary: View inter-app communication Description: This app attempts to intercept as many intents as possible in order to examine their contents. This helps when trying ...
  • 方法非常简单:只需在请求或响应上调用intercept方法并注册“ interception ”事件(或者在调用intercept时传递回调)。 当它被触发时,您将获得截获的数据。 到时候你就可以随心所欲地用它做任何事情,然后事情就会...
  • ssl-拦截-ldpreload 用于拦截使用 openssl 建立的 SSL 连接的纯文本的 LD_PRELOAD 库 用法: LD_PRELOAD=./libinterceptssl.so curl https://www.google.com
  • site-intercept

    2021-04-09 21:09:39
    Intercept是一个小型中间件,可在TYPO3内核或接近内核的世界中使用的各种服务之间进行通信。 拦截可以在在线找到。 进程,设置,体系结构等可在本自述文件中找到。 服务 竹岗亭 端点“ / bamboo”-在核心构建完成时...
  • Laravel开发-intercept

    2019-08-28 17:38:13
    Laravel开发-intercept 各种各样的Laravel中间件
  • Intercept Redirect-crx插件

    2021-04-03 02:18:51
    语言:English ...有关此扩展程序支持的域的列表,请访问https://github.com/bjornstar/intercept-redirect对于Mozilla Firefox用户-https://intercept-redirect.firefox.bjornstar.com对于Mirosoft Edge用户- ...
  • fb_intercept_polyfill 一个在hhvm 3.24-> 4.present中使用fb_intercept的polyfill 为什么您不需要此polyfill。 该填充工具桥是能够理解的HHVM之间的差距inout没有过渡期是为&$references和一个不明白&$references...
  • CGLIB动态代理之intercept函数刨析

    千次阅读 多人点赞 2020-12-12 15:51:01
    网上搜CGLIB动态代理,几乎所有的博文都只给了示例代码而缺少对代码的解释说明(特别是关键的intercept函数),看完实在是云里雾里。所以,这篇博文将带你从源码的角度来理解intercept函数。 前言 关于如何使用...

    网上搜CGLIB动态代理,几乎所有的博文都只给了示例代码而缺少对代码的解释说明(特别是关键的intercept函数),看完实在是云里雾里。所以,这篇博文将带你从源码的角度来理解intercept函数。

    前言

    关于如何使用CGLIB创建动态代理,网上已经有很多资料,这里就不再赘述。本文将使用如下代码进行分析,如果你还看不懂下面的代码,请先自行搜索资料看懂后再继续后面的内容。

    import net.sf.cglib.core.DebuggingClassWriter;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    
    class Human {
        public void say() {
            System.out.println("I am super man~~~");
        }
    }
    
    class ProxyWithCglib {
        public static Object newProxy(Object object) {
            Enhancer enhancer = new Enhancer();
    
            enhancer.setSuperclass(object.getClass());
    
            MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
            myMethodInterceptor.bind(object);
            enhancer.setCallback(myMethodInterceptor);
    
            return enhancer.create();
        }
    }
    
    class MyMethodInterceptor implements MethodInterceptor {
    
        private Object target;
    
        public void bind(Object o) {
            this.target = o;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("pre-function logic.");
            
            Object ret = methodProxy.invokeSuper(o, objects);
    
            System.out.println("post-function logic.");
            
            return ret;
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./debug_info"); // 用于输出CGLIB产生的类
            Human humanProxy = (Human) ProxyWithCglib.newProxy(new Human());
            System.out.println(humanProxy.getClass());
            humanProxy.say();
        }
    }
    

    简要对代码进行一些说明:

    • Human类为被代理对象;
    • MyMethodInterceptor中的target属性用于保存被代理对象的实例;
    • ProxyWithCglib类的静态方法newProxy用于创建代理对象实例;
    • 运行代码即可在debug_info目录下看到运行时生成的类,这将用于我们后续的分析

    理解intercept的四个参数

    要理解intercept方法的参数的含义,一个不错的办法就是看看运行时调用intercept方法的时候传入的参数是什么。

    为了找到在哪里调用了intercept方法,我们从main方法的最后一行:

    humanProxy.say();
    

    开始跟。

    根据

    System.out.println(humanProxy.getClass());
    

    这一行我们可以知道运行时生成的类为:

    Human$$EnhancerByCGLIB$$xxxxxx
    

    这个类的代码为(为了便于阅读,只放出我们关注的部分):

    public class Human$$EnhancerByCGLIB$$1a29a813 extends Human implements Factory {
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$say$0$Method;
        private static final MethodProxy CGLIB$say$0$Proxy;
        private static final Object[] CGLIB$emptyArgs;
        private static final Method CGLIB$equals$1$Method;
        private static final MethodProxy CGLIB$equals$1$Proxy;
        private static final Method CGLIB$toString$2$Method;
        private static final MethodProxy CGLIB$toString$2$Proxy;
        private static final Method CGLIB$hashCode$3$Method;
        private static final MethodProxy CGLIB$hashCode$3$Proxy;
        private static final Method CGLIB$clone$4$Method;
        private static final MethodProxy CGLIB$clone$4$Proxy;
    
        static void CGLIB$STATICHOOK1() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("Human$$EnhancerByCGLIB$$1a29a813");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, (var1 = Class.forName("Human")).getDeclaredMethods())[0];
            CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
            // .....
        }
    
        final void CGLIB$say$0() {
            super.say();
        }
    
        public final void say() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
            } else {
                super.say();
            }
        }
    	//.....
    }
    

    通过这段代码,我们可以获得如下信息:

    • CGLIB为我们生成的代理类继承了Human,验证了CGLIB是基于继承来实现代理的。

    • 当我们调用humanProxy.say()方法时,如果我们设置了MethodInterceptor的话,执行的逻辑实际是:

      var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
      

      所以,也就是在这里,调用了intercept方法!

      再多说一句,这里的MethodInterceptor对象就是我们用enhancer的setCallback方法传进来的,即如下代码:

      MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
      myMethodInterceptor.bind(object);
      enhancer.setCallback(myMethodInterceptor);
      

    好了,找到intercept方法的调用后,我们就可以开始理解他的四个参数了,为了便于阅读,我把intercept的声明也列出来:

    // 声明
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {...}
    
    // 调用 (在say()方法的上下文中)
    var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    
    // 相关的变量(在CGLIB$STATICHOOK1()中初始化)
    CGLIB$emptyArgs = new Object[0];
    
    CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, (var1 = Class.forName("Human")).getDeclaredMethods())[0];
    
    CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
    

    这么一看,intercept方法的四个参数的含义也就比较清楚了:

    • Object o:代理对象本身

      通过调用时传入this很容易看出。

    • Method method: 被代理对象的方法

      通过CGLIB$say 0 0 0Method的初始化过程我们可以知道,他实际就指向了被代理类(Human)中对应的方法(say())。也就是说,你在代理对象上调用了方法fun,当进入到intercept函数时,method参数就是指向了被代理对象的fun方法。

    • Object[] objects:函数调用的参数

      通过CGLIB$emptyArgs我们也可以很容易的猜出这个参数实际就是函数调用时要传递的参数列表。在本例中,由于say()不需要任何参数,所以传个空参即可。

    • MethodProxy methodProxy:方法的代理

      这个是四个参数中最难理解的一个,下面对这个参数展开说明。

    理解MethodProxy(重点!)

    同样,我们还是从源码的角度进行分析,源码中创建MethodProxy的代码为:

    Class var0 = Class.forName("Human$$EnhancerByCGLIB$$1a29a813");
    var1 = Class.forName("Human");
    CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
    

    这里,var1就是我们的被代理对象,而var0则是代理对象本身,然后通过MethodProxy.create()方法创建一个MethodProxy,MethodProxy类的源码为:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package net.sf.cglib.proxy;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import net.sf.cglib.core.AbstractClassGenerator;
    import net.sf.cglib.core.CodeGenerationException;
    import net.sf.cglib.core.GeneratorStrategy;
    import net.sf.cglib.core.NamingPolicy;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.reflect.FastClass;
    import net.sf.cglib.reflect.FastClass.Generator;
    
    public class MethodProxy {
        private Signature sig1;
        private Signature sig2;
        private MethodProxy.CreateInfo createInfo;
        private final Object initLock = new Object();
        private volatile MethodProxy.FastClassInfo fastClassInfo;
    
        public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
            MethodProxy proxy = new MethodProxy();
            proxy.sig1 = new Signature(name1, desc);
            proxy.sig2 = new Signature(name2, desc);
            proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
            return proxy;
        }
    
        private void init() {
            if (this.fastClassInfo == null) {
                synchronized(this.initLock) {
                    if (this.fastClassInfo == null) {
                        MethodProxy.CreateInfo ci = this.createInfo;
                        MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                        fci.f1 = helper(ci, ci.c1);
                        fci.f2 = helper(ci, ci.c2);
                        fci.i1 = fci.f1.getIndex(this.sig1);
                        fci.i2 = fci.f2.getIndex(this.sig2);
                        this.fastClassInfo = fci;
                        this.createInfo = null;
                    }
                }
            }
    
        }
    
        private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
            Generator g = new Generator();
            g.setType(type);
            g.setClassLoader(ci.c2.getClassLoader());
            g.setNamingPolicy(ci.namingPolicy);
            g.setStrategy(ci.strategy);
            g.setAttemptLoad(ci.attemptLoad);
            return g.create();
        }
    
    	// 为了便于观看,省略部分非关键代码
    
        public static MethodProxy find(Class type, Signature sig) {
            try {
                Method m = type.getDeclaredMethod("CGLIB$findMethodProxy", MethodInterceptorGenerator.FIND_PROXY_TYPES);
                return (MethodProxy)m.invoke((Object)null, sig);
            } catch (NoSuchMethodException var3) {
                throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
            } catch (IllegalAccessException var4) {
                throw new CodeGenerationException(var4);
            } catch (InvocationTargetException var5) {
                throw new CodeGenerationException(var5);
            }
        }
    
        public Object invoke(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                return fci.f1.invoke(fci.i1, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            } catch (IllegalArgumentException var5) {
                if (this.fastClassInfo.i1 < 0) {
                    throw new IllegalArgumentException("Protected method: " + this.sig1);
                } else {
                    throw var5;
                }
            }
        }
    
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
    
        private static class CreateInfo {
            // 省略
        }
    
        private static class FastClassInfo {
    		// 省略
        }
    }
    

    友情提示:看下面的文字时,一定要特别关注是代理类,还是被代理类!

    代码不算长,有一定编程经验的人应该都能看个大概(建议不要在这里看,最好能用IDE看,会方便很多),下面说一下看完代码后应得到的一些信息:

    • 代码里实际维护了两个东西,一个指向被代理的对象,一个指向代理对象。且和被代理对象相关的变量以1结尾,而与代理对象相关的变量则以2结尾

      这个通过create方法的源码即可知道。我们在调用create方法时,第一个参数var1,对应create方法中的形参class1,而var1指向的是被代理类,所以在MethodProxy类中,xxx1就是和被代理类有关;类似的,xxx2就是和代理类相关。这是最关键的一点!

    • 函数调用过程中涉及到了FastClass

      MethodProxy的两个重要方法就是invoke和invokeSuper,我们可以看到在他们的源码中,其实最后都是通过FastClass来完成了函数的调用。FastClass的原理就属于另外一个内容了,就不放在这篇文章里讲了。你现在只需要知道FastClass可以加速调用过程即可

    我们在使用过程中,最主要的还是调用MethodProxy的invoke或者invokeSuper方法。所以我们再对这两个方法挖的深入一点:

    public Object invoke(Object obj, Object[] args) throws Throwable {
         try {
             this.init();
             MethodProxy.FastClassInfo fci = this.fastClassInfo;
             return fci.f1.invoke(fci.i1, obj, args);
         } catch (InvocationTargetException var4) {
             throw var4.getTargetException();
         } catch (IllegalArgumentException var5) {
             if (this.fastClassInfo.i1 < 0) {
                 throw new IllegalArgumentException("Protected method: " + this.sig1);
             } else {
                 throw var5;
             }
         }
     }
    
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
    

    从代码中可以看出,invoke和invokeSuper最主要的区别在于他们是用不同的FastClass完成了最终的调用,即:

    // inivoke
    return fci.f1.invoke(fci.i1, obj, args);
    // invokeSuper
    return fci.f2.invoke(fci.i2, obj, args);
    

    回想我们前面说的,1就是和被代理类相关,2就是和代理类相关。所以,套用到这里,我们就可以对这两个语句进行解释:

    • invoke中的fci.f1.invoke(fci.i1, obj, args);表示用对象obj以参数args调用被代理类中函数描述为desc的name1方法

      desc和name1是什么呢?再回过头看看MethodPeoxy的create()函数:

      // 声明
      public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
         MethodProxy proxy = new MethodProxy();
         proxy.sig1 = new Signature(name1, desc);
         proxy.sig2 = new Signature(name2, desc);
         proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
         return proxy;
      }
      // 调用
      CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
      

      可以发现,desc描述了这个函数的参数列表以及返回值,由于我们这里的say方法没有返回值且不接收参数,所以是()V。而name1则是函数在被代理类中的名字。在本文的例子中就是say。

    • invokeSuper中的fci.f2.invoke(fci.i2, obj, args);表示用对象obj以参数args调用代理类中函数描述为desc的name2方法

      和上面一样的分析方式,可以知道这里调用的是代理类的CGLIB$say$0方法,那这个方法是什么呢?回到源码中一看,发现这个方法是:

      final void CGLIB$say$0() {
          super.say();
      }
      

      这个方法很简单,只有一行,直接super.say();相当于直接调用了父类(也就是被代理类)的say方法。

    好了,分析到这,我们对MethodProxy应该有个比较清晰的认识了,总结起来就是(一定要理解!):

    • MethodProxy本质上是对一个方法的代理。通过create函数,我们可以创建一个MethodProxy,并且指定被代理类(class1),代理类(class2),被代理方法的描述(desc),被代理方法在被代理类中的名字(name1),被代理方法在代理类中的名字(name2)

      这里对name2的解释可能不是非常准确。对于一个被代理类中的方法(如say()),代理类其实会为他生成两个方法(say()和CGLIB$say$0()):

      final void CGLIB$say$0() {
          super.say();
      }
      
      public final void say() {
          MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
          if (var10000 == null) {
              CGLIB$BIND_CALLBACKS(this);
              var10000 = this.CGLIB$CALLBACK_0;
          }
      
          if (var10000 != null) {
              var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
          } else {
              super.say();
          }
      }
      

      其中,和被代理类中的方法完全重名的方法是用来给外部调用的,这个方法实际重写了被代理类(也就是父类)的方法。逻辑就是要把函数调用转发到intercept函数中,从而实现对被代理对象的代理!还有一个函数带有CGLIB前缀和标号(CGLIB$say$0()),这个函数逻辑简单,就是直接调用被代理类的函数。我们这里说的name2就是指带有CGLIB前缀和标号的这个函数!

    • invoke(Object obj, Object[] args)函数的语义是用对象obj以参数args调用被代理类中函数描述为desc的name1方法

    • invokeSuper(Object obj, Object[] args)函数的语义是用对象obj以参数args调用代理类中函数描述为desc的name2方法

    编写intercept逻辑

    经过以上分析,我们已经对intercept的四个参数有了清晰的认识了。那最终的目的当然还是应用啦。所以最后我们来说一下如何编写intercept方法的逻辑。intercept方法的逻辑模板就是:

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 1. pre-function logic
        
        // 2. invoke actual function
    
    	// 3. post-function logic
    	
    	// return 
        return ret;
    }
    

    简单来说就是:

    1. 执行函数前的代理逻辑
    2. 调用被代理类的方法,执行函数
    3. 执行函数后的代理逻辑
    4. 将返回值返回

    这里,第1,3步根据业务逻辑来写就行,第4步返回也没什么好说的。最关键的在于我们怎么调用被代理类的方法呢(即第2步)?

    那这里就要用到我们前面对intercept参数的理解啦。我们可以用method来完成,也可以用methodProxy来完成。下面依次讲解全部可行的方法:

    1. 通过method.invoke(target, objects)

      这里target是我们维护的被代理对象,所以这句话相当于是说在target上用objects作为参数调用函数method,根据我们前面的分析,这个method实际就是Human类中的say方法,所以可以。

    2. 通过methodProxy.invoke(target, objects)

      根据前面的分析,这里的语义为“用target以objects为参数调用被代理类上的函数”,实际就是用target去调用了say,所以没问题。

    3. 通过methodProxy.invokeSuper(o, objects)

      同样的道理,根据前面的分析,这句话相当于是用o调用了CGLIB$say$0方法,也没问题。

    虽然三种方式都可以,但还是推荐使用第2,3种方式,因为他们使用了FastClass,可以提升效率。第2种方式要求你在MyMethodInterceptor中维护一个被代理对象的实例target,而第3种方式则没有这个要求。


    以上是三种可行的方式,还有三种不可行的方式也需要注意:

    1. method.invoke(o, objects)

      死循环。这句话相当于用o调用say方法,o中的say方法会一直调用intercept方法,intercept方法又调用say方法,从而导致死循环,stack overflow!

    2. methodProxy.invokeSuper(target, objects);

      运行报错!target是被代理类,也就是父类,这句话的语义相当于你要用target去调用CGLIB$say$0方法,而这个方法是在子类中才有的!所以会报错!

    3. methodProxy.invoke(o, objects);

      死循环。这句话引起死循环的原因和1非常类似。相当于你要在代理类上调用被代理类的say方法,所以最终会分配到intercept,那死循环就出现了!say—>intercept—>say---->intercept,最后stack overflow!

    好了,到此为止,我们已经分析了所有可能的调用方式以及他们的正确性。实际上,这些方式完全不用背,只要你理解了本文所分析的内容,看到某一种调用方式,你就能分析出他的语义,从而推断出他的正确性。

    最后,再放一下完整的测试代码,以下代码可以用于测试所有6种方式。

    import net.sf.cglib.core.DebuggingClassWriter;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    
    class Human {
        public void say() {
            System.out.println("I am super man~~~");
        }
    }
    
    class ProxyWithCglib {
        public static Object newProxy(Object object) {
            Enhancer enhancer = new Enhancer();
    
            enhancer.setSuperclass(object.getClass());
    
            MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
            myMethodInterceptor.bind(object);
            enhancer.setCallback(myMethodInterceptor);
    
            return enhancer.create();
        }
    }
    
    class MyMethodInterceptor implements MethodInterceptor {
    
        private Object target;
    
        public void bind(Object o) {
            this.target = o;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("pre-function logic.");
    
    //        Object ret = method.invoke(target, objects); // 可以
    //        Object ret = method.invoke(o, objects); // 死循环
    //        Object ret = methodProxy.invokeSuper(o, objects); // 可以
            Object ret = methodProxy.invokeSuper(target, objects); // 运行时报错
    //        Object ret = methodProxy.invoke(target, objects); // 可以
    //        Object ret = methodProxy.invoke(o, objects); // 死循环
    
            System.out.println("post-function logic.");
            return ret;
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./debug_info");
            Human humanProxy = (Human) ProxyWithCglib.newProxy(new Human());
            System.out.println(humanProxy.getClass());
            humanProxy.say();
        }
    }
    

    写在最后

    总结一下,本文旨在通过源码层面的分析来更好的理解CGLIB动态代理中的关键方法intercept,回答了如何在intercept中完成对被代理方法的调用这一绝大多数博客都没有明确说明的问题。其中,对于MethodProxy的理解是本文的关键。最后,本文对所有可能的六种调用方式进行了分析,说明了他们的语义及正确性。希望本文能对你有所帮助。


    最后的最后:源码分析类文章不太会写,欢迎批评指正!

    展开全文
  • intercept.js (deprecated) 超简单表单验证解决方案 ####优势 操作简单 : 只须在dom中添加属性即可 异步支持 : 支持异步提交表单,以及异步字段 依赖关系 : 不需要任何依赖,原生javascript 兼容性能 : ie8+,以及...
  • // Now we intercept this method to control its in- and outputs. // A `release`-function is returned to release the intercepted method and // revert it to its original state. var rel
  • 资源加载等 不需要在使用前调用 cy.server() ,实际上 cy.server() 根本不影响 cy.intercept() 默认情况下没有将请求方法设置为 GET 语法格式 cy.intercept(url, routeHandler?) cy.intercept(method, url, ...

    如果想从头学起Cypress,可以看下面的系列文章哦

    https://www.cnblogs.com/poloyy/category/1768839.html

    作用

    使用该命令在网络层管理 HTTP 请求的行为

    包含以下功能

    • 对任何类型的 HTTP 请求进行 stub 或 spy
    • 在 HTTP 请求发送到目标服务器前,可以修改 HTTP 请求 body、headers、URL(类似抓包工具对请求进行打断点然后修改)
    • 动态或静态地对 HTTP 请求的响应进行 stub
    • 接收 HTTP 响应后可对 HTTP 响应 body、headers、status、code 进行修改(类似抓包工具对响应进行打断点然后修改)
    • 在所有阶段都可以完全访问所有 HTTP 请求

    相较于 cy.route() 的不同

     cy.route() 命令详解:https://www.cnblogs.com/poloyy/p/13852941.html

    • 可以拦截所有类型的网络请求,包括 Fetch API,页面加载,XMLHttpRequest,资源加载等
    • 不需要在使用前调用 cy.server() ,实际上 cy.server() 根本不影响 cy.intercept() 
    • 默认情况下没有将请求方法设置为 GET

    语法格式

    cy.intercept(url, routeHandler?)
    cy.intercept(method, url, routeHandler?)
    cy.intercept(routeMatcher, routeHandler?)
    

    url

    要匹配的请求 URL ,可以是字符串也可以是正则表达式

    cy.intercept('http://example.com/widgets')
    cy.intercept('http://example.com/widgets', { fixture: 'widgets.json' })
    

    没有指定请求方法的话,可以匹配任意类型的请求方法

    method

    请求方法

    cy.intercept('POST', 'http://example.com/widgets', {
      statusCode: 200,
      body: 'it worked!'
    })
    

    routeMatcher 

    • 它是一个对象
    • 用于匹配此路由将处理哪些传入的 HTTP 请求
    • 所有对象属性都是可选的,不是必填的
    • 设置的所有属性必须与路由匹配才能处理请求
    • 如果将字符串传递给任何属性,则将使用 minimatch 将与请求进行全局匹配

    它有以下属性

    {
      /**
       * 与 HTTP Basic身份验证中使用的用户名和密码匹配
       */
      auth?: { username: string | RegExp, password: string | RegExp }
    
      /**
       * 与请求上的 HTTP Headers 匹配
       */
      headers?: {
        [name: string]: string | RegExp
      }
    
      /**
       * 与请求上的 hostname 匹配
       */
      hostname?: string | RegExp
    
      /**
       * If 'true', 只有 https 的请求会被匹配
       * If 'false', 只有 http 的请求会被匹配
       */
      https?: boolean
    
      /**
       * 与请求上的 method 请求方法匹配
       * 默认 '*', 匹配全部类型的 method
       */
      method?: string | RegExp
    
      /**
       * 主机名后的路径, 包括了 ? 后面的查询参数
       * www.baidu.com/s?wd=2
       */
      path?: string | RegExp
    
      /**
       * 和 path 一样, 不过不管 ? 后面的查询参数
       * www.baidu.com/s
       */
      pathname?: string | RegExp
    
      /**
       * 与指定的端口匹配, 或者传递多个端口组成的数组, 其中一个匹配上就行了
       */
      port?: number | number[]
    
      /**
       * 与请求路径 ? 后面跟的查询参数匹配上
       * wd=2
       */
      query?: {
        [key: string]: string | RegExp
      }
    
      /**
       * 完整的请求 url
       * http://www.baidu.com/s?wd=2
       */
      url?: string | RegExp
    }
    

    routeHandler 

    • routeHandler 定义了如果请求和 routeMatcher 匹配将对请求进行的指定的处理
    • 可接受的数据类型:string、object、Function、StaticResponse

    StaticResponse

    • 相当于一个自定义响应体对象
    • 可以自定义 Response headers、HTTP 状态码、Response body 等
    • 详细栗子将在后面展开讲解

    StaticResponse 对象的属性

    {
      /**
       * 将 fixture 文件作为响应主体, 以 cypress/fixtures 为根目录
       */
      fixture?: string
      /**
       * 将字符串或 JSON 对象作为响应主体
       */
      body?: string | object | object[]
      /**
       * 响应 headers
       * @default {}
       */
      headers?: { [key: string]: string }
      /**
       * 响应状态码
       * @default 200
       */
      statusCode?: number
      /**
       * 如果 true, Cypress 将破坏网络连接, 并且不发送任何响应
       * 主要用于模拟无法访问的服务器
       * 请勿与其他选项结合使用
       */
      forceNetworkError?: boolean
      /**
       * 发送响应前要延迟的毫秒数
       */
      delayMs?: number
      /**
       * 以多少 kbps 发送响应体
       */
      throttleKbps?: number
    }
    

    string

    • 如果传递一个字符串,这个值相当于响应 body 的值
    • 等价于 StaticResponse 对象 { body: "foo" } 

    object

    • 如果传递了没有 StaticResponse 密钥的对象,则它将作为 JSON 响应 Body 发送
    • 例如, {foo:'bar'} 等价于 StaticResponse 对象 {body:{foo:'bar'}} 

    function

    • 如果传递了一个回调函数,当一个请求匹配上了该路由将会自动调用这个函数
    • 函数第一个参数是请求对象
    • 在回调函数内部,可以修改外发请求、发送响应、访问实际响应
    • 详细栗子将在后面展开讲解

    命令返回结果

    • 返回 null
    • 可以链接 as() 进行别名,但不可链接其他命令
    • 可以使用 cy.wait() 等待 cy.intercept() 路由匹配上请求,这将会产生一个对象,包含匹配上的请求/响应相关信息

    实际栗子的前置准备

    Cypress 官方项目的下载地址:https://github.com/cypress-io/cypress-example-kitchensink

    下载好后进入下图项目文件夹

    启动项目

    npm start
    

    通过 URL 路由匹配请求的栗子

    测试代码

    等价于 route() 的测试代码

    注:  route()  未来将会被弃用

    运行结果

    登录请求匹配上了路由

    Console 查看 cy.wait() 返回的对象

    最重要的当然是 request 和 response 两个属性

    通过 RouteMatcher 路由匹配请求的栗子

    测试代码

    断言请求体和响应状态码

    运行结果

    Console 查看 cy.wait() 返回的对象

    另一种断言方式

    // 断言匹配此路由的请求接收到包含【username】的请求 body
    cy.wait('@login3').its('request.body').should('have.property', 'username')
    
    // 断言匹配此路由的请求接收到 HTTP 状态码为 500
    cy.wait('@login3').its('response.statusCode').should('eq', 200)
    
    // 断言匹配此路由的请求接收到包含【redirect】的请求 body
    cy.wait('@login3').its('response.body').should('have.property', 'redirect')
    

    不过这样的话只能每次写一条不能同时三条都写,所以还是建议像代码图一样,先 .then() 再进行断言

    自定义不同类型的响应体的各种栗子

    自定义一个纯字符串的响应体

    测试代码

    运行结果

    接口响应

    自定义一个 JSON 的响应体

    测试代码

    会从cypress安装目录/fixtures 下读取对应的数据文件,它会变成响应 body 的数据

    test.json 数据文件

    运行结果

    接口响应

    自定义一个 StaticResponse 的响应体

    测试代码

    自定义了响应body、statusCode,还有返回响应的延时时间

    运行结果

    延时生效了

    body 和 statusCode 变成自定义的数据了

    拦截请求的栗子

    前置操作

    beforeEach(() => {
        cy.visit('http://localhost:7079/login')
    })
    

    断言请求的栗子

    测试代码

    运行结果

    Console 查看打印结果

    可以看到回调函数只有一个参数,就是 request 参数

    重点

    回调函数内不能包含 cy.**() 的命令,如果包含会报错

    简单来说就是

     cy.type() 命令执行完后会返回一个 promise 对象,同时又会调用回调函数,而回调函数内又调用了 cy.get() 返回了一个 promise 对象,Cypress 会将这种情况当做测试失败处理

    将请求传递给下一个路由处理程序

    前言

    意思就是一个请求可以同时匹配上多个路由

    测试代码

    运行结果

    一个登录请求匹配成功了两个路由,且回调函数会按匹配的顺序执行

    总结

    回调函数的参数就是一个请求对象,它其实可以调用以下方法

    {
      /**
       * 销毁该请求并返回网络错误的响应
       */
      destroy(): void
    
      /**
       * 控制请求的响应
       * 如果传入的是一个函数, 则它是回调函数, 当响应时会调用
       * 如果传入的是一个 StaticResponse 对象, 将不会发出请求, 而是直接将这个对象当做响应返回
       */
      reply(interceptor?: StaticResponse | HttpResponseInterceptor): void
    
      /**
       * 使用 response body(必填) 和 response header(可选) 响应请求
       */
      reply(body: string | object, headers?: { [key: string]: string }): void
    
      /**
       * 使用 HTTP 状态码(必填)、 response body(可选)、response header(可选) 响应请求
       */
      reply(status: number, body?: string | object, headers?: { [key: string]: string }): void
    
      /**
       * 重定向到新的 location 来响应请求,
       * @param statusCode 用来重定向的 HTTP 状态代码, Default: 302
       */
      redirect(location: string, statusCode?: number): void
    }
    

    拦截响应的栗子

    req.reply() 函数详解

    前言

    可以使用 req.reply() 函数来动态控制对请求的响应

    使用讲解

    cy.intercept('/login', (req) => {
        // functions on 'req' can be used to dynamically respond to a request here
    
        // 将请求发送到目标服务器
        req.reply()
    
        // 将这个 JSON 对象响应请求
        req.reply({plan: 'starter'})
    
        // 将请求发送到目标服务器, 并且拦截服务器返回的实际响应, 然后进行后续操作(类似抓包工具对响应打断点)
        req.reply((res) => {
            // res 就是实际的响应对象
        })
    })
    

    .reply() 直接修改响应的栗子

    测试代码

    接口响应内容

    拦截响应的小栗子

    测试代码

    运行结果

    Console 查看打印结果

    一个是 request 对象,一个是 response 对象

    自定义响应内容

    前言

    • 可以使用 resp.send() 函数动态控制传入的响应
    • 另外,当响应发送到浏览器时,对 resp 的任何修改都将保留
    • 如果尚未调用 resp.send() ,则它会在 req.reply() 回调函数完成后隐式调用

    使用讲解

    cy.intercept('/notification', (req) => {
        req.reply((resp) => {
            // Success 将作为 response body 返回到浏览器
            resp.send('Success')
    
            // 将 success.json 里面的数据作为 response body 返回到浏览器
            resp.send({fixture: 'success.json'})
    
            // 将响应延迟 1000ms
            resp.delay(1000)
    
            // 将响应限制为 64kbps
            resp.throttle(64)
        })
    })
    

    传递字符串作为响应内容

    测试代码

    接口响应内容

    传递 JSON 对象作为响应内容

    测试代码

    接口响应内容

    传递 StaticResponse 对象作为响应内容

    测试代码

    接口响应内容

    resp 可调用的函数总结

    {
    /**
    * 可以自定义 response statusCode、response body、response header
    * 也可以直接传 StaticResponse 对象
    */
    send(status: number, body?: string | number | object, headers?: { [key: string]: string }): void
    send(body: string | object, headers?: { [key: string]: string }): void
    send(staticResponse: StaticResponse): void
    /**
    * 继续返回响应
    */
    send(): void
    /**
    * 等待 delayMs 毫秒,然后再将响应发送给客户端
    */
    delay: (delayMs: number) => IncomingHttpResponse
    /**
    * 以多少 kbps 的速度发送响应
    */
    throttle: (throttleKbps: number) => IncomingHttpResponse
    }
    
    展开全文
  • 作为一名后端开发,每次调接口联调时都很痛苦,postman intercept插件感觉香,有需要的可以自行下载,网上也找了很多和下载了很多都失败。有问题可以留言!!!
  • # events-intercept 节点非常强大。 但是,有时在事件到达处理程序之前拦截事件、修改数据或发出其他事件可能很有价值。 那是event-intercept 。 ##安装 npm install events-intercept ##独立使用 该模块包含一...
  • proxy_intercept_errors指令应用

    一   总汇

    ①   需求背景引出

    ②   官方介绍

    proxy_intercept_errors  on|off;
    
    作用:当上游响应的响应码'大于等于'300[常见"404"、"500"等]时,应将响应'直接返回'客户端还是nginx捕获后"自定义"错误页面'按error_page指令'处理
    
    思考:如果开启'on',但是没有配置'error_page'呢?会出现什么现象?

    ③  案例讲解

    1)实验环境

    1. nginx作为'代理'服务器 -->172.25.2.100
    
    2. 上游服务器也是'nginx'["为了简单"] -->172.25.2.157
    
    备注:也可以为'python、tomcat、php、perl'等应用程序均可
    
    补充:更简单的是一个主机上'启'两个nginx,'端口'不一样

    2)代理服务器配置

    3)上游[后端服务器]配置

     4)测试1 

    1. 上面的是'nginx代理'的日志
    
    2. 下面的是'上游服务器'的日志

    5)测试2

    6)测试3

     (1)后端服务器直接断开,nginx代理侧的日志

    说明1:显然请求没有到达'后端服务',但是'nginx'还是记录'upstream_status为502',所以这个值'不能'表明建立了'tcp'连接
    
    说明2:status和'upstream_status'不一定总是'一致的'

    (2)思考

    ++++++++++ "思考一个问题"  ++++++++++
    
    如果'代理侧'开启拦截共功能'on',但是'error_page'没有配置'补获'对应的'错误码',那客户端得到的'报错信息'是谁提供的? --> '后端的'

    1. 上面是nginx'代理'配置 -->没有配置'502',配置了'500、503、504'、同时'开启拦截'
    
    2. 下面是'后端服务器'配置

    ++++++++++++"测试"++++++++++++

      

     (3)对比实验

      

    1. 上面是'nginx'代理侧的日志
    
    2. 下面是'后端服务器'的日志

     参考链接

     nginx模拟死循环

     nginx的internal指令

    遗留1: nginx的高级正则应用 -->命名补获之类

    遗留2:探究location @的应用场景

    遗留3:error_page也可以对客户端的请求进行直接返回错误页面

    展开全文
  • intercept函数的功能和实践操作
  • 在 跑一个cypress 进行ui 操作的用例时, 也可以...cypress也提供了cy.intercept 供使用。https://docs.cypress.io/api/commands/intercept#Syntax // spying, dynamic stubbing, request modification, etc. cy...
  • Intercept and cache requests of WKWebView. 介绍 网易团队采用了更完整细致(原理相同)的解决方案,建议采用他们的: Features Intercept All Requests of WKWebView. Cache requests. Installation Drag the SSWKURL...
  • Intercept 拦截器的主要作用是拦截请求,进行处理,比如用户登录,权限校验,主要针对Action请求进行处理。 拦截器的实现可以继承HandlerInterceptorAdapter或者实现HandlerInterceptor接口 public class ...
  • Search Intercept-crx插件

    2021-04-04 11:58:17
    语言:English 拦截传出的Google搜索! 您是否曾经创建过无意的Google搜索? 这可能很难配置,但是如果您喜欢挑战,那么值得付出努力! 如果配置正确,它可以使您有机会查看在URL栏中输入的所有内容,然后再将其发送...
  • 在进行线性回归方法调用的时候,报这个错误 <p><img alt="" height="136" src=... 在查找函数方法的时候,确实存在这个方法并且根据示例来看没有出错的情况 ... ...
  • coef_ 和 intercept_ 的含义

    千次阅读 2021-11-15 15:36:03
    “斜率”参数(w,也叫作权重或系数)被保存在 coef_ 属性中,而偏移或截距(b)被保 存在 intercept_ 属性中 L1 正则化时,可以通过coef_中不等于0的个数来确定使用了几个特征np.sum(lasso.coef_ != 0) ...
  • Intercept 实现Intercept方法
  • Proxy是代理的意思 intercept是拦截的意思 Raw:原始数据 Hex:16进制数据 Params:请求参数数据 Header:请求头数据 Proxy模块下面的HTTPhistory项
  • Ajax-request-intercept.zip

    2019-09-17 10:50:33
    Ajax-request-intercept.zip,所有请求的拦截器库,包括本机获取和本机ajax(或xmlhttprequest),ajax代表异步javascript和xml。它是多种web技术的集合,包括html、css、json、xml和javascript。它用于创建动态网页,...
  • coef_与intercept_含义

    千次阅读 2020-07-19 10:13:28
    model.LinearRegression() reg.fit([[1, 0], [0, 1], [2, 2]], [2, 2, 3]) print(reg.coef_) print(reg.intercept_) [0.33333333 0.33333333] 1.6666666666666667 这个才是完整的,intercept_代表w0,另两个参数分别...
  • 如果我启动了调试器,则会收到两次调用intercept()的调用,并且在所传递的ActionInvocation上找不到任何明显的内容. (更新:这部分是我的主要困惑,一旦知道,我就可以在下面回答我的问题) “Big picture”页面对被调用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 118,477
精华内容 47,390
关键字:

intercept