4.2Java设计模式-----JDK动态代理(Dynamic Proxy)

接上文:4.1Java设计模式-----JDK静态代理(Static Proxy)

JDK动态代理

        JDK动态代理是指动态的在内存中构建代理对象(需要我们制定要代理的目标对象实现的接口类型),即利用JDK的API生成指定接口的对象,也称之为JDK代理或者接口代理。

       动态代理代码部分,下面将做进一步分析。用到的Person接口和Student被代理类保持不变。

①代理对象的创建(动态代理,需要实现InvocationHandler接口)

package dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理实现
 */
public class DynamicProxy implements InvocationHandler {

    //被代理类
    Student student;

    //创建构造方法
    public DynamicProxy(Student student){
        this.student = student;
    }

    /**
     * 重写invoke方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("(此处为前置增强)--我是中介,如下是用户的一些需求");
        System.out.println("**************************************************");
        //通过反射机制
        method.invoke(student,args);
        System.out.println("**************************************************");
        System.out.println("(此处为后置增强)--我是中介,如上就是我代理用户的需求");
        return null;
    }
}

②JDK动态代理测试

package dynamicProxy;

import java.lang.reflect.Proxy;

public class test {

    public static void main(String[] args) {

        //生成$Proxy0的class文件,这一句是生成代理类的class文件(可有可无!!!)
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        Student student = new Student();
        /**
         * 动态代理参数准备
         */
        //1.指明加载被代理类的类加载器--ClassLoader
        ClassLoader classLoader = student.getClass().getClassLoader();
        //2.指明被代理类实现的接口--Interface
        Class[] interfaces = student.getClass().getInterfaces();
        //3.创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法--InvocationHandler
        DynamicProxy invocationHandler = new DynamicProxy(student);

        /**
         * 通过newPrxoyInstance生成动态代理类Object(强转为Person类)
         */
        Person proxy= (Person)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        /**
         * 代理用户开始帮助用户找房子
         */
        proxy.findHouse();

    }
}
代码运行结果:
(此处为前置增强)--我是中介,如下是用户的一些需求
**************************************************
被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习
**************************************************
(此处为后置增强)--我是中介,如上就是我代理用户的需求

仔细分析上面的JDK动态代理实现代码,我们看到这里涉及到java反射包下的一个接口InvocationHandler和一个类Proxy。

package java.lang.reflect;
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
	throws Throwable;
}

这个接口只有一个invoke方法,我们在通过代理类调用被代理类的方法时,最终都会委托给这个invoke方法执行

//通过代理类调用被代理类的方法
proxy.findHouse();

所以我们就可以在这个invoke方法中对被代理类进行增强或做一些其他操作。

        Proxy类的public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)方法内部通过拼接字节码的方式来创建代理类,后面我会反编译出它所创建的代理类看看内容。

我们看这个方法的三个参数:
1.ClassLoader loader:指定一个动态加载代理类的类加载器
2.Class[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法
3.InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。

你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。

③反编译Proxy.newProxyInstance所创建的代理类

//这一句是生成代理类的class文件(如报,你需要在工程根目录下创建com/sun/proxy目录)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

 我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class

4.2Java设计模式-----JDK动态代理(Dynamic Proxy)_第1张图片

④创建的代理类代码$Proxy0.class文件,反编译后【其中注释是自己加上去的】

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import dynamicProxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;


/**
 *代理类也实现了Person接口,看起来和静态代理的方式也是一样的 
 *同时代理类也继承了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;

    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);
        }
    }
    //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
    public final void findHouse() throws  {
        try {
            // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
            // m3为代理类通过反射获得的Method
            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 {
            //代理类通过反射 获得的接口方法Method
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("dynamicProxy.Person").getMethod("findHouse");
            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());
        }
    }
}

 JDK动态代理的优点和缺点

        优点:解决了静态代理的缺点。动态代理让我们在不直接访问某些对象的情况下,通过代理机制也可以访问被代理对象的方法,这种技术可以应用在很多地方比如RPC框架,Spring AOP机制。

        缺点:动态代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重写哪些方法。这样如果一个没有实现任何接口的类,就无法通过动态代理机制来进行代理操作了。

附链接:4. Java设计模式-----代理模式(Proxy Pattern)

              4.1 Java设计模式-----JDK静态代理(Static Proxy)

              4.3 Java设计模式-----Cglib动态代理(Cglib Proxy)

你可能感兴趣的:(设计模式)