设计模式

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

2019-12-05  本文已影响0人  Liuzz25

目录

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

上一篇下一篇

猜你喜欢

热点阅读