Java中JDK动态代理详解

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("狗在吃。。。。");
         }
     }
 }
 ​
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值