Java动态代理机制

前言

还是对动态代理机制不熟悉,今天总结一下Java动态代理截止

静态代理

在讲动态代理之前,我们先了解下什么是静态代理。静态代理在编译使用时,定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。我们用一个出租房子作为实例讲解。

定义一个接口

  1. public interface Rental {
  2. public void sale();
  3. }

委托类(实现接口的方法)

  1. public class Entrust implements Rental{
  2. @Override
  3. public void sale() {
  4. System.out.println("出租房子");
  5. }
  6. }

代理类

  1. public class Test {
  2. // 静态代理使用示例
  3. public static void consumer(Rental subject) {
  4. subject.sale();
  5. }
  6. public static void main(String[] args) {
  7. Rental test = new Entrust();
  8. System.out.println("---使用代理之前---");
  9. consumer(test);
  10. System.out.println("---使用代理之后---");
  11. consumer(new AgentRental(test));
  12. }
  13. }

Java动态代理 - 图1

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

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

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

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

动态代理

例子: 我们先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。

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

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

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

Java动态代理 - 图2

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

Java动态代理 - 图3

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、通过代理对象调用目标方法;

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

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. public class TestAgent implements InvocationHandler {
  4. // target变量为委托类对象
  5. private Object target;
  6. public TestAgent(Object target) {
  7. this.target = target;
  8. }
  9. // 实现 java.lang.reflect.InvocationHandler.invoke()方法
  10. @Override
  11. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12. // 添加自定义的委托逻辑
  13. System.out.println("房子出租价位有1k-3k");
  14. // 调用委托类的方法
  15. Object result = method.invoke(target,args);
  16. return result;
  17. }
  18. }
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Proxy;
  3. public class test1 {
  4. public static void main(String[] args) {
  5. // 获取委托类的实例对象
  6. Entrust testEntrust = new Entrust();
  7. // 获取CLassLoader
  8. ClassLoader classLoader = testEntrust .getClass().getClassLoader();
  9. // 获取所有接口
  10. Class[] interfaces = testEntrust .getClass().getInterfaces();
  11. // 获取一个调用处理器
  12. InvocationHandler invocationHandler = new TestAgent(testEntrust);
  13. // 查看生成的代理类
  14. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
  15. // 创建代理对象
  16. Rental proxy = (Rental) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
  17. // 调用代理对象的sayHello()方法
  18. proxy.sale();
  19. }
  20. }

Java动态代理 - 图4

这里我们可以看见我们生成的动态代理类的字节码文件,放置在程序根目录下的com.sun.proxy.$Proxy0.class文件中。

Java动态代理 - 图5


参考:https://xz.aliyun.com/t/9197