手写动态代理--java

发布时间:2023年12月28日

动态代理

首先明确代理模式是一个类的方法不能直接使用,我们在客户端和目标对象之间创建一个中介,这个中介就是动态代理,例如租房者,中介和房东之间,租房者通过中介访问房东。
在程序执行的过程中创建代理类对象,在创建过程中利用反射机制来实现的。
即为在使用过程中利用JDK提供的方法才会创建对象。
动态代理的实现有两种:
JDK动态代理:使用jdk字节的类,利用反射实现的代理,反射包Java.lang.reflect包括三个类:Proxy,Method和InvocationHandler
Method:能够实现所有某个接口定义的方法,具体运行哪个具体类的方法,根据invoke传入的对象来决定的。
InvocationHandler:只有一个接口invoke() ,表示代理对象要执行的功能代码,你的代理类完成的功能就写在invoke() 方法中。
代理类完成的功能:调用目标方法,并且功能增强

静态代理的写法

被代理类接口

package proxy1;

public interface Foo {
    void fo();
    int bar();
}

被代理类

package proxy1;

public class target1 implements Foo{

    @Override
    public void fo() {
        System.out.println("fo");
    }

    @Override
    public int bar() {
        System.out.println("bar");
        return 1;
    }
}

代理类

package proxy1;

public class $Proxy1 implements Foo{
    @Override
    public void fo() {
        System.out.println("before fo");
        new target1().fo();
    }

    @Override
    public int bar() {
        System.out.println("before bar");
       return new target1().bar();
    }
}

执行

package proxy1;

public class Main1 {
    public static void main(String[] args) {
        Foo proxy = new $Proxy1();
        proxy.fo();
        proxy.bar();
    }
}

结果

before fo
fo
before bar
bar

动态代理基本写法

上面的代理类的方法都是写死的,如 new target1().fo(); return new target1().bar();
如果语句是需要动态的执行的话,那么就不好搞了,因此需要把这些动态语句,抽离出来。
这时候定义了InvocationHnadler接口,来表达这个被代理的类,通过h.invoke()方法来执行真实的类,就有了如下写法
我们通过反射的方式动态获取方法对象。
void invoke(Method method, Object[] args)
修改传入参数,一个是要执行的方法,一个是传入方法的参数
在代理中通过反射的方式来获取方法,并且将参数传进去
被代理接口

package proxy2;

import java.lang.reflect.InvocationTargetException;

public interface Foo {
    void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
    int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}

被代理的类

package proxy2;

public class target1 implements Foo {

    @Override
    public void fo() {
        System.out.println("fo");
    }

    @Override
    public int bar() {
        System.out.println("bar");
        return 1;
    }
}

代理接口

package proxy2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public interface InvocationHandler {
    Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}

代理类

package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class $Proxy1 implements Foo {

    proxy2.InvocationHandler h;

    public $Proxy1(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = Foo.class.getMethod("fo");
        h.invoke(method, null);
    }

    @Override
    public int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = Foo.class.getMethod("bar");
        String[] str = new String[2];
        str[0] = "param1";
        str[1] = "param2";
        return (int) h.invoke(method, str);
    }
}

具体执行

package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main1 {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Foo proxy = new $Proxy1(new InvocationHandler() {

            @Override
            public Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {
                System.out.println("before...");
                return method.invoke(new target1(), args);
            }
        });
        proxy.fo();
        System.out.println(proxy.bar());
    }
}

通过预先定义一个InvocationHandler h,h中传入具体的类的方法即可通过反射的方式实现方法的执行。
ps:这里需要补充一下lamda表达式的写法,在new $Proxy1时传入一个new InvocationHandler(),这是一个接口需要具体实现,于是就有了

{
            @Override
            public Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {
                System.out.println("before...");
                return method.invoke(new target1(), args);
            }
}

实际上,这里有个隐形的实现类,不过被隐藏起来了,可以简化为lamda表达式

 Foo proxy = new $Proxy1((proxy1, method, objects) -> {
            System.out.println("before...");
            return method.invoke(new target1(), args);
        });

(proxy1, method, objects)就代表实现了接口的类

静态化具体方法

进一步的优化,将被代理类的方法进行静态化,这样可以加快执行速度

package proxy4;

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

public class $Proxy1 implements Foo {

    InvocationHandler h;
    public $Proxy1(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void fo() {
        try {
            h.invoke(this, fo, null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    @Override
    public int bar() {

        try {
            String[] str = new String[2];
            str[0] = "param1";
            str[1] = "param2";
            return (int) h.invoke(this, bar , str);
        } catch (Throwable e) {
            e.printStackTrace();
            return -1;
        }

    }
    //静态化被代理方法
    static Method bar;
    static Method fo;
    static {
        try {
            bar = Foo.class.getMethod("bar");
            fo = Foo.class.getMethod("fo");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

package proxy3;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class $Proxy1 implements Foo {

    InvocationHandler h;
    public $Proxy1(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void fo() {
        try {
            h.invoke(this, fo, null);
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    @Override
    public int bar() {
        try {
            String[] str = new String[2];
            str[0] = "param1";
            str[1] = "param2";
            return (int) h.invoke(this, bar , str);
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            return -1;
        }
    }
    static Method bar;
    static Method fo;
    static {
        try {
            bar = Foo.class.getMethod("bar");
            fo = Foo.class.getMethod("fo");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

改成用JDK的InvocationHandler

在程序中自定义了InvocationHandler
如果删除这个接口,将引用的这个接口改变为import java.lang.reflect.InvocationHandler;实际上效果是一样的
被代理类:

package proxy4;

public class target1 implements Foo {

    @Override
    public void fo() {
        System.out.println("fo");
    }

    @Override
    public int bar() {
        System.out.println("bar");
        return 1;
    }
}

代理类

package proxy4;

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

public class $Proxy1 implements Foo {

    InvocationHandler h;
    public $Proxy1(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void fo() {
        try {
            h.invoke(this, fo, null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    @Override
    public int bar() {

        try {
            String[] str = new String[2];
            str[0] = "param1";
            str[1] = "param2";
            return (int) h.invoke(this, bar , str);
        } catch (Throwable e) {
            e.printStackTrace();
            return -1;
        }

    }
    static Method bar;
    static Method fo;
    static {
        try {
            bar = Foo.class.getMethod("bar");
            fo = Foo.class.getMethod("fo");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

测试代码

package proxy4;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class Main1 {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {

        Foo proxy = new $Proxy1((proxy1, method, objects) -> {
            System.out.println("before...");
            return method.invoke(new target1(), args);
        });
        proxy.fo();
        System.out.println(proxy.bar());
        System.out.println(proxy.getClass());
        System.in.read();
    }
}

实现InvacationHandler接口,重写invoke()方法,在invoke() 方法中增强类的方法

文章来源:https://blog.csdn.net/Artisan_w/article/details/135280135
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。