Java中JDK动态代理详解

发布时间:2023年12月28日

1.动态代理是什么?有什么用?

动态代理(Dynamic Proxy)是一种在【运行时】动态生成代理对象的技术。

它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。

2.动态代理中的角色

  • 目标对象【待增强的对象】

  • 代理对象【增强过后的对象】(也就是我们会使用的对象)

3.代理模式

  1. 静态代理

    在【编译期间】就确认好代理关系

  2. 动态代理

    在【运行期间】确认好代理关系

    • jdk动态代理(反射)

      • 目标接口

      • 目标对象 implements 目标接口

      • 代理对象 implements 目标接口

      代理对象目标对象 是平级关系

    • cglib代理(继承)

      • 目标对象

      • 代理对象 extends 目标对象

      代理对象目标对象 是父子关系

4.代理模式的应用

  1. 功能增强

  2. 控制访问

5.如何使用、实现?

1.目标接口
/**
* 目标接口
*/
interface Target{
    void eat();
}
2.目标对象(原始对象)implements 目标接口
/**
?* 目标对象(原始对象)
?*/
?static class TargetDog implements Target{
? ? ?@Override
? ? ?public void eat() {
? ?      System.out.println("狗在吃。。。。");
? ?  }
?}
3.代理对象 implements 目标接口

Q:怎么实现功能增强

A:Java中提供了两个核心的类 API来实现。

  1. 代理类 proxy

  2. invocationHandler类

来实现动态代理

?public static void main(String[] args) {
??
? ? ? ? ?Target proxy = (Target) Proxy.newProxyInstance(TestJdkAgent.class.getClassLoader(),
? ? ? ? ? ? ? ? ?TargetDog.class.getInterfaces(),
? ? ? ? ? ? ? ? ?new InvocationHandler() {
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ? ?// 1.前置增强
? ? ? ? ? ? ? ? ? ? ? ? ?System.out.println("before!!!");
? ? ? ? ? ? ? ? ? ? ? ? ?//2.目标对象的原始方法
? ? ? ? ? ? ? ? ? ? ? ? ?//现在是 方法.invoke(obj, arg) === 原来是 目标对象.方法
? ? ? ? ? ? ? ? ? ? ? ? ?Object invoke = method.invoke(new TargetDog(), args);
? ? ? ? ? ? ? ? ? ? ? ? ?return invoke;
? ? ? ? ? ? ? ? ? ?  }
? ? ? ? ? ? ? ?  });
? ? ? ? ?proxy.eat();
? ?  }

Proxy.newProxyInstance() : 来实例化一个经过反射下的一个代理实例,需要要传递的3个参数,如下

  1. ClassLoader loader:类加载器 ,因为我们生成的这个代理对象是没有.java源文件的,是在运行时直接生成的一个.class的二进制字节码文件,他既然直接生成一个.class二进制文件的,那么它是需要去加载,被加载到Java的运行时数据区,所以他就需要一个类加载器

    PS:什么是类加载器?有什么作用?

    在Java中,.class文件是Java编译器生成的二进制字节码文件,它包含了Java类的定义和方法。当我们运行一个Java程序时,Java虚拟机(JVM)需要加载这些.class文件到其运行时数据区。

    类加载器(ClassLoader)的主要任务就是负责将.class文件中的二进制数据读入到内存中,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的Java类型。这个过程被称为类加载机制。

    类加载器还有另一个重要的功能,那就是隔离加载类的命名空间。不同的类加载器可以加载同名的类,但是由不同的类加载器加载的类属于不同的命名空间,互不干扰。

    因此,类加载器在Java程序运行中起着至关重要的作用。它不仅负责加载类,还负责对类进行校验,解析和初始化,以及隔离加载类的命名空间。

  1. @NotNull Class<?>[] interfaces:接口类型,有两种方法:

    1. 因为已经知道了接口的类型,直接 Target.class

    2. new TargetDog().getClass().getInterfaces():通过目标对象去获得,因为目标对象实现了接口,先得到目标对象 TargetDog().getClass(),然后再 .getInterfaces(),会基于反射去拿到接口上的信息;(反射就是可以去拿到你的类的一些原始信息然后操作等)

  1. @NotNull InvocationHandler h:InvocationHandler接口 ,写代理的逻辑,直接 new InvocationHandler()接口,重写他的 invoke方法,就是在 invoke方法中写代理的逻辑是什么。invoke有3个参数:Object proxy, Method method, Object[] args

    1. proxy:就是生成的代理对象

    2. method:当前的方法中,我要增强的那个方法 method.invoke(new TargetDog(),args) == 原来的 TargetDog().eat()

    3. args:参数就是 method方法中的参数

    PS:因为代理对象?和?目标对象?是平级关系,所以可以直接进行强转

    Target proxy = (Target) Proxy.newProxyInstance()

代理模式的应用体现

  • 功能增强体现在:在原有的基础上,可以在eat()方法之前加上其他方法;

    ?// 1.前置增强
    ?System.out.println("before!!!");
    ?//2.目标对象的原始方法
    ?Object invoke = method.invoke(new TargetDog(), args);
    ?--------------------------------
    ?before!!!
    ?狗在吃。。。。

  • 控制访问体现在:可以直接不要原始的方法,只有新增的方法;

    ?// 1.前置增强
    ?System.out.println("before!!!");
    ?------------------------------------
    ?before!!!

4.完整代码
?package org.example.testAgent;
??
?import java.lang.reflect.InvocationHandler;
?import java.lang.reflect.Method;
?import java.lang.reflect.Proxy;
??
?public class TestJdkAgent {
??
? ? ?public static void main(String[] args) {
??
? ? ? ? ?Target proxy = (Target) Proxy.newProxyInstance(TestJdkAgent.class.getClassLoader(),
? ? ? ? ? ? ? ? ?TargetDog.class.getInterfaces(),
? ? ? ? ? ? ? ? ?new InvocationHandler() {
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ? ?// 1.前置增强
? ? ? ? ? ? ? ? ? ? ? ? ?System.out.println("before!!!");
? ? ? ? ? ? ? ? ? ? ? ? ?//2.目标对象的原始方法
? ? ? ? ? ? ? ? ? ? ? ? ?//现在是 方法.invoke(obj, arg) === 原来是 目标对象.方法
? ? ? ? ? ? ? ? ? ? ? ? ?Object invoke = method.invoke(new TargetDog(), args);
? ? ? ? ? ? ? ? ? ? ? ? ?return invoke;
? ? ? ? ? ? ? ? ? ?  }
? ? ? ? ? ? ? ?  });
? ? ? ? ?proxy.eat();
? ?  }
? ? ?/**
? ? ? * 目标接口
? ? ? */
? ? ?interface Target{
? ? ? ? ?void eat();
? ?  }
? ? ?/**
? ? ? * 目标对象(原始对象)
? ? ? */
? ? ?static class TargetDog implements Target{
??
? ? ? ? ?@Override
? ? ? ? ?public void eat() {
? ? ? ? ? ? ?System.out.println("狗在吃。。。。");
? ? ? ?  }
? ?  }
?}
??
文章来源:https://blog.csdn.net/weixin_73509688/article/details/135174732
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。