精华内容
下载资源
问答
  • Object result = method.invoke(object, args);
  • 设计模式中有一种模式叫代理模式,Spring框架离不开动态代理技术,Android hook技术用到了反射 + 动态代理,Framework中我们也经常看到各种proxy,如ApplicationThreadProxy, ActivityManagerProxy。 那么,今天...

     

    设计模式中有一种模式叫代理模式,Spring框架离不开动态代理技术,Android hook技术用到了反射 + 动态代理,Framework中我们也经常看到各种proxy,如ApplicationThreadProxy, ActivityManagerProxy。

    那么,今天就来说下Java中的代理模式和动态代理。

     

    目录:

    1. 代理模式
    2. 静态代理
    3. 动态代理
    4. 代理模式的优缺点
    5. 代理模式的使用场景
    6. 动态代理原理分析

     

     

    1. 代理模式

    代理模式是常用的java设计模式,它的特征是代理类与委托类有相同的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。

    举一个通俗点的例子:

    我们男生都爱看NBA,比如詹姆斯转会湖人,球队老板不会直接找詹姆斯,而是找他的经纪人商谈,这中间就拦了一道。詹姆斯可以打球,代言,他的经纪人也具有他一样的属性:打球,代言,但是真正打球,代言的是詹姆斯本人。

    画一个草图理解下:

    代理模式根据创建代理类的时间点,又可以分为静态代理和动态代理。

     

     

     

    2. 静态代理

     

    • 2.1 概念

    由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

     

    • 2.2 例子
    private interface IProxy {
    
            void playBasketball();
    
            void endorsement();
        }
    
        private static final class James implements IProxy {
    
            @Override
            public void playBasketball() {
                System.out.println("James play basketball");
            }
    
            @Override
            public void endorsement() {
                System.out.println("James endorsement");
            }
        }
    
    
        private static final class Middleman implements IProxy {
    
            final James james;
    
            Middleman(James james) {
                this.james = james;
            }
    
            @Override
            public void playBasketball() {
                System.out.println("Let's talk about the money, let James play basketball");
                james.playBasketball();
            }
    
            @Override
            public void endorsement() {
                System.out.println("Let's talk about the money, let James endorsement");
                james.playBasketball();
            }
        }
    
    
        public static void main(String[] args) {
            Middleman middleman = new Middleman(new James());
            middleman.playBasketball();
            middleman.endorsement();
        }

    执行输出:

    Let's talk about the money, let James play basketball
    James play basketball
    Let's talk about the money, let James endorsement
    James play basketball

     

     

     

    3. 动态代理

     

    • 3.1 概念

    代理类在程序运行时创建的代理被成为动态代理。 我们上面静态代理的例子中,代理类(Middleman)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的"指示"动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 

     

    • 3.2 "java.lang.reflect.Proxy"类

    现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5之后提供了一个"java.lang.reflect.Proxy"类,通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象,如下所示:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    newProxyInstance()方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

     

    • 3.3 例子
        private interface IProxy {
    
            void playBasketball();
    
            void endorsement();
        }
    
        private static final class James implements IProxy {
    
            @Override
            public void playBasketball() {
                System.out.println("James play basketball");
            }
    
            @Override
            public void endorsement() {
                System.out.println("James endorsement");
            }
        }
    
        private static final class MiddlemanProxy {
    
            private IProxy james = new James();
    
            IProxy getProxy() {
                return (IProxy) Proxy.newProxyInstance(James.class.getClassLoader(), james.getClass().getInterfaces(),
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                if (method.getName().equals("playBasketball")) {
                                    System.out.println("Let's talk about the money, let James play basketball");
                                    return method.invoke(james, args);
                                } else if (method.getName().equals("endorsement")) {
                                    System.out.println("Let's talk about the money, let James endorsement");
                                    return method.invoke(james, args);
                                }
                                return null;
                            }
                        });
            }
        }
    
    
        public static void main(String[] args) {
            MiddlemanProxy middlemanProxy = new MiddlemanProxy();
            IProxy proxy = middlemanProxy.getProxy();
            proxy.playBasketball();
            proxy.endorsement();
        }

    执行输出:

    Let's talk about the money, let James play basketball
    James play basketball
    Let's talk about the money, let James endorsement
    James endorsement

    可以看到,效果与静态代理一样,后面将分析动态代理的实现原理。

     

     

     

    4. 代理模式的优缺点

    优点:

    • 代理模式可以将代理对象和真实被调用的目标对象隔离。
    • 一定程度上降低了系统耦合度,扩展性好。
    • 保护目标对象。
    • 增强目标对象。

    缺点:

    • 代理模式会造成系统设计中类的数量增加。
    • 在客户端和目标对象中间增加一个代理对象,会造成请求处理速度变慢。
    • 增加系统的复杂度。

     

     

     

    5. 代理模式的使用场景

    • 1.远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
    • 2.虚拟(Virtual)代理:懒加载,根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载,代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
    • 3.Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。 
    • 4.保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
    • 5.Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。 
    • 6.防火墙(Firewall)代理:保护目标,不让恶意用户接近。 
    • 7.同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。 
    • 8.智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

     

     

     

    6. 动态代理原理分析

    以Proxy.newProxyInstance()作为入口分析。

    @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            // InvocationHandler不能为空
            Objects.requireNonNull(h);
            // 将代理接口clone生成intfs
            final Class<?>[] intfs = interfaces.clone();
            // 获取系统的安全管理类
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                // 检查访问权限
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            // 关键的一句,获取代理类的Class对象
            Class<?> cl = getProxyClass0(loader, intfs);
    
            try {
                if (sm != null) {
                    // 校验权限
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
                // 获取代理类的构造器
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                // 生成代理对象
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

    可以看到,利用了反射的机制,关键一句:Class<?> cl = getProxyClass0(loader, intfs)

    private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            return proxyClassCache.get(loader, interfaces);
        }

    这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件是缓存在java虚拟机中的。

     

    我们可以来看看系统生成的代理类class是什么样的:

       public static void main(String[] args) {
            MiddlemanProxy middlemanProxy = new MiddlemanProxy();
            IProxy proxy = middlemanProxy.getProxy();
            proxy.playBasketball();
            proxy.endorsement();
    
            byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", James.class.getInterfaces());
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(new File("Proxy0.class"));
                fos.write(classFile);
                fos.flush();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    看看生成的Proxy0.class:

    public final class $Proxy0 extends Proxy implements IProxy {
        private static Method m1;
        private static Method m4;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void endorsement() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void playBasketball() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m4 = Class.forName("io.kzw.advance.csdn_blog.TestDynamicProxy$IProxy").getMethod("endorsement");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("io.kzw.advance.csdn_blog.TestDynamicProxy$IProxy").getMethod("playBasketball");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    可以看到上面JDK生成的代理类class,和静态代理类的结构差不多,只不过调用目标对象是通过反射的方式。

     

     

    展开全文
  • 本文主要从三个方面介绍代理模式,什么是代理模式,提供了什么好处;代理模式的三种实现方式;三种代理的区别 首先简单说明下为什么需要代理模式:为其他对象提供一种代理以控制对这个对象的访问,可以隔离客户端和...

    本文主要从三个方面介绍代理模式,什么是代理模式,提供了什么好处;代理模式的三种实现方式;三种代理的区别

    首先简单说明下为什么需要代理模式:为其他对象提供一种代理以控制对这个对象的访问,可以隔离客户端和委托类的中介。我们还可以借助代理来在增加一些功能,而不需要修改原有代码。

    重点是代理模式的三种实现方式:

    先给出简单的接口和实现类:

    public interface IHello {
        void sayHello();
    }
    public final class Hello implements IHello{
        @Override
        public void sayHello() {
            System.out.println("hello");
    
        }
    }

    1静态代理模式

    public class StaticProxy {
        IHello hello;
        public StaticProxy(IHello hello){
            this.hello=hello;
        }
        public void syaHello(){
            System.out.println("before");
            hello.sayHello();
            System.out.println("after");
        }
        public static void  main(String  args[]){
            new StaticProxy(new Hello()).syaHello();
        }
    }

    输出为:

    before
    hello
    after

    2java动态代理实现的动态代理

    //java动态代理实现的动态代理
    public class DynamicProxy implements InvocationHandler {
        Object target;
        public DynamicProxy(Object target){
            this.target=target;
        }
        // 此处生成接口的实现类,所以需要有接口,无法代理未实现接口的类
        public Object bind(){
            return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
                        this.target.getClass().getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            method.invoke(this.target,args);
            System.out.println("after");
            return null;
        }
    
        public static void  main(String  args[]){
            //此处返回的必定是接口,不可以转为具体实体类,所以java反射要求被代理类必须实现了接口;但不要求final
           IHello hello = (IHello) new DynamicProxy(new Hello()).bind();
           hello.sayHello();
        }
    
    }

    java动态代理实现代理步骤:

    a定义被代理类:接口及接口实现类

    b定义代理类,代理类构造器传入被代理类,且需要实现InvocationHandler接口的类并重写invoke方法

    c生成被代理的类的实例:调用 Proxy.newProxyInstance(被代理的类.getClass().getClassLoader(),

                                                                                            被代理的类.getClass().getInterfaces(),

                                                                                             InvocationHandler的实现类);

                  注意:newProxyInstance返回的是接口类型,所以java动态代理要求被代理类实现接口。

    d被代理的类的实例调用需要执行的方法

    3cglib实现的动态代理

    maven先引入包:

    <dependency>

        <groupId>cglib</groupId>

        <artifactId>cglib</artifactId>

        <version>2.2.2</version>

    </dependency>

    public class CGLibProxy {
        public static void  main(String  args[]){
            Enhancer enhancer = new Enhancer();
            // 此处将目标类设置为父类,生成该类的子类来实现动态代理,所以如果此时将Hello类声明为final,则会报IllegalArgumentException;但不要求实现接口
            enhancer.setSuperclass(Hello.class);
            enhancer.setCallback(new MethodInterceptor(){
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    System.out.println("before");
                    Object result = methodProxy.invokeSuper(o,objects);
                    System.out.println("after");
                    return result;
                }
            });
            Hello hello = (Hello)enhancer.create();
            hello.sayHello();
        }
    }

    Ehancer介绍:

    Enhancer
    Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多(如果对Proxy不懂,可以参考这里)。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。

    public class SampleClass {
        public String test(String input){
            return "hello world";
        }
    }

    下面我们将以这个类作为主要的测试类,来测试调用各种方法

    @Test
    public void testFixedValue(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "Hello cglib";
            }
        });
        SampleClass proxy = (SampleClass) enhancer.create();
        System.out.println(proxy.test(null)); //拦截test,输出Hello cglib
        System.out.println(proxy.toString()); 
        System.out.println(proxy.getClass());
        System.out.println(proxy.hashCode());
    }

    程序的输出为:

    Hello cglib
    Hello cglib
    class com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7

    java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

        at com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7.hashCode(<generated>)
        ...

    上述代码中,FixedValue用来对所有拦截的方法返回相同的值,从输出我们可以看出来,Enhancer对非final方法test()、toString()、hashCode()进行了拦截,没有对getClass进行拦截。由于hashCode()方法需要返回一个Number,但是我们返回的是一个String,这解释了上面的程序中为什么会抛出异常。

    Enhancer.setSuperclass用来设置父类型,从toString方法可以看出,使用CGLIB生成的类为被代理类的一个子类,形如:SampleClass$$EnhancerByCGLIB$$e3ea9b7

    Enhancer.create(Object…)方法是用来创建增强对象的,其提供了很多不同参数的方法用来匹配被增强类的不同构造方法。(虽然类的构造放法只是Java字节码层面的函数,但是Enhancer却不能对其进行操作。Enhancer同样不能操作static或者final类)。我们也可以先使用Enhancer.createClass()来创建字节码(.class),然后用字节码动态的生成增强后的对象。
     

    原理上:

    Java动态代理通过创建接口的实现类来完成对目标对象的代理,使用Java原生的反射API进行操作,在生成类上比较高效;但不能代理未实现接口的类;

    CGLIB 在运行期间生成的是目标类扩展的子类,直接使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效;不能扩展final修饰的类或方法;

    使用上:

    Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为ProxyJava类继承机制不允许多重继承);CGLIB能够代理普通类,但是不能代理final修改时的类;

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 
    2
    、如果目标对象实现了接口,可以强制使用CGLIB实现AOP ,spring中配置proxy-target-class='true'
    3
    、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    借鉴:https://blog.csdn.net/danchu/article/details/70238002?utm_source=copy 

    性能对比:

    关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。

    主要体现在如下的两个指标中:

    CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

    但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

    因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
    原文链接:https://blog.csdn.net/qq_35190492/article/details/103795708


    什么时候应该使用动态代理?

    aop就是一个很好的例子,我执行所有方法都需要记录日志,是不是所有方法都需要修改增加记录日志的逻辑?使用动态代理就不需要,
    调用方法需要调用代理,代理除了调用方法会增加日志的逻辑,那么所有原方法就无须修改!!

    spring的动态代理是怎么实现的? 没有继承接口为什么也可以?
    若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
    优点:因为有接口,所以使系统更加松耦合
    缺点:为每一个目标类创建接口
    若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
    优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

    展开全文
  • 两万+的吐血总结。代理模式(Proxy Pattern)是一个使用频率非常高的设计模式,其定义如下:Provide a surrogate or placeholder for another object to ...以及结合spring和jdk源码分析aop原理,手写jdk动态代理方式。

    一、代理模式

    熟悉代理模式的可以直接点击目录第二章,jdk动态代理实现原理,本文的精髓所在,通过这个例子,教大家如何去学习源码。

    1. 定义

    代理模式(Proxy Pattern) 是一个使用频率非常高的设计模式,其定义如下:

    Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

    2. 示例

    (1)静态代理

    • 游戏者接口

      /**
       * 游戏者接口
       */
      public interface IGamePlayer {
          /**
           * 登录游戏
           *
           * @param user     用户名
           * @param password 用户密码
           */
          void login(String user, String password);
      
          /**
           * 玩游戏
           */
          void play();
      }
      
    • 游戏者

      public class GamePlayer implements IGamePlayer {
      
         private String name = "";
      
         public GamePlayer(String name) {
             this.name = name;
         }
      
         /**
          * 登录
          */
         @Override
         public void login(String user, String password) {
             System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");
         }
      
         /**
          * 玩游戏
          */
         @Override
         public void play() {
             System.out.println(this.name + "的账号正在进行游戏");
         }
      }
      
      
    • 代练者

      /**
      * 代练类,负责帮助玩家代练,按时计费
      */
      public class GamePlayerProxy implements IGamePlayer {
         /**
          * 代练对象
          */
         private IGamePlayer gamePlayer = null;
      
         /**
          * 代练一小时价格
          */
         private int PER_HUOR_COST = 5;
      
         /**
          * 通过构造方法传入需要代练服务的对象
          */
         public GamePlayerProxy(IGamePlayer gamePlayer) {
             this.gamePlayer = gamePlayer;
         }
      
         @Override
         public void login(String user, String password) {
             System.out.println("代练开始登录账号");
             this.gamePlayer.login(user, password);
         }
      
         @Override
         public void play() {
             long strTime = System.currentTimeMillis();
             Date date = new Date(strTime);
             Calendar calendar = Calendar.getInstance();
             calendar.setTime(date);
             System.out.println("代练开始时间是" + date);
             this.gamePlayer.play();
             long endTime = System.currentTimeMillis();
             int costTime = (int) (endTime - strTime);
             //使用毫秒模拟小时,给开始时间增加消耗的毫秒数个小时
             calendar.add(Calendar.HOUR, costTime);
             System.out.println("代练结束时间是" + calendar.getTime());
             System.out.println("共计代练" + costTime + "小时,收费" + costTime * PER_HUOR_COST + "元。");
         }
      }
      
    • 场景类

      public class Client {
         public static void main(String[] args) {
             //定义一个痴迷的玩家
             IGamePlayer player = new GamePlayer("张三");
             //然后再定义一个代练者
             IGamePlayer proxy = new GamePlayerProxy(player);
             //登陆账号
             proxy.login("zhangSan", "password");
             //开始代练
             proxy.play();
         }
      }
      
    • 测试结果
      静态代理测试结果

    (2)动态代理

    动态代理步骤:
    1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2.创建被代理的类以及接口
    3.通过Proxy的静态方法
    newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
    4.通过代理调用方法

    • Person接口

      public interface Person {
          void hello();
      }
      
    • ZhangSan实现类

      public class ZhangSan implements Person {
          @Override
          public void hello() {
              System.out.println("我是张三,大家好");
          }
      }
      
    • MyInvocationHandler

      public class MyInvocationHandler implements InvocationHandler {
          private ZhangSan zhangSan;
      
          public MyInvocationHandler(ZhangSan zhangSan) {
              this.zhangSan = zhangSan;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              before();
              Object o = method.invoke(zhangSan, args);
              after();
              return o;
          }
      
          public void before() {
              System.out.println("动态代理前置处理");
          }
      
          public void after() {
              System.out.println("动态代理后置处理");
          }
      
      }
      

    • Test类
      public class Test {
          public static void main(String[] args) {
              try {
      
                  Person person = (Person) Proxy.newProxyInstance(Test.class.getClassLoader(),
                          ZhangSan.class.getInterfaces(), new MyInvocationHandler(new ZhangSan()));
                  person.hello();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      
    • 测试结果
      动态代理测试结果

    3. 通用类图

    代理模式的通用类如图所示,如果不能理解也可以先看下面的示例,再返回来重新查看通用模型
    代理模式通用类图
    代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策
    略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理 模式可以提供非常好的访问控制。在一些著名开源软件中也经常见到它的身影,如Spring框架中的aop就是基于代理模式(更确切的说是动态代理)实现的。我们先看一下类图中三个组件的解释:

    - Subject抽象主题角色

    抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

    /**
     * 业务接口
     */
    public interface ISubject {
        /**
         * 定义一个方法
         */
        void request();
    }
    
    

    - RealSubject具体主题角色

    也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者,这个往往是我们需要关注的重点。

    /**
     * 实际业务对象
     */
    public class RealISubject implements ISubject {
        /**
         * 业务实现方法
         */
        @Override
        public void request() {
            //业务逻辑处理
        }
    }
    

    - Proxy代理主题角色

    也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制
    委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

    public class Proxy implements ISubject {
        /**
         * 被代理对象的引用
         */
        private ISubject subject = null;
    
        /**
         * 默认被代理对象
         */
        public Proxy() {
            this.subject = new Proxy();
        }
    
        /**
         * 传入被代理对象
         */
        public Proxy(ISubject subject) {
            this.subject = subject;
        }
    
        /**
         * 代理对象控制被代理对象行为,添加预处理和后置处理,如可以添加日志打印,权限控制,事务管理
         */
        @Override
        public void request() {
            before();
            this.subject.request();
            after();
        }
    
        public void before() {
            //预处理
        }
    
        public void after() {
            //后置处理
        }
    }
    
    

    4. 代理模式的优点

    • 职责清晰
      主题角色(被代理对象)就是实现实际的业务逻辑,不用关心其他非本职责的逻辑处理,如日志,事务的开启、关闭、回滚等,就可以通过后期代理来实现。在spring中常说的AOP(面向切面编程)的思想就是将代码进行横向切分,通过预编译方式和运行期间动态代理实现程序功能的统一维护。它可以将模块划分的更加细致,减少各个模块和公用模块之间的耦合,让我们将关注点转移到业务本身。
    • 高扩展性
      具体主题角色(被代理对象)是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。在这种情况下,我们的代理类只需直接调用真实对象的业务方法即可,我们只需要关心流程控制,和一些其他的逻辑。如我们在使用业务代码前,进行权限验证,如进入业务代码前,记录此次调用,将调用记录(如时间,调用方法,调用地点)写入数据库来方便后台监控用户的行为。同样的,我们在主题角色(被代理对象)中只需专注于业务逻辑变更即可。

    二、jdk动态代理实现原理

    1. jdk动态代理源码分析(通过该示例学会阅读源码的方法)

    以之前的动态代理 代码为例,我们阅读jdk动态代理源码分析其实现原理。在阅读源码时我们最重要的是找对切入点,在这段代码中,没有复杂的逻辑,很明显,我们以newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)这个方法为切入点。我们在代码中着重观察我们的代理类生成的路径,f3进入这个方法。这里如果不懂或者暂时不感兴趣可直接查看下一部分和总结部分查看结论。推荐大家最好可以跟着自己走一遍。大家也可以查看我的另一篇文章ArrayList核心源码分析-扩容机制(jdk1.8)来练习源码阅读能力。

    1. newProxyInstance:Proxy.java(java.lang.reflect)
      在这个方法中,找到关键代码,这一行代码返回代理类的class文件,下面再通过反射创造实例返回。(我这里因为篇幅原因省去了这些类的具体代码,如果看起来有点晕,强烈建议自己动手试试,或者直接查看结论)

      /*
      * Look up or generate the designated proxy class.
      * 查找或生成指定的代理类。
      */
      Class<?> cl = getProxyClass0(loader, intfs);
      

      再次点击f3进入getProxyClass0方法

    2. getProxyClass0:Proxy (java.lang.reflect)
      这个方法非常简单,我们观察它的返回值

      // If the proxy class defined by the given loader implementing
      // the given interfaces exists, this will simply return the cached copy;
      // otherwise, it will create the proxy class via the ProxyClassFactory
      //如果存在由实现了给定接口的给定加载器定义的代理类,则将仅返回缓存的副本;
      //否则,它将通过ProxyClassFactory创建代理类。在java中是采用了享元模式存储了已经生成的代理类的class文件
      return proxyClassCache.get(loader, interfaces);
      

      再次f3进入get方法

    3. get:WeakCache (java.lang.reflect)
      我们可以观察到最终总是返回value

        while (true) {
                  if (supplier != null) {
                      // supplier might be a Factory or a CacheValue<V> instance
                      V value = supplier.get();
                      if (value != null) {
                          return value;
                      }
                  }
         ...
      

      f3进入get方法

      public interface Supplier<T> {
      
          /**
           * Gets a result.
           *
           * @return a result
           */
          T get();
      }
      

      我们走到这里无法确定到底调用的是那个实现类,此时可以在上一层V value = supplier.get();处打上断点。然后debug运行Test类,可以看到停到断点处。
      断点运行
      我们可以观察到调用的是WeakCache$Factory类的方法。找到该类的这个方法
      在这里插入图片描述

    4. get:WeakCache$Factory (java.lang.reflect)即WeakCache的内部类Factory
      观察该方法,我们发现最终return value,再细看有一行create value,发现这一行关键代码,==在这行打上断点。==我们每次进行类的跳转如果不是很清楚就可以在每次跳转前打上断点。

      value = Objects.requireNonNull(valueFactory.apply(key, parameter));
      

      这段代码requireNonNull()做一个非空检查,核心是valueFactory.apply(key, parameter),再次f3进入该方法在这里插入图片描述
      可以看到这是一个接口,我们无法判断是那个实现类,我们继续使用上次的方法,查看到底调用的是哪个实现类,在debug中f6,走到我们上一步中打下的断点。
      在这里插入图片描述
      我们可以看到代理类的class文件从这个方法中生成,我们观察debug窗口变量监控
      在这里插入图片描述
      可以看到发现我们调用的是Proxy$ProxyClassFactory的apply()方法。找到该类的该方法
      在这里插入图片描述

    5. apply:Proxy$ProxyClassFactory (java.lang.reflect)

      我们观察到该方法中有一行代码,生成指定的proxy class,这就是我们要找的东西啦。
      在这里插入图片描述
      我们可以看到该方法生成class文件的byte流,我们再进入方法,可以观察到其实是在使用文件写入的方式,动态写入java文件,然后编译java文件生成class文件,最后将其转换为byte流返回。其实动态代理和静态代理的区别就是:静态是在运行程序时已经生成了class文件并且加载进了jvm。我们看到下一行defineClass0()其实就是将该class文件动态的加载到jvm中,显然动态代理就是运行时加载代理类的class。我们会在第三部分会自己手写实现这个gennerate方法,所以不在重复。可以从第三部分中获得更多的细节。

    2.jdk动态代理生成的代理类的源码

    改变之前的Test测试类代码如下

    public class Test {
        public static void main(String[] args) {
            try {
                Person person = (Person) Proxy.newProxyInstance(Test.class.getClassLoader(),
                        ZhangSan.class.getInterfaces(), new MyInvocationHandler(new ZhangSan()));
                //之前源码分析中的关键方法
                byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
                //在哪里存储生成的class文件,因为idea中有反编译插件,所以我们可以看到该java文件源码
                FileOutputStream f = new FileOutputStream("src/main/java/com/jdk_proxy/$Proxy0.class");
                f.write(bytes);
                person.hello();
                System.out.println(person.getClass());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    生成的class文件如下,注意看注释行

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.jdk_proxy.Person;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    //可以看到该类实现了newProxyInstance:Proxy.java(java.lang.reflect)这个方法传入的接口参数,继承了Proxy类
    public final class $Proxy0 extends Proxy implements Person {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    	//我们可以看到它会传递InvocationHandler至父类Proxy
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    	
    	//这里就是我们的代理类重写接口中的方法,也是我们最终在调用代理类中的方法时所真正调用的方法
        public final void hello() throws  {
            try {
            	//注意这里的三个参数,我们可以看到他将this和在staic块中反射生成的接口中的方法,和调用代理传入的参数
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    	//在加载时通过反射从实现的接口中获得接口中的Method对象。
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("com.jdk_proxy.Person").getMethod("hello");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    

    Proxy类

    public class Proxy implements java.io.Serializable {
    	...
    	
        /**
         * the invocation handler for this proxy instance.
         * @serial
         */
        protected InvocationHandler h;
        
        protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }
    	...
    

    InvocationHandler类

    public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    

    3.总结

    • 动态代理实现过程(结合上面的class文件和我们示例程序)
    1. 通过实现 InvocationHandler 接口,自定义调用处理器;
    2. 通过调用 newProxyInstance:Proxy.java(java.lang.reflect) 指定ClassLoader对象和一组 interface 来创建动态代理类;
    3. 通过newProxyInstance传入的接口来动态的生成代理类java文件(下一部分有自己实现代码,可以详细了解原理),然后进行编译,最后加载到jvm中。代理类中的所有方法都调用了newProxyInstance:Proxy.java(java.lang.reflect) 中传入的InvocationHandler的invoke方法。也就是说我们的代理类重写了接口中的所有方法,然后再这些方法中只做了一件事,调用invoke:InvocationHandler
    4. 通过反射创建代理类对象返回。
    • 源码分析总结
      我们将断点打ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});这个方法上,debug运行
      在这里插入图片描述
      生成Java文件,编译成class文件,然后加载到jvm中,整个调用链如下。在学习时如果上面源码分析部分有点晕可以利用这种方法,快速定位代码。
      在这里插入图片描述

    三、手写实现jdk动态代理

    • Generator

      public class Generator {
          static String rt = "\r\n";
      
          public static Class<?> generator(Class<?>[] interfaces, MyClassLoader loader, MyInvocationHandler h) throws Exception {
              try {
                  // 1.创建代理类java源码文件,写入到硬盘中..
                  Method[] methods = interfaces[0].getMethods();
                  String name = interfaces[0].getName();
                  String packageName = name.substring(0, name.lastIndexOf("."));
                  StringBuilder stringBuilder = new StringBuilder();
                  //包名
                  stringBuilder.append("package ");
                  stringBuilder.append(packageName);
                  stringBuilder.append(";");
                  stringBuilder.append(rt);
                  //导入的类
                  stringBuilder.append("import java.lang.reflect.Method;");
                  stringBuilder.append(rt);
                  stringBuilder.append("import com.proxy_design.*;");
                  stringBuilder.append(rt);
                  //类的声明
                  stringBuilder.append("public class Proxy0 extends MyProxy implements ");
                  stringBuilder.append(interfaces[0].getName());
                  stringBuilder.append("{");
                  stringBuilder.append(rt);
                  //构造方法
                  stringBuilder.append("\tpublic Proxy0(MyInvocationHandler h){");
                  stringBuilder.append(rt);
                  stringBuilder.append("\t\tsuper(h);");
                  stringBuilder.append(rt);
                  stringBuilder.append("\t}");
                  stringBuilder.append(rt);
                  //添加重写后的方法,在所有方法中调用super.h.invoke方法即可
                  stringBuilder.append(getMethodString(methods, interfaces[0]));
                  stringBuilder.append(rt);
                  stringBuilder.append("}");
                  stringBuilder.append(rt);
                  String proxyClass = stringBuilder.toString();
                  // 2. 将代理类源码文件写入硬盘中,根据自己的目录输入
                  String filename = "src/main/java/"
                          + packageName.replace(".", "/") + "/Proxy0.java";
                  File f = new File(filename);
                  FileWriter fw = new FileWriter(f);
                  fw.write(proxyClass);
                  fw.flush();
                  fw.close();
                  // 3.使用JavaJavaCompiler 编译该Proxy0源代码 获取class文件
                  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                  StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
                  Iterable units = fileMgr.getJavaFileObjects(filename);
                  JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
                  t.call();
                  fileMgr.close();
                  return loader.findClass("Proxy0");
              } catch (Exception e) {
                  e.printStackTrace();
              }
              throw new ClassNotFoundException();
          }
      
      
          public static String getMethodString(Method[] methods, Class intf) {
              StringBuilder proxyMe = new StringBuilder();
              for (int i = 0; i < methods.length; i++) {
                  proxyMe.append("\tprivate static Method m").append(i).append(";").append(rt);
              }
      
              for (int i = 0; i < methods.length; i++) {
                  proxyMe.append("\tpublic void ");
                  proxyMe.append(methods[i].getName());
                  proxyMe.append("(){");
                  proxyMe.append(rt);
                  proxyMe.append("\t\ttry {");
                  proxyMe.append(rt);
                  proxyMe.append("\t\t\tsuper.h.invoke(this,m");
                  proxyMe.append(i);
                  proxyMe.append(",null);");
                  proxyMe.append(rt);
                  proxyMe.append("\t\t} catch (Throwable throwable) {");
                  proxyMe.append(rt);
                  proxyMe.append("\t\t\tthrowable.printStackTrace();");
                  proxyMe.append(rt);
                  proxyMe.append("\t\t}");
                  proxyMe.append(rt);
                  proxyMe.append("\t}");
                  proxyMe.append(rt);
      
              }
              //从接口中反射获得所有方法
              proxyMe.append("\tstatic {");
              proxyMe.append(rt);
              proxyMe.append("\t\ttry{");
              proxyMe.append(rt);
              for (int i = 0; i < methods.length; i++) {
                  proxyMe.append("\t\t\tm");
                  proxyMe.append(i);
                  proxyMe.append("=");
                  proxyMe.append(intf.getName());
                  proxyMe.append(".class.getMethod(\"");
                  proxyMe.append(methods[i].getName());
                  proxyMe.append("\",new Class[]{});");
                  proxyMe.append(rt);
              }
              proxyMe.append("\t\t} catch (NoSuchMethodException var2) {");
              proxyMe.append(rt);
              proxyMe.append("\t\t\tthrow new NoSuchMethodError(var2.getMessage());");
              proxyMe.append(rt);
              proxyMe.append("\t\t}");
              proxyMe.append(rt);
              proxyMe.append("\t}");
              return proxyMe.toString();
          }
      }
      
      
    • MyClassLoader

      public class MyClassLoader extends ClassLoader {
      @Override
      protected Class<?> findClass(String name) throws ClassNotFoundException {
          //class文件将生成的位置
          File file = new File("src/main/java/com/proxy_design/Proxy0.class");
          if (file.exists()) {
              try {
                  //将文件转换为byte流
                  FileInputStream fis = new FileInputStream(file);
                  ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
                  byte[] b = new byte[1000];
                  int n;
                  while ((n = fis.read(b)) != -1) {
                      bos.write(b, 0, n);
                  }
                  fis.close();
                  bos.close();
                  byte[] buffer = bos.toByteArray();
                  //加载类返回类,此时静态块中会被调用
                  return defineClass("com.proxy_design." + name, buffer, 0, buffer.length);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return super.findClass(name);
      }
      

    }
    ```

    • MyInvocationHandler

      public interface MyInvocationHandler {
          public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable;
      
      }
      
    • MyInvocationHandlerImp

      public class MyInvocationHandlerImp implements MyInvocationHandler {
      
          private ZhangSan zhangSan;
      
      
          public MyInvocationHandlerImp(ZhangSan zhangSan) {
              this.zhangSan = zhangSan;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("动态代理前置处理");
              //调用被代理对象的真实方法
              Object o = method.invoke(zhangSan, args);
              System.out.println("动态代理后置处理");
              return o;
          }
      }
      
      
    • MyProxy

      public class MyProxy {
          /**
           * 在使用动态代理生成的proxy类中,他会继承本类,然后在构造方法中传入h
           */
          protected MyInvocationHandler h;
      
          public MyProxy(MyInvocationHandler h) {
              this.h = h;
          }
      
          public static Object newProxyInstance(MyClassLoader loader,
                                                Class<?>[] interfaces,
                                                MyInvocationHandler h) throws Exception {
              Class<?> proxy = Generator.generator(interfaces, loader, h);
              Constructor<?> proxyConstructor = proxy.getConstructor(MyInvocationHandler.class);
              return proxyConstructor.newInstance(h);
          }
      }
      
    • Test

      public class Test {
          public static void main(String[] args) {
              try {
                  Person proxyMapper = (Person) MyProxy.newProxyInstance(new MyClassLoader(),
                          ZhangSan.class.getInterfaces(), new MyInvocationHandlerImp(new ZhangSan())
                  );
                  proxyMapper.hello();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      
    • Person、ZhangSan和上面一样。(动态代理示例

    • 生成的Proxy类源码

      public class Proxy0 extends MyProxy implements com.proxy_design.Person{
      	public Proxy0(MyInvocationHandler h){
      		super(h);
      	}
      	private static Method m0;
      	//实际生效方法
      	public void hello(){
      		try {
      			super.h.invoke(this,m0,null);
      		} catch (Throwable throwable) {
      			throwable.printStackTrace();
      		}
      	}
      	static {
      		try{
      			m0=com.proxy_design.Person.class.getMethod("hello",new Class[]{});
      		} catch (NoSuchMethodException var2) {
      			throw new NoSuchMethodError(var2.getMessage());
      		}
      	}
      }
      
    展开全文
  • 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。 好处:可以不用动原来类的逻辑,...

    控制对象访问权限

    • 概念
    • 类图
    • 代码实例
    • 和装饰者区别

    概念

    为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。

    好处:可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。真正的业务还是交给被代理对象处理的,因此在其委托被代理对象处理业务前后实现一些公共逻辑服务,例如加入缓存或日志等功能,无须修改原来的类就可以使用代理进行实现。

    类图

    在这里插入图片描述

    Subject: 代理类和被代理类实现同样的接口

    Proxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理

    RealSubject:被代理类,可以在其内做一些访问权限控制,额外的业务处理

    Client:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性

    代码实例

    UserDAO 代理和被代理的公共的接口(Subject)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:47 AM
     * @description 用户数据持久化逻辑接口
     **/
    public interface UserDAO {
    
        boolean insert(String name,int age);
    }
    

    UserDAOImpl 被代理类(RealSubject)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:47 AM
     * @description 用户数据持久具体实现
     **/
    public class UserDAOImpl implements UserDAO {
    
        @Override
        public boolean insert(String name, int age) {
            System.out.println("insert to database name="+name +" age="+age);
            return true;
        }
    }
    

    UserDAOProxyByInterface 代理类,通过实现接口方式实现代理方式(Proxy)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:50 AM
     * @description 用户持久数据的代理类(和被代理类实现同个接口方式)
     **/
    public class UserDAOProxyByInterface implements UserDAO {
        private UserDAOImpl conreteUserDAO;
    
        public UserDAOProxyByInterface() {
    
        }
        public UserDAOProxyByInterface(UserDAOImpl conreteUserDAO) {
            this.conreteUserDAO = conreteUserDAO;
        }
        @Override
        public boolean insert(String name, int age) {
            System.out.println("before insert handle some logic by interface");
            return conreteUserDAO.insert(name, age);
        }
    }
    

    UserDAOProxyByExtend 代理类,通过继承被代理类实现的代理方式(Proxy)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:50 AM
     * @description 用户持久数据的代理类(继承被代理类方式)
     **/
    public class UserDAOProxyByExtend extends UserDAOImpl {
        private UserDAOImpl conreteUserDAO;
    
        public UserDAOProxyByExtend() {
    
        }
        public UserDAOProxyByExtend(UserDAOImpl conreteUserDAO) {
            this.conreteUserDAO = conreteUserDAO;
        }
        @Override
        public boolean insert(String name, int age) {
            System.out.println("before insert handle some logic by extend");
            return conreteUserDAO.insert(name, age);
        }
    }
    

    Client 获取代理对象客户端(Client)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:52 AM
     * @description
     **/
    public class Client {
        public static void main(String[] args) {
            UserDAOImpl conreteUserDAO = new UserDAOImpl();
            UserDAOProxyByInterface userDAOProxyByInterface = new UserDAOProxyByInterface(conreteUserDAO);
            //和被代理类实现同个接口方式进行代理
            userDAOProxyByInterface.insert("dynamo", 18);
    
            //通过继承被代理类方式进行代理
            UserDAOProxyByExtend userDAOProxyByExtend = new UserDAOProxyByExtend(conreteUserDAO);
            userDAOProxyByExtend.insert("dynamo", 18);
    
        }
    }
    

    输出结果:

    before insert handle some logic by interface
    insert to database name=dynamo age=18
    before insert handle some logic by extend
    insert to database name=dynamo age=18
    

    和装饰者模式区别

    代理模式关注的是控制对对象的访问,一般具体的被代理对象对Client是不可见的,直接在创建代理对象时就创建了被代理对象,也就是编译时就确定了。 但是装饰者模式:更关注的是在一个对象方法上动态添加修饰逻辑,但是这些逻辑修饰在运行时才能确定的,通过构造方法传入具体的装饰者对象,执行时递归调用修饰对象的方法。

    动态代理

    静态代理和动态代理:

    1、静态代理:代理类由程序员创建的然后编译成.class文件。但是其中缺点是,具有重复代码,灵活性不好,例如在执行接口A中所有方法之前加上日志逻辑,那么使用静态代理的话,在代理类中每个方法都得加,如果我想add* 开头方法加上一种逻辑,select* 开头方法加上另一种逻辑,那么就很难去实现和维护了,想解决以上困惑就要使用动态代理了。

    2、动态代理:是在运行的时候,通过jvm中的反射进行动态创建对象,生成字节码对象(构造方法参数 InvocationHandler h类型),传入由我们实现InvocationHandler接口的对象,通过反射创建代理对象。 然后当调用代理对象的任何方法都会调用h中的 invoke(Object proxy,Method method,Object[] args)传入当前代理对象、当前调用的方法、方法参数值。

    JDK动态代理实现原理

    要求被代理类必须要实现接口,因为JDK动代理实现是通过实现接口方式来实现的。需要实现 InvocationHandler接口(在invoke方法中实现一些额外的逻辑,添加一些新功能),通过Proxy.newProxyInstance(ClassLoader classLoader,Class<?> interfaces,InvocationHandler h)。

    实现步骤

    1.定义一个接口ProxyObj

    2.编写该接口I的实现类ProxyObjImpl

    3.编写InvocationHandler接口的实现类InvokcationInvokeHandler,构造h类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。

    4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
    、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。

    这样就生成了Proxy0Proxy0类的对象,由于Proxy0类实现了ProxyObj接口,所以可以将对象强制转型成ProxyObj。

    再说一下Proxy.newProxyInstance方法的实际过程:

    1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。

    2.运行时生成代理Class,即$Proxy0

    3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。

    实现代码

    ProxyObj 代理类和被代理类实现的公共接口

    public interface ProxyObj {
    
     public  void setName(String name);
    }
    

    ProxyObjImpl 被代理类

    public class ProxyObjImpl implements  ProxyObj,ProxyObj1{
    
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        System.out.println("set name="+name);
        this.name = name;
      }
    
      @Override
      public void setAge(Integer age) {
        System.out.println("set age="+age);
      }
    }
    

    InvokcationInvokeHandler 实现InvocationHandler接口

    public class InvokcationInvokeHandler implements InvocationHandler {
        //真实的对象
        private ProxyObj proxyPbj;
    
        public InvokcationInvokeHandler(ProxyObj proxy) {
            this.proxyPbj = proxy;
        }
    
        /**
         *
         * @param proxy jvm生成的动态代理对象 $Proxy0
         * @param method 当前代理对象调用的方法对象,是代理对象实现接口中的方法
         * @param args 调用对象传入的参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before call set proxy" + proxy.getClass()+" method="+method+" args="+ Arrays.toString(args));
            method.invoke(proxyPbj, args);
            //((ProxyObj)proxy).setName("2"); 会导致invoke方法循环调用导致StackoverflowError
            return null;
        }
    }
    

    ProxyFactory 获取代理对象的工厂

    public class ProxyFactory {
    
      //获取代理对象
      public static ProxyObj getProxy(ProxyObj proxyObj) {
        /**
         * loader 指定加载jvm运行时动态生成的代理对象的加载器
         *
         * interface 真实对象实现的所有接口
         *
         * h 实现InvocationHandler接口对象
         */
        return (ProxyObj) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
            proxyObj.getClass().getInterfaces(), new InvokcationInvokeHandler(proxyObj));
      }
    
      public static void main(String[] args) {
        ProxyFactory.getProxy(new ProxyObjImpl()).setName("hh");
      }
    }
    

    输出结果

    before call set proxyclass com.sun.proxy.$Proxy0 method=public abstract void com.dynamo.proxy.dynamicproxy.jdk.ProxyObj.setName(java.lang.String) args=[hh]
    set name=hh
    

    流程图:

    在这里插入图片描述

    通过javap可以看到具体代理类为:

    import java.lang.reflect.InvocationHandler;   
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;   
    import java.lang.reflect.UndeclaredThrowableException;  
       
    public final class $Proxy0 extends Proxy implements UserManager {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
                m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",  
                        new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                        .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public void addUser() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
      
        }  
    } 
    
    

    为什么无论调用JDK的动态代理对象还是CGLIB的代理对象的toString方法会出现栈溢出?

    通过阅读JDK中代理类的源码 $Proxy0 extends Proxy implements I1,I2 看到,其中该代理类通过反射获取到这么几种Method对象, Object类中的方法对象:m2 = Class.forName(“java.lang.Object”).getMethod(“toString”,new Class[0]);
    除了toString,还有 equals 和 hashCode方法。

    还有在Proxy.newProxInstance()传入的Class[] interfaces 所有接口中的方法。当这些方法调用的时候都会触发父类Proxy中的InvocationHandler对象中的invoke方法

    ((String)super.h.invoke(this, m2, null));

    所以如果在 实现InvocationHandler对象中的invoke方法中 调用代理对象中的 toString hashCode equals 和 其中被代理类的方法时都会导致栈溢出。
    从上述的流程和代码可以看到如果调用代理类中的toString equals hashCode 和实现被代理类方法时都会导致方法栈溢出。

    CGLIB动态代理实现原理

    CGLIB(Code Generation Library)实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。所以被代理类一定不能定义为final class并且对于final 方法不能被代理。

    实现步骤

    Ehancer enhancer = new Enhancer() //Enhancer为字节码增强器,很方便对类进行扩展
    enhancer.setSuperClass(被代理类.class);
    enhancer.setCallback(实现MethodInterceptor接口的对象)
    enhancer.create()//返回代理对象,是被代理类的子类
    

    MethodInterceptor接口的intercept方法

    /**
    *obj 代理对象
    *method 委托类方法
    *arg 方法参数
    *MethodProxy 代理方法MethodProxy对象,每个方法都会对应有这样一个对象 
    *通过methodProxy.invokeSuper(obj,arg)方法调用委托类的方法
    */
    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
    

    实现代码

    Query 被代理类

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:12 AM
     * @description 被代理类
     **/
    public class Query {
    
        public boolean query(String name){
            System.out.println("Query query"+name);
            return true;
        }
    
        /**
         * cglib 代理通过继承被代理类,所有final方法不能被重写,所以定义的final方法不能被代理
         * @param name
         * @return
         */
        public final  boolean query1(String name){
            System.out.println("Query query1"+name);
            return true;
        }
    }
    

    CglibProxy 方法拦截器

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:04 AM
     * @description 目标方法拦截,在进行具体业务逻辑前后做一些额外逻辑
     **/
    public class CglibProxy implements MethodInterceptor {
    
        /**
         *cglib代理是通过继承被代理类的方式进行代理的
         *
         * @param o 目标实例对象(继承于传入的被代理类)
         * @param method 目标方法对象
         * @param args 方法参数
         * @param proxy 代理对象实例
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //o.toString o.equals o.hashCode方法调用都会导致方法栈溢出
            System.out.println("o="+o.getClass()+" method="+method+" args="+ Arrays.toString(args)+" proxy="+proxy);
            System.out.println("before call add extera logic");
            //调用父类(被代理类)的方法
            boolean result = ((Boolean) proxy.invokeSuper(o,args)).booleanValue();
            System.out.println("after call add extera logic");
            return result;
        }
    }
    

    CglibProxyFactory 产生代理对象的工厂

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:04 AM
     * @description
     **/
    public class CglibProxyFactory {
        private Enhancer enhancer = new Enhancer();
    private CglibProxy cglibProxy = new CglibProxy();
        public Object getProxyObject(Class cls) {
            enhancer.setSuperclass(cls);
            enhancer.setCallback(cglibProxy);
            return enhancer.create();
        }
    }
    

    Client 获取代理对象的Client

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:18 AM
     * @description
     **/
    public class Client {
        public static void main(String[] args) {
            CglibProxyFactory cglibProxyFactory = new CglibProxyFactory();
            Query query = (Query)cglibProxyFactory.getProxyObject(Query.class);
            query.query("dynamo");
            //该方法不能进过代理对象,因为query1方法定义为final,因此不能被代理类重写,因此直接调用被代理类的query1方法
            query.query1("dynamo1");
        }
    }
    

    流程图

    在这里插入图片描述

    cglib动态生成的代理类

    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06
      extends HelloServiceImpl
      implements Factory
    {
      private boolean CGLIB$BOUND;
      private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
      private static final Callback[] CGLIB$STATIC_CALLBACKS;
      private MethodInterceptor CGLIB$CALLBACK_0;
      private static final Method CGLIB$sayHello$0$Method;
      private static final MethodProxy CGLIB$sayHello$0$Proxy;
      private static final Object[] CGLIB$emptyArgs;
      private static final Method CGLIB$finalize$1$Method;
      private static final MethodProxy CGLIB$finalize$1$Proxy;
      private static final Method CGLIB$equals$2$Method;
      private static final MethodProxy CGLIB$equals$2$Proxy;
      private static final Method CGLIB$toString$3$Method;
      private static final MethodProxy CGLIB$toString$3$Proxy;
      private static final Method CGLIB$hashCode$4$Method;
      private static final MethodProxy CGLIB$hashCode$4$Proxy;
      private static final Method CGLIB$clone$5$Method;
      private static final MethodProxy CGLIB$clone$5$Proxy;
      
      static void CGLIB$STATICHOOK1()
      {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class localClass1 = Class.forName("proxy.HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06");
        Class localClass2;
        Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = tmp95_92[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
        Method[] tmp115_95 = tmp95_92;
        CGLIB$equals$2$Method = tmp115_95[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        Method[] tmp135_115 = tmp115_95;
        CGLIB$toString$3$Method = tmp135_115[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        Method[] tmp155_135 = tmp135_115;
        CGLIB$hashCode$4$Method = tmp155_135[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
        Method[] tmp175_155 = tmp155_135;
        CGLIB$clone$5$Method = tmp175_155[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        tmp175_155;
        Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "sayHello", "()V" }, (localClass2 = Class.forName("proxy.HelloServiceImpl")).getDeclaredMethods());
        CGLIB$sayHello$0$Method = tmp223_220[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "sayHello", "CGLIB$sayHello$0");
        tmp223_220;
        return;
      }
      
      final void CGLIB$sayHello$0()
      {
        super.sayHello();
      }
      
     	public final void sayHello()
    	  {
    	    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    	    if (tmp4_1 == null)
    	    {
    	      tmp4_1;
    	      CGLIB$BIND_CALLBACKS(this);
    	    }
    	    if (this.CGLIB$CALLBACK_0 != null) {
    	     tmp17_14.intercept(this, CGLIB$sayHello$3$Method, CGLIB$emptyArgs, CGLIB$sayHello$3$Proxy);
    	    }
    	    super.sayHello();
    	  }
      
      final void CGLIB$finalize$1()
        throws Throwable
      {
        super.finalize();
      }
      
      protected final void finalize()
        throws Throwable
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) {
          return;
        }
        super.finalize();
      }
      
      final boolean CGLIB$equals$2(Object paramObject)
      {
        return super.equals(paramObject);
      }
      
      public final boolean equals(Object paramObject)
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null)
        {
          Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$2$Method, new Object[] { paramObject }, CGLIB$equals$2$Proxy);
          tmp41_36;
          return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
        }
        return super.equals(paramObject);
      }
      
      final String CGLIB$toString$3()
      {
        return super.toString();
      }
      
      public final String toString()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null) {
          return (String)tmp17_14.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
        }
        return super.toString();
      }
      
      final int CGLIB$hashCode$4()
      {
        return super.hashCode();
      }
      
      public final int hashCode()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null)
        {
          Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
          tmp36_31;
          return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
        }
        return super.hashCode();
      }
      
      final Object CGLIB$clone$5()
        throws CloneNotSupportedException
      {
        return super.clone();
      }
      
      protected final Object clone()
        throws CloneNotSupportedException
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null) {
          return tmp17_14.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
        }
        return super.clone();
      }
      
      public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
      {
        String tmp4_1 = paramSignature.toString();
        switch (tmp4_1.hashCode())
        {
        case -1574182249: 
          if (tmp4_1.equals("finalize()V")) {
            return CGLIB$finalize$1$Proxy;
          }
          break;
        }
      }
      
      public HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06()
      {
        CGLIB$BIND_CALLBACKS(this);
      }
      
      public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
      }
      
      public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
      }
      
      private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
      {
        82ef2d06 local82ef2d06 = (82ef2d06)paramObject;
        if (!local82ef2d06.CGLIB$BOUND)
        {
          local82ef2d06.CGLIB$BOUND = true;
          Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
          if (tmp23_20 == null)
          {
            tmp23_20;
            CGLIB$STATIC_CALLBACKS;
          }
          local82ef2d06.CGLIB$CALLBACK_0 = (// INTERNAL ERROR //
    

    其中该代理会实现所有被代理类非final的方法,并且每个方法有两种形式:例如被代理类有sayHello()方法,那么代理类会有两个该方法

    final void CGLIB$sayHello$0()//该方法是通过在MethodInterceptor对象中methodProxy.invokeSuper(obj,args)进行调用的
      {
        super.sayHello();
      }
    
    public final void sayHello()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) {
         tmp17_14.intercept(this, CGLIB$sayHello$3$Method, CGLIB$emptyArgs, CGLIB$sayHello$3$Proxy);
        }
        super.sayHello();
      }
    

    一个是重写了父类的sayHello方法,如果callback不为null就会调用 其中实现MethodInterprector接口的对象的invoke方法。

    实现MethodInterceptor接口的实例的intercept方法

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //o.toString o.equals o.hashCode方法调用都会导致方法栈溢出
            System.out.println("o="+o.getClass()+" method="+method+" args="+ Arrays.toString(args)+" proxy="+proxy);
            System.out.println("before call add extera logic");
            //调用父类(被代理类)的方法
            boolean result = ((Boolean) proxy.invokeSuper(o,args)).booleanValue();
            System.out.println("after call add extera logic");
            return result;
        }
    

    代理类为每个委托方法都会生成两个方法
    在这里插入图片描述

    其中如果想要调用被代理类中的方法就需要使用 methodProxy.invokeSuper(obj,arg) 其中每个父类中非final的方法都会对应一个MethodProxy对象,下面看看其中该类中的invokeSuper方法:

    在这里插入图片描述

    和JDK动态代理最大区别就是,JDK是通过反射进行调用被代理对象的方法,而cglib是直接对被代理方法进行调用的。 通过类FastClassInfo存放了该被代理类和代理类对象,f1指向委托类对象,f2指向代理类对象,i1和i2分别代表着sayHello方法以及CGLIB$sayHello$0方法在类信息数组中的下标。其中

    在这里插入图片描述

    FastClass对Class对象进行特别的处理,比如将会用数组保存method的引用,每次调用方法的时候都是通过一个index下标来进行对类中某个方法调用。其中FastClassInfo中的f1、f2 i1、i2通过init方法进行初始化的,i1 和 i2是通过MethodProxy的getIndex方法就是通过方法签名来获得方法在存储了Class信息的数组中的下标。

    在这里插入图片描述

    从上面的源码分析可以看到invokeSuper方法调用实际上就是调用代理类的CGLIB$sayHello$0()方法然后该方法又调用其父类的sayHello方法。

    代理方式

    1、远程代理

    例如:客户端有个接口A 里面有多种方法,客户端想和通过接口A和服务端通讯,给服务端发送请求。客户端并不需要知道实现A接口具体实现,只管调用就行,和请求本地接口一样。 这里A接口的实现就可以看作访问服务端的代理。这个代理就帮你发送请求了,并获取结果吐给客户端。

    2、虚拟代理

    例如浏览器加载大图片时会很慢,加载文字就很快。所以通过虚拟代理进行优化,代理对象会记录下图片的url和尺寸,然后一并返回未加载出来图片,等到访问到该图片的位置再去请求该页面。这个时候就不会出现下面的加载很快的文字一直等待上面图片加载出来才显示。

    3、安全代理

    在访问具体对象的时候做一些权限的校验,这样更好的控制对象的访问。

    4、智能指引

    在访问真实对象时候做一些额外逻辑处理,例如记录对象访问次数和引用次数,该对象第一次访问加入缓存中,在访问一个实际对象前,检查是否已经锁定它。

    展开全文
  • 1:强制代理模式 普通代理模式和强制代理模式代理模式的其中俩个部分。对于这俩个代理模式,我们采用一个类似事情解释下:在网络上的代理服务器分透明代理和普通代理;对于透明代理来说,字如其意,就是这个代理...
  • <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> <...--Microsoft.Practices....
  • 秒懂Java代理与动态代理模式

    万次阅读 多人点赞 2018-06-30 17:08:23
    什么是动态代理模式?二者什么关系?具体如何实现?什么原理?如何改进?这即为我们学习一项新知识的正确打开方式,我们接下来会以此展开,让你秒懂。 概念 什么是代理模式 定义:为其他对象提供一种代理以控制...
  • 代理模式的使用总结

    万次阅读 多人点赞 2020-04-20 14:14:37
    一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理...
  • 动态代理是 AOP(Aspect Orient Programming)编程思想,理解动态代理原理,对学习AOP框架至关重要。 JDK动态代理不需要任何外部依赖,但是只能基于接口进行代理;CGLIB通过继承的方式进行代理,无论目标对象有没有...
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现...
  • 代理模式的目的 防止直接访问目标对象带来的复杂性 增强原有的业务逻辑 动态代理是不改变源码的基础上,对类中的某个方法进行增强,字节码随用随创建,随用随加载。 静态代理是字节码一上来就创建好,并且加载完成...
  • Java JDK 动态代理(AOP)使用及实现原理分析

    万次阅读 多人点赞 2019-05-08 21:28:06
    目录 ​ 一、什么是代理? 二、Java 动态代理类 三、JDK的动态代理怎么使用?...四、动态代理怎么实现的?...代理是一种常用的设计模式,其目的就是为其他...代理模式UML图: 简单结构示意图: 为了保持行为的...
  • 文章目录1 引子2 业务场景介绍3 静态代理模式4 装饰器模式5 动态代理模式 1 引子 看过上篇文章《【Mybatis源码探索】 — Mybatis查询过程核心源码解读 — 先聊聊selectOne方法》对Executor和StatementHandler的...
  • cglib动态代理实现原理详细分析

    千次阅读 2019-01-01 18:29:08
    而本文,将介绍另一种动态代理模式:cglib动态代理。阅读完本文,你将对cglib代理模式的运行的流程有一个清晰的认识。 本文的目录如下: 目录 一:cglib动态代理的样例展示 二:cglib生成的代理类的分析 三:cgli...
  • Carson带你学设计模式:动态代理模式(Proxy Pattern)

    千次阅读 多人点赞 2018-06-06 09:02:54
    手把手带你全面了解动态代理模式
  • 【Spring基础】JDK动态代理实现原理(jdk8)

    万次阅读 多人点赞 2018-06-05 21:32:47
    在了解JDK动态代理前,有需要可以了解下代理模式。 参考:https://blog.csdn.net/yhl_jxy/article/details/52679882; 天天的都听到人们说JDK动态代理,听上去感觉好屌的样子,为什么要叫JDK动态代理? ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 161,814
精华内容 64,725
关键字:

动态代理模式原理