java动态代理概述

 一.动态代理的理解

    动态代理是一种在运行时生成代理对象的机制,用于在不修改原始类的情况下对其进行拦截和增强。

     通过动态代理,我们可以在调用目标对象的方法之前或之后插入自定义的逻辑。

动态代理通过使用一组代理类来包装目标对象,并将方法调用重定向到代理类。

     代理类可以在方法调用前后执行特定的代码,实现对目标对象方法的拦截、改变参数、增强功能等操作。

Java中的动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现

二.动态代理的类型

  Java的动态代理主要有两种类型:基于接口的动态代理和基于类的动态代理。 

        1.基于接口的动态代理:使用java.lang.reflect.Proxy类创建代理对象

        2.基于类的动态代理:使用第三方库(如CGLIB或Byte Buddy)创建代理对象
 

     二者的主要区别

     这两种动态代理类型在Java中具有不同的应用场景和特点。基于接口的动态代理适用于接口代理和对特定接口的方法增强,而基于类的动态代理适用于对类的方法进行代理和增强,无需依赖接口的情况

       基于接口的动态代理不需要第三方库,只需要有JDK即可,今天着重来讲解一下基于接口的动态代理

三.动态代理例子

下面一个例子解释下基于接口的动态代理

  首先定义一个接口Calculator

public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);

}

创建一个CalculatorImpl实现类 

public class CalculatorImpl implements Calculator {

    @Override
    public int add(int i, int j) {

        int result = i + j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int sub(int i, int j) {

        int result = i - j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int mul(int i, int j) {

        int result = i * j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int div(int i, int j) {

        int result = i / j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    public void run(){
        System.out.println("run......");
    }

}

创建一个获取代理对象的类

public class ProxyFactory {
//目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回代理对象
    public Object getProxy(){
        /*1.加载动态生成代理类的类加载器
        *2.目标对象实现的所有接口的class类型数组
        * */
        //第一个参数
        ClassLoader classLoader = target.getClass().getClassLoader();

        //2.第二个参数
        Class<?>[] interfaces = target.getClass().getInterfaces();

        //3.第三个参数
        InvocationHandler invocationHandler = new InvocationHandler() {
            //第一个参数:代理对象
            //第二个参数:需要重写目标对象的方法
            //第三个参数:method方法里的参数
            @Override
            //args  proxy.add(1, 2);传过来的参数
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("日志-动态代理-前"+method.getName());
                Object result = method.invoke(target, args);
                System.out.println("日志-动态代理-后"+method.getName());
                return result;
            }
        };

      //会通过我们传入的interfaces生成代理类
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }

}

创建一个测试类

public class ProxyTest {
   @Test
    public void test1(){

       ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
       // Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);会产生一个代理类,是系统自动生成的

       Calculator proxy = (Calculator) proxyFactory.getProxy();

        //调用proxy.add(1, 2)实际会在生成的代理类中的add方法中执行,在InvocationHandler的invoke()方法,而invoke方法是我们写的增强的方法
       //invoke()方法   1.完成目标方法的执行   2.完成代理逻辑的执行

       int add = proxy.add(1, 2);
      
   }

}

结果

日志-动态代理-前add
方法内部 result = 3
日志-动态代理-后add

由此可以看出动态代理实现的方法增强

四.动态代理的流程步骤

1.首先当调用Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);时会根据我们传入的三个参数,程序自动产生一个代理类 例:$Proxy0(在内存动态生成的类,根据传入的参数自动生成的,不需要我们编写)

其中的三个参数分别是

  1. classLoader(类加载器):指定用于加载动态代理类的类加载器。可以通过目标对象的 getClass().getClassLoader() 方法获取其类加载器,或者使用特定的类加载器来加载代理类。该参数是一个 ClassLoader 类型的对象。

  2. interfaces(接口数组):指定要代理的接口列表。代理对象将实现这些接口。可以传入一个接口数组,即 new Class[]{Interface1.class, Interface2.class}。这些接口定义了代理对象要实现的方法。

  3. invocationHandler(调用处理程序):指定用于处理代理对象方法调用的对象。该对象必须实现 InvocationHandler 接口。

2.生成的代理类的具体代码

例如上面的例子

//生成的代理类会自动继承Proxy类,并且实现Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);中的interfaces接口数组中的每个接口,此处只有一个接口

public class $Proxy1  extend Proxy  implements Calculator{

InvocationHandler h;

public $Proxy1(InvocationHandler h) {

this.h = h;

}

//它会实现接口中的所有方法,此处我仅以一个方法来演示

@Override
    public String  add(int p0,String p1) {
        try {

            //使用了 Java 反射技术,获取了add方法的具体信息
            Method md = Calculator.class.getMethod("add",new Class[]{ int.class,String.class});
       

      //此处调用了InvocationHandler 实现类里面的invoke()方法,此处this表示此代理对象 $Proxy1,md表示add方法,new Object[] {p0, p1,}表示方法里的参数

         return  h.invoke(this, md,new Object[] {p0, p1,})
     

  }catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

........其他的实现了接口的方法

3.前面的例子当调用proxy.add(1, 2)实际会在生成的代理类中的add方法中执行,从而调用InvocationHandler的invoke()方法,而invoke方法是我们写的增强的方法

其中//invoke()方法需要完成以下两个任务

1.完成目标方法的执行 2.完成代理逻辑的执行

最终得出结果

五.总结

1.代理类是程序运行的过程中,根据被代理的接口来动态生成代理类 $Proxy0 的 class 字节码文件

2.产生的代理类 $Proxy0 默认继承了 Proxy 类,同时实现了被代理类的接口(如 Calculator);所以才能强制将代理对象转换为被代理类的接口的类型(如 Calculator),然后可以调用 $Proxy0 中的 add() 方法

3.而在代理对象 $Proxy0 调用其实现了接口方法(如 add())时,其本质就是使用了代理对象 $Proxy0 调用了 invoke() 方法

4.而 invoke() 方法的实现是自定义的,如 CustomInvocationHandler类,在这个类中可以处理相应的代理业务逻辑

5.JDK 动态代理不能对类进行代理:因为代理类($Proxy0)已经继承了 Proxy 类,由于 Java 语言只支持单继承,所以 JDK 动态代理不能对类进行代理,只能对接口类型进行代理

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值