Java动态代理

Java学习

Java动态代理

利用了代理模式,一个通俗易懂的说法:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。

简单说明

代理模式是Java中常见的设计模式。

其特征是代理类和委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把信息转发给委托类,以及事后处理消息等。

代理类与委托类之间通常会存在关联关系,一个代理类对象与一个委托类对象关联,代理类对象本身并不真正实现服务,而是通过委托类对象的相关方法来提供特定服务。

image-20250120193441331

静态代理

需要代理对象和目标对象实现同样的接口。

**缺点:**当需要的代理的对象过多就需要实现大量的代理类,并且当接口增加方法,目标对象与代理对象都要进行修改

直接用参考文章的一个demo来演示一下什么是静态代理,理解一下

Event.java:

1
2
3
4
5
6
//接口类
package java_foundation;

public interface Event {
    void sale();
}

Text.java

1
2
3
4
5
6
7
8
//委托类
package java_foundation;

public class Text implements Event {
    public void sale(){
        System.out.println("要卖房子了");
    }
}

MyTest.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//代理类
package java_foundation;

public class MyTest implements Event{
    private Event instances;
    public MyTest(Event a){
        this.instances = a;
    }

    public void sale(){
        instances.sale();
        System.out.println("售价2000元");
    }
}

最终的测试类:Test.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//测试类
package java_foundation;

public class Main{
    public static void main(String[] args) throws Exception{
        System.out.println("======使用代理前====");
        Text a = new Text();
        a.sale();
        System.out.println("======使用代理后====");
        MyTest b = new MyTest(new Text());
        b.sale();
    }
}

只要搞清楚对象的关系,这里的代码就不难理解了。最后结果如下:

1
2
3
4
5
======使用代理前====
要卖房子了
======使用代理后====
要卖房子了
售价2000元

也就是可以将代理设想为这里卖房子的中介,中介来给你定义具体的内容,你只需要把大体的说明就行了。

我们这里是用的接口类作为接收参数的类型,并且构造函数的接收的参数类型也都是接口类型。

有一个点说明一下,这里为什么这里要使用Event接口类型作为媒介呢,应该是因为委托类实现了Event接口,这样就可以使得相应的Text()对象可以成为这个类型,同时代理类MyTest也接口了Event接口,所以MyTest类也可以接受Event类型的数据。

这里最重要的实现代理的操作就是代理类中的如下代码:

1
2
3
4
public void sale(){
    instances.sale();
    System.out.println("售价2000元");
}

就是因为这串代码导致的最终输出为

1
2
要卖房子了
售价2000元

因为我们new后将instance对应到了委托类的对象,此时就会调用Text.java中的sale()方法,所以其实一般代理类的重要代码都会调用两次。简单流程图如下

image-20241225152844182

动态代理

与静态代理相同,需要公共接口,委托类,代理类。区别就是动态代理是利用反射机制在运行时创建代理类。这里需要用到位于Java.lang.reflect下的Proxy类InvocationHandler接口

  • InvocationHandler接口:负责提供调用代理的操作
1
2
3
4
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

其中proxy为代理类的实例对象,method表示调用的方法名,args[]为调用方法的参数数组

这个接口定义了一个invoke()方法,每个代理对象都有一个关联的接口。这个是很重要的:当代理对象上调用任意方法时,该方法会被自动转发到InvocationHandler.invoke()方法进行调用

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

位于java.lang.reflect.Proxy,提供了静态方法用于得到代理对象

1
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{}

继续使用前面的那个例子,结合代码,我们这里需要获得类加载器,相对应的方法,直接给代码:

委托类和接口不变,

Text.java:

1
2
3
4
5
6
7
8
//委托类
package java_foundation;

public class Text implements Event {
    public void sale(){
        System.out.println("要卖房子了");
    }
}

Event.java:

1
2
3
4
5
6
//接口类
package java_foundation;

public interface Event {
    void sale();
}

看其他需要修改的代码:

MyTest.java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//动态获取代理的代理类
package java_foundation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyTest implements InvocationHandler{
    private Object instances;
    public MyTest(Object a){
        this.instances = a;
    }

    //重写invoke()方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("售价2000元");
        Object result = method.invoke(instances,args);
        return result;
    }
}

在这个代理类重写的invoke()方法中,看到了很熟悉的操作,调用方法,对,就是调用了invoke()方法,结合在前面说明InvocationHandler接口类时,大概久能知道这里的调用逻辑。后面看了结果就知道了。

Main.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//测试类
package java_foundation;

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;

public class Main{
    public static void main(String[] args) throws Exception{
        //获取委托类的实例对象
        Text text = new Text();
        //获取CalssLoader
        ClassLoader classLoader = text.getClass().getClassLoader();
        //获取所有接口
        Class[] interfaces = text.getClass().getInterfaces();
        //获取一个调用处理器
        InvocationHandler invocatinoHandler = new MyTest(text);
        //创建代理对象
        Event proxy = (Event)Proxy.newProxyInstance(classLoader,interfaces,invocatinoHandler);
        proxy.sale();
    }
}

成功输出

1
2
售价2000元
要卖房子了

大概就是这这样,其实理解一下就行了,难度不是很高,重点关注invoke()中的代码。

(参考文章有一道题,后面可以学习一下)

参考文章:

https://xz.aliyun.com/t/9197?time__1311=n4%2BxuDgD9DyDnB7QGQD%2FD0WoQ4D55i%3D31YNGt4D

https://blog.csdn.net/DDDYSz/article/details/109451049

https://tttang.com/archive/1769/

https://www.cnblogs.com/whirly/p/10154887.html

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计