zoukankan      html  css  js  c++  java
  • 四、单例模式之代理模式

    代理模式在生活中的应用常见随处可见,例如:快递员、中介、媒婆、黄牛等等。代理模式可以实现AOP,拦截器,代码解耦等功能。一般有3中实现方式:

    1. 静态代理
    2. JDK实现的动态代理(创建目标对象的所有接口的代理实现类)
    3. CGLIB实现的动态代理(创建目标对象的代理子类)

    实现代理模式,代理对象需要拿到目标对象的引用且能够调用目标对象的方法。当然也有些代理不需要目标对象的引用,例如Mybatis的Mapper代理对象就不需要目标对象,不过Mapper也没有具体实现的类。

    1.静态代理

    这种代理实现的功能比较单一,因为在代理运行前,目标对象是明确的(类型明确),同时也不易扩展,扩展时需要同时改造目标类和代理类,不遵守开闭原则。属于需要人工干预的代理,不具有智能自动化,一般很少使用。

    2.动态代理(JDK)

    动态代理在运行前,所有的东西都是未知的,只有到运行时才知道,容易扩展。属于智能自动化的代理,应用的比较广泛。

    使用JDK实现的动态代理代码:

    package com.kancy.pattern.proxy;
    
    import pattern.ApplePhone;
    import pattern.Phone;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKDynamicProxy<T> implements InvocationHandler {
        private T target;
        public JDKDynamicProxy() {
        }
        public JDKDynamicProxy(T target) {
            this.target = target;
        }
        public T getProxyObject(){
            return (T) Proxy.newProxyInstance(
                    target.getClass().getClassLoader() //加载动态生成的class到JVM
                    ,target.getClass().getInterfaces() //代理对象需要实现的接口
                    ,this // 代理对象具体代理实现,这里为了方便,使用当前类
            );
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理处理------------start");
            Object invoke = method.invoke(target, args);
            System.out.println("代理处理------------end");
            return invoke;
        }
    
        public static void main(String[] args) {
            // 手机Phone接口类型的代理
            JDKDynamicProxy<Phone> proxy = new JDKDynamicProxy(new ApplePhone());
            Phone proxyObject = proxy.getProxyObject();
            proxyObject.getName();
        }
    }

    原理解析:(字节码重组)

    1. 拿到目标对象的引用,并获取他的所有接口和类加载器
    2. 使用sun.misc.ProxyGenerator类生成一个实现了目标对象所有接口的实现类(java源码),并且动态编译成class字节码数组。
      1. 生成类的格式:目标类名.$Proxy{序号n}
    3. 使用目标对象的类加载器,把编译后的字节码加载到JVM使用。
    4. 删除代理创建过程中的产生的class文件。
    /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }

    3.动态代理(CGLIB)

    CGLIB实现的动态代理与JDK实现的动态代理,不同点在于CGLIB动态生成的是目标对象的子类,而JDK动态生成的是目标对象所有接口的实现类,其他原理基本类似。

    使用CGLIB实现的动态代理代码:

    package com.kancy.pattern.proxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import pattern.ApplePhone;
    
    import java.lang.reflect.Method;
    
    public class CGLIBDynamicProxy<T> implements MethodInterceptor {
        private T target;
        public CGLIBDynamicProxy() {
        }
        public CGLIBDynamicProxy(T target) {
            this.target = target;
        }
        public T getProxyObject(){
            Object proxy = Enhancer.create(target.getClass(), this);
            return (T) proxy;
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("代理处理------------start");
            Object invoke = method.invoke(target, objects);
            System.out.println("代理处理------------end");
            return invoke;
        }
        
        public static void main(String[] args) {
            ApplePhone target = new ApplePhone();//目标对象
            CGLIBDynamicProxy<ApplePhone> proxy = new CGLIBDynamicProxy(target);
            ApplePhone proxyObject = proxy.getProxyObject();
            proxyObject.getName();
        }
    }
    Enhancer.create()代码:
    public static Object create(Class type, Callback callback) {
            Enhancer e = new Enhancer();
            e.setSuperclass(type);
            e.setCallback(callback);
            return e.create();
        }

    在附上sun.misc.ProxyGenerator创建Class字节码代码:

    package com.kancy.pattern.proxy;
    
    import pattern.ApplePhone;
    import sun.misc.ProxyGenerator;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class ProxyGeneratorTest {
        public static void main(String[] args) throws IOException {
            // 生成ApplePhone类的代理类CLASS文件
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",
                    ApplePhone.class.getInterfaces());
            FileOutputStream out = new FileOutputStream("$Proxy0.class");
            out.write(bytes);
            out.close();
        }
    }

    创建的class反编译代码:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import pattern.Phone;
    
    public final class $Proxy0 extends Proxy implements Phone {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        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 String getName() throws  {
            try {
                return (String)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);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("pattern.Phone").getMethod("getName");
                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());
            }
        }
    }
    kancy
  • 相关阅读:
    手动配置linux(centos)的IP地址
    linux(centos)上配置nginx、mysql、phpfpm开机启动
    visual studio 2022 下载地址
    自己动手开发编译器(五)miniSharp语言的词法分析器
    自己动手开发编译器(一)编译器的模块化工程
    自己动手开发编译器(二)正则语言和正则表达式
    趣味问题:你能用Reflection.Emit生成这段代码吗?
    自己动手开发编译器(零)序言
    自己动手开发编译器特别篇——用词法分析器解决背诵圣经问题
    自己动手开发编译器(三)有穷自动机
  • 原文地址:https://www.cnblogs.com/kancy/p/10227001.html
Copyright © 2011-2022 走看看