jdk 动态代理的原理

一、代理设计模式

代理设计模式是Java常用的设计模式之一。

特点:

01.委托类和代理类有共同的接口或者父类;

02.代理类负责为委托类处理消息,并将消息转发给委托类;

03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;

04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;

二、静态代理与动态代理

按照代理类创建的时机,代理类分为两种:

01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;

02.动态代理:在程序运行期间,通过反射创建出来的代理类

三、jdk动态代理

顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。

01.创建一个Person接口

public interface Person {
    void eat();
    void sleep();
}

02.创建ZhangSan.java实现Person接口

public class ZhangSan implements Person {

    public void eat() {
        System.out.println("吃饭...");
    }

    public void sleep() {
        System.out.println("睡觉...");
    }

}

03.创建代理类ZSProxy.java

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

public class ZSProxy implements InvocationHandler {
    
    //被代理类对象引用
    private ZhangSan zhangSan;
    
    
    public ZSProxy(ZhangSan zhangSan) {
        super();
        this.zhangSan = zhangSan;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //zhangSan.eat();
        Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值
        after();
        return null;
    }
    
    private void before() {
        System.out.println("前置...");
    }
    private void after() {
        System.out.println("后置...");
    }
}

jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。

001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法

public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;

proxy:代理类对象

method:被代理的方法

args:被代理方法的参数列表

002.Proxy类

public class Proxy implements java.io.Serializable {
        ... 

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

        ...
}

loader:类加载器

interfaces:代理类实现的所有接口

h:InvocationHandler接口的一个实例

public class Test {

    public static void main(String[] args) throws Throwable {
        System.out.println("----------------------JDK动态代理----------------------------");
        //获取代理类对象
        Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), 
                new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
        proxyInstance.eat();
}

通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。

程序运行结果:

 

虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??

首先要了解一个类 ==》$Proxy0.java

001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();

002.执行$Proxy0类中的invoke

 我们debug运行观察:

 

内存中代理类的特征:

01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。

代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。

02.内存中的代理类必须持有InvocationHandler接口的实现类引用。

整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?

/**
     * 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
     */
    private static void createProxyClassFile() {
        byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class});
        
        try {
            FileOutputStream out = new FileOutputStream("$Proxy0.class");
            out.write(data);
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

对$Proxy0.java反编译后

public final class class extends Proxy
    implements Person
{

    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public class(InvocationHandler invocationhandler) //通过构造将handler传入
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void eat()
    {
        try
        {
            super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void sleep()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch (NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch (ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

那么就真相大白了。

********************************************************************************************************************************************

以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。

 01.创建Person接口

package cn.yzx.myProxy;

public interface Person {
    void eat() throws Throwable;
    void sleep() throws Throwable;
}

02.创建ZhangSan实现类

package cn.yzx.myProxy;

public class ZhangSan implements Person {

    public void eat() {
        System.out.println("吃饭...");
    }

    public void sleep() {
        System.out.println("睡觉...");
    }
}

03.ZSProxy代理类

package cn.yzx.myProxy;
import
java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler { //目标对象 private Person person; public ZSProxy(Person person) { this.person = person; } @Override public Object invoke(Object proxy, Method method, Object args) { before(); try { person.eat(); } catch (Throwable e) { e.printStackTrace(); } after(); return null; } private void before() { System.out.println("前置..."); } private void after() { System.out.println("后置..."); } }

04.自定义动态代理Proxy类

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


/**
 * 自定义的动态代理Proxy类
 * @author Xuas
 */
public class MyProxy {
    static String rt = "\r\n";
    /**
     * 自定义创建内存中代理实例的方法
     * @param loader
     * @param interfaces
     * @param handler
     * @return
     */
    public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) {

        try {
            Method[] methods = interfaces.getMethods();
            //01.使用拼凑字符串的方式将内存中的代理类拼出来
            String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
                    + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
                    + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
                    + getMethodString(methods, interfaces) + rt + "}";
            //02.使用IO将拼凑的代理类写入到文件中
            String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
            File file = new File(filePathName);
            FileWriter fw = new FileWriter(file);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            
            //03.编译上面生成的java文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, 
                    null, null);//文件管理器
            Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
            CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
            task.call();//编译
            fileManager.close();//关闭文件管理器
            /**
             * 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
             *         所以要把硬盘中class文件加载到JVM内存中去!
             */
            //04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
            MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy");
            
            //proxy0Clazz:就是内存中代理类的class对象
            Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象
            /**
             * public $Proxy0(MyInvocationHandler h) {..}
             * 要通过构造器的参数将handler对象的引用传进来
             */
            Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
            Object instance = constructor.newInstance(handler);//返回内存中代理类实例
            return instance;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getMethodString(Method[] methods, Class interfaces) {

        String proxyMe = "";

        for (Method method : methods) {
            proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
                    + interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
                    + "this.h.invoke(this,md,null);" + rt + "}" + rt;
        }

        return proxyMe;
    }
}

05.自定义InvocationHandler接口

import java.lang.reflect.Method;

/**
 * 自定义InvocationHandler接口
 * @author Xuas
 *
 */
public interface MyInvocationHandler {
    public Object invoke(Object proxy, Method method, Object args);
}

06.自定义的类加载器

 

package cn.yzx.myProxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {
    
    private File dir;
    
    public MyClassLoader(String path) {//要加载的文件路径
        dir = new File(path);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        
        if(null != dir) {
            File clazzFile = new File(dir, name + ".class");
            if(clazzFile.exists()) {
                try {
                    FileInputStream input = new FileInputStream(clazzFile);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while((len = input.read(buffer)) != -1) {
                        baos.write(buffer, 0, len);
                    }
                    return defineClass("cn.yzx.myProxy."+name,
                            baos.toByteArray(),
                            0,
                            baos.size());//最终把输出流输出到JVM内存中
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return super.findClass(name);
    }
    
}

07.内存中自定义生成的 $Proxy0.java

package cn.yzx.myProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements cn.yzx.myProxy.Person{
    MyInvocationHandler h;
    public $Proxy0(MyInvocationHandler h) {
        this.h = h;
    }
    public void sleep() throws Throwable {
        Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
        this.h.invoke(this,md,null);
    }
    public void eat() throws Throwable {
        Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
        this.h.invoke(this,md,null);
    }

}                

08.测试类

public class Test {

    public static void main(String[] args) throws Throwable {
        System.out.println("----------------------自定义动态代理----------------------------");
        cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(), 
                cn.yzx.myProxy.Person.class, 
                new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
        person2.eat();
    }
}

程序运行结果:

 

总结:

通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。

出处:https://www.cnblogs.com/9513-/p/8432276.html

转载于:https://www.cnblogs.com/myseries/p/10762231.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值