zoukankan      html  css  js  c++  java
  • JDK动态代理

    什么是代理

    所谓代理,是指对一个对象A的方法调用会被对象B先截获,然后由B来决定是继续调用A的方法或者做其他操作。也就是说,在代码中,我们直接操作的对象从A变成了B,对A的任何操作都要经过B才能完成。那么就可以说,B是A的代理

    如何实现一个代理

    要实现一个代理是非常简单的,只需要将代理对象B设置为目标对象A的相同类型,然后将A对象作为B对象的一个属性,然后将B对象提供出去,那么B可以当作A的代理来使用了。如果从A对象得到它的代理对象B的过程是自动完成的,那么就称之为动态代理。

    注意,在代理关系中,一定会存在一个代理对象和一个目标对象,而且一般这两个对象不能是同一个。试想,如果代理对象和目标对象都是目标对象本身,则失去了代理的作用。反之,如果代理对象和目标对象都是代理对象本身,那么就会形成循环代理,也就是说,对代理对象B的任何操作,需要代理对象B询问自己该如何进行,该询问动作也包含在对B的任何操作范畴内,所以会发出对询问的询问,这在代码中就变成了无限递归了,最终会导致栈空间溢出

    JDK动态动态代理的实现方式

    JDK的动态代理需要提供三份信息,一个classLoader和一个接口列表,以及代理方法(InvocationHandler)。下面说说这三这的作用。

    • classLoader一定要提供,是因为代理对象和被代理对象一定要在同一个类继承体系当中,而JVM判断两个类是不是同一个类时,会将类加载器作为判断标识之一,如果类加载器不一样,则不能认为是同一个类。
    • 接口列表一定要提供,因为动态代理是要去充当目标对象被外部使用,那么代理对象一定要有目标对象的方法,外部才能正常使用,因此提供接口列表,就能协助JDK生成相应的方法签名然后供外部调用。
    • InvocationHandler一定要提供,是因为JDK并不清楚代理对象被调用时,应该如何目标对象,因此这部分逻辑需要我们来提供。在Invocationhanlder中,我们可以做一些自己的操作,然后调用目标对象,甚至,我们也不调用目标对象。

    从上面那几点来看,似乎在生成动态代理的时候,并没有提供目标对象呀。确实,生成一个动态代理对象,只需要知道这个代理对象要实现的方法即可,目标对象其实是在真正调用的时候,才需要用到的。通常的做法是,在生成动态代理的类中,提供一个属性作为目标对象,然后在Invocationhanlder中调用这个目标对象的方法。如下所示:

            final DoSth worker = new DoSth()
            {
                @Override
                public void doSth()
                {
                    System.out.println("I am working");
                }
            };
            final DoSth sb = (DoSth) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(), 
                    new Class[]{DoSth.class},
                    new InvocationHandler()
                    {
                        private DoSth target = worker;
    
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
                        {
                            System.out.println("proxy is called");
                            target.doSth();
                            return null;
                        }
                    });
    
    

    如何查看生成的代理类的代码

    在代码中加入如下片段即可在源码目录中得到生成的代理类的class文件

        Field field = System.class.getDeclaredField("props");
    
        field.setAccessible(true);
    
        Properties props = (Properties) field.get(null);
    
        props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    

    一般生成的类名如下:

    com.sun.proxy.$Proxy0
    

    生成的代码如下:

        public final void doSth() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
  • 相关阅读:
    函数进阶,递归,二分法查找
    内置函数
    IDEA使用maven创建web工程并完善的过程
    后端传入前端的数据的属性名全部为小写的解决方法
    今日总结,复习了很多知识
    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ztreeoneServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; 错误的解决方法
    xxx cannot be resolved to a type 的可能的解决方法,mybatis的Example类不存在
    记录一下Spirng Initializr初始化项目的时候pom文件的内容
    使用nacos进行服务注册的配置
    org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing问题的一种解决方法参考
  • 原文地址:https://www.cnblogs.com/humc/p/6035162.html
Copyright © 2011-2022 走看看