Java代理

反射的机制,可以绕过java私有访问权限检查,反射获取并调用Runtime类执行命令执行。而Java的自带jdk动态代理机制,位于java.lang.reflect.proxy包中,其本质实现是通过反射执行invoke方法来动态获取执行方法。

代理模式Java当中最常用的设计模式之一。其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。而Java的代理机制分为静态代理和动态代理,而这里我们主要重点学习java自带的jdk动态代理机制。

image-20240521183054692

静态代理

静态代理在编译使用时,定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

我们用一个出租房子作为实例讲解。

定义一个接口

public interface Rental {
    public void sale();
}

委托类

// 委托类,实现接口的方法

public class Entrust implements Rental{
    @Override
    public void sale() {
        System.out.println("出租房子");
    }
}

代理类

这里代理类也命名了一个sale()方法,并且它的sale()方法也调用了委托类的sale()方法

public class AgentRental implements Rental{
    private Rental target; // 被代理对象
    public AgentRental(Rental target) {
        this.target = target;
    }
    @Override
    public void sale() {
        System.out.println("房子出租价位有1k-3k"); // 增加新的操作
        target.sale(); // 调用Entrust委托类的sale方法
    }
}

测试类

// 测试类,生成委托类实例对象,并将该对象传入代理类构造函数中。

public class Test {
    // 静态代理使用示例
    public static void consumer(Rental subject) {
        subject.sale();
    }
    public static void main(String[] args) {
        Rental test = new Entrust();
        System.out.println("---使用代理之前---");
        consumer(test);
        System.out.println("---使用代理之后---");
        consumer(new AgentRental(test));
    }
}

image-20240521183603666

通过上面的例子,我们可以看见静态代理的优点:

我们可以在==不改变Entrust委托类源代码的情况下 ,通过AgentRental代理类来修改Entrust委托类的功能==,从而实现“代理”操作。在进行代理后,自定义说明房子出租价位有1k-3k的操作方法。

但这个是我们通过代理类进行实现更改的方法,如果当我们需要过多的代理类对委托类进行修改的情况下,则可能出现下图情况:

image-20240521183709915

由此可以我们得知此静态代理的缺点:

当我们的接口类需要增加和删除方式的时候,委托类和代理类都需要更改,不容易维护。

同时如果需要代理多个类的时候,每个委托类都要编写一个代理类,会导致代理类繁多,不好管理。

因为java静态代理是对类进行操作的,我们需要一个个代理类去实现对委托类的更改操作,针对这个情况,我们可以==利用动态代理来解决,通过程序运行时自动生成代理类==。

动态代理

Java动态代理位于Java.lang.reflect包下,我们一般就仅涉及Java.lang.reflect.Proxy类与InvocationHandler接口,使用其配合反射,完成实现动态代理的操作。

  • InvocationHandler接口:负责提供调用代理操作。

    ​ 是由代理对象调用处理器实现的接口,定义了一个invoke()方法,每个代理对象都有一个关联的接口。当代理对象上调用方法时,该方法会被自动转发到InvocationHandler.invoke()方法来进行调用。

  • Proxy类:负责动态构建代理类

    ​ 提供四个静态方法来为一组接口动态生成的代理类并返回代理类的实例对象。

getProxyClass(ClassLoader,Class<?>...):获取指定类加载器和动态代理类对象
newProxyInstance(ClassLoader,Class<?>[],InvocationHandler):指定类加载器一组接口调用处理器
isProxyClass(Class<?>):判断获取的类是否为一个动态代理类;

getInvocationHandler(Object):获取指定代理类实例查找与它相关联的调用处理器实例;

实现过程

1、使用java.lang.InvocationHandler接口创建自定义调用处理器,由它来实现invoke方法,执行代理函数;

2、使用java.lang.reflect.Proxy类指定一个ClassLoader,一组interface接口和一个InvocationHandler;

3、通过反射机制获得动态代理类的构造方法,其唯一参数类型是调用处理器接口类型;

4、调用java.lang.reflect.Proxy.newProxyInstance()方法,分别传入类加载器,被代理接口,调用处理器;创建动态代理实例对象。

5、通过代理对象调用目标方法;

我们继续使用前面那个例子进行讲解,因为接口类和委托类不用更改,这里就不重复了。

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

public class TestAgent implements InvocationHandler {
    // target变量为委托类对象
    private Object target;
    public TestAgent(Object target) {
        this.target = target;
    }
    // 实现 java.lang.reflect.InvocationHandler.invoke()方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 添加自定义的委托逻辑
        System.out.println("房子出租价位有1k-3k");
        // 调用委托类的方法
        Object result = method.invoke(target,args);
        return result;
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class test {
    public static void main(String[] args) {
        // 获取委托类的实例对象
        Entrust testEntrust = new Entrust();
        // 获取CLassLoader
        ClassLoader classLoader = testEntrust .getClass().getClassLoader();
        // 获取所有接口
        Class[] interfaces = testEntrust .getClass().getInterfaces();
        // 获取一个调用处理器
        InvocationHandler invocationHandler = new TestAgent(testEntrust);
        // 查看生成的代理类
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        // 创建代理对象
        Rental proxy = (Rental) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
        // 调用代理对象的sayHello()方法
        proxy.sale();
    }
}

image-20240521184908480

0%