美文网首页设计模式
设计模式之动态代理 - 彻底搞懂JDK动态代理

设计模式之动态代理 - 彻底搞懂JDK动态代理

作者: Liuzz25 | 来源:发表于2019-12-05 22:55 被阅读0次

    目录

    1.什么是JDK动态代理
    2.简单案例
    3.彻底搞懂JDK动态代理,自己动手实现JDK动态代理。
    4.项目源码

    1.什么是JDK动态代理

    JDK动态代理是设计模式中代理模式的一种实现方式,在java的运行过程中,动态的生成代理类,其只能代理接口。

    2.简单案例

    本案例说明
    (1)创建一个抽象类,Person接口,使其拥有一个没有返回值的doSomething方法。
    /**
     * 抽象类人
     */
    public interface Person {
        void doSomething();
    }
    
    (2)创建一个名为Bob的Person接口的实现类,使其实现doSomething方法。
    /**
     * 创建一个名为Bob的人的实现类
     */
    public class Bob implements Person {
        public void doSomething() {
            System.out.println("Bob doing something!");
        }
    }
    
    (3)创建JDK动态代理类,使其实现InvocationHandler接口。拥有一个名为target的变量,并创建getTarget获取代理对象方法。
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * JDK动态代理
     * 需实现InvocationHandler接口
     */
    public class JDKDynamicProxy implements InvocationHandler {
    
        // 被代理的对象
        Person target;
    
        // JDKDynamicProxy构造函数
        public JDKDynamicProxy(Person person) {
            this.target = person;
        }
    
        // 获取代理对象
        public Person getTarget() {
            return (Person) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
    
        // 动态代理invoke方法
        public Person invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 被代理方法前执行
            System.out.println("JDKDynamicProxy do something before!");
            // 执行被代理的方法
            Person result = (Person) method.invoke(target, args);
            // 被代理方法后执行
            System.out.println("JDKDynamicProxy do something after!");
            return result;
        }
    }
    
    (4)创建JDK动态代理测试类JDKDynamicTest。
    /**
     * JDK动态代理测试
     */
    public class JDKDynamicTest {
    
        public static void main(String[] args) {
    
            System.out.println("不使用代理类,调用doSomething方法。");
            // 不使用代理类
            Person person = new Bob();
            // 调用doSomething方法
            person.doSomething();
    
            System.out.println("-------------------------------------- 分割线 --------------------------------------");
    
            System.out.println("使用代理类,调用doSomething方法。");
            // 获取代理类
            Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();
            // 调用doSomething方法
            proxyPerson.doSomething();
    
        }
    }
    

    运行结果如下图所示:


    image.png

    3.彻底搞懂JDK动态代理,自己动手实现JDK动态代理。

    想要自己实现JDK动态代理,首先我们先梳理下原理:

    1.拿到被代理对象的引用,然后获取其接口。
    2.JDK代理重新生成一个类,同时实现我们给的代理所实现的接口。
    3.同时拿到代理对象的引用。
    4.动态生成class文件字节码。
    5.编译源代码,并生成. class文件。
    6.将class文件动态加载到JVM。
    7.返回被代理后的代理对象。

    好了原理梳理完了,下面就开始自己动手实现JDK动态代理吧。(ps:可能部分用语不专业,大家理解下工科妹子的表达能力)

    第1步和第2步完全同上,这里就省略不写了啊。

    (1)创建自定义InvocationHandler
    /**
     * 自定义InvocationHandler
     */
    public interface LzzInvocationHandler {
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    
    (2)创建自定义一个ClassLoader
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    /**
     * 自定义一个ClassLoader
     */
    public class LzzClassLoader extends ClassLoader {
        private File baseDir;
    
        public LzzClassLoader() {
            String basePath = LzzClassLoader.class.getResource("").getPath();
            this.baseDir = new java.io.File(basePath);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String className = LzzClassLoader.class.getPackage().getName() + "." + name;
            if (baseDir != null) {
                File classFile = new File(baseDir, name.replaceAll("\\.", "/") + ".class");
                if (classFile.exists()) {
                    FileInputStream in = null;
                    ByteArrayOutputStream out = null;
                    try {
                        in = new FileInputStream(classFile);
                        out = new ByteArrayOutputStream();
                        byte[] buff = new byte[1024];
                        int len;
                        while ((len = in.read(buff)) != -1) {
                            out.write(buff, 0, len);
                        }
                        return defineClass(className, out.toByteArray(), 0, out.size());
    
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (null != in) {
                            try {
                                in.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (null != out) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        classFile.delete();
                    }
    
                }
            }
            return null;
        }
    }
    
    (3)创建自定义Proxy类
    import javax.tools.JavaCompiler;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * 自定义Proxy类
     */
    public class LzzProxy {
    
        // 换行符
        private static String ln = "\r\n";
    
        public static Object newProxyInstance(LzzClassLoader classLoader, Class<?>[] interfaces, LzzInvocationHandler h) {
    
    
            try {
                //1.生成源代码
                String proxySrc = generateSrc(interfaces[0]);
    
                //2.保存将生成出来的源代码
                String filePath = LzzProxy.class.getResource("").getPath();
                File f = new File(filePath + "$Proxy0.java");
                FileWriter fw = new FileWriter(f);
                fw.write(proxySrc);
                fw.flush();
                fw.close();
    
                //3.编译源代码,生成.CLASS文件
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
                Iterable iterable = manager.getJavaFileObjects(f);
                JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
                task.call();
                manager.close();
    
                //4.将class文件动态加载到JVM返回被代理后的对象
                Class proxyClass = classLoader.findClass("$Proxy0");
                Constructor c = proxyClass.getConstructor(LzzInvocationHandler.class);
                f.delete();
    
                return c.newInstance(h);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
            return null;
        }
    
    
        private static String generateSrc(Class<?> interfaces) {
            StringBuffer src = new StringBuffer();
            src.append("package lzzly.custom;" + ln);
            src.append("import java.lang.reflect.Method;" + ln);
            src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
    
            src.append("LzzInvocationHandler h;" + ln);
    
            src.append("public $Proxy0(LzzInvocationHandler h) {" + ln);
            src.append("this.h = h;" + ln);
            src.append("}" + ln);
    
            for (Method m : interfaces.getMethods()) {
                src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
    
                src.append("try{" + ln);
                src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
                src.append("this.h.invoke(this,m,null);" + ln);
                src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
                src.append("}" + ln);
            }
    
            src.append("}");
    
            return src.toString();
        }
    }
    
    (4)创建CustomDynamicProxy自定义动态代理类
    import java.lang.reflect.Method;
    
    /**
     * 自定义动态代理类
     * 实现LzzInvocationHandler接口
     */
    public class CustomDynamicProxy implements LzzInvocationHandler {
    
        // 被代理的对象
        Person target;
    
        // JDKDynamicProxy构造函数
        public CustomDynamicProxy(Person person) {
            this.target = person;
        }
    
        // 获取代理对象
        public Person getTarget() {
            return (Person) LzzProxy.newProxyInstance(new LzzClassLoader(), target.getClass().getInterfaces(), this);
        }
    
        // 动态代理invoke方法
        public Person invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 被代理方法前执行
            System.out.println("LzzDynamicProxy do something before!");
            // 执行被代理的方法
            Person result = (Person) method.invoke(target, args);
            // 被代理方法后执行
            System.out.println("LzzDynamicProxy do something after!");
            return result;
        }
    }
    
    (5)创建自定义动态代理测试类
    /**
     * 自定义动态代理测试
     */
    public class CustomDynamicTest {
    
        public static void main(String[] args) {
    
            System.out.println("不使用代理类,调用doSomething方法。");
            // 不使用代理类
            Person person = new Bob();
            // 调用doSomething方法
            person.doSomething();
    
            System.out.println("-------------------------------------- 分割线 --------------------------------------");
    
            System.out.println("使用代理类,调用doSomething方法。");
            // 获取代理类
            Person proxyPerson = new CustomDynamicProxy(new Bob()).getTarget();
            // 调用doSomething方法
            proxyPerson.doSomething();
    
        }
    }
    

    运行结果如下图所示:


    image.png

    项目源码

    码云-DynamicProxy:https://gitee.com/OrgXxxx/DynamicProxy

    相关文章

      网友评论

        本文标题:设计模式之动态代理 - 彻底搞懂JDK动态代理

        本文链接:https://www.haomeiwen.com/subject/roijgctx.html