静态代理&&动态代理

静态代理&&动态代理

静态代理

  • 真实类,代理类,都实现接口
  • 在代理类构造器中传入正常的真实类的对象(实现真实类,代理类对接),重写代理类中的方法即用真实类对象调用真实类方法(有点像将 正常的对象调用方法 封装到代理类的方法中来间接调用,对象也传进去),再添加增强的功能

示例代码

接口

1
2
3
4
5
6
7
8
package com.test;

public interface IUser {
public void show();
public void creat();
public void update();
}

真实类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.test;

public class UserImpl implements IUser {
@Override
public void show() {
System.out.println("展示");
}

@Override
public void creat() {
System.out.println("creat");
}

@Override
public void update() {
System.out.println("update");
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.test;

public class UserProxy implements IUser {
IUser user;

public UserProxy(IUser user) {
this.user = user;
}

@Override
public void show() {
user.show();
System.out.println("调用了show");
}

@Override
public void creat() {
user.creat();
System.out.println("调用了Creat");
}

@Override
public void update() {
user.update();
System.out.println("调用了Update");
}
}

执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.test;

import java.lang.reflect.Proxy;

public class ProxyTest {
public static void main(String[] args) {
//直接连接调用
IUser user = new UserImpl();//接口类引用指向实现类对象
// user.show();


//静态代理
IUser userProxy = new UserProxy(user);//传进真正的对象
userProxy.show();
userProxy.creat();
}
}

联想截图_20250407204639

疑问&&结论

学习ing对代理类对象调用 ‘与真实类的方法重名的 ‘即它重写的方法即 userProxy.show();有了困惑,为啥这么巧合,代理类中重写方法然后代理类对象调用同名的方法,来实现真实对象的方法的调用(同名对他们的调用有关系??)(它本质上不就是一个对直接调用的封装吗,那我把它装进代理类中单独写的一个方法不可以吗)

就进行下面的尝试,在代理类中写一个单独的方法(与实现类调用方法不重名,接口中也没有的),欸!这里要注意创建代理类对象就必须引用类型也是代理类的(或者是接口类的引用类型再强转成代理类的)才 能在后续中调用代理类独有的方法进而调用真正对象的方法,操作之后发现是可以的(具体部分代码如下)但是对比就感觉步骤繁琐了,这两种都重写了接口中的全部方法,但它还再写了一个方法太麻烦了

部分代理类代码

这里单独写了一个方法 show2 调用正常真实类的对象的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UserProxy implements IUser  {
IUser user;

public UserProxy(IUser user) {
this.user = user;
}

public void show2() {
user.show();
System.out.println("调用了show");
}
@Override
public void show() {
//user.show();
// System.out.println("调用了show");
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserImpl implements IUser {
@Override
public void show() {
System.out.println("展示");
}

@Override
public void creat() {
System.out.println("创造");
}

@Override
public void update() {
System.out.println("update");
}
}

执行

1
2
3
4
//静态代理
UserProxy userProxy = new UserProxy(user);//传进真正的对象
userProxy.show2();
userProxy.creat();

联想截图_20250407204639

所以最后得出结论它们之间的调用与同名无关,同名也是为了看着会清楚,知道这个代理对象调用了这个show方法意味着这个方法show里面会调用真正对象的重名方法show,也体现出接口它规范行为的这么一个作用吧

就干脆直接在代理类中重写实现类要调的方法,把真正掉的丢进方法里去,代理类调用与真实类的方法同名的方法也就是告诉你最后会调到真实对象的方法


但静态代理的缺陷:

接口中每增加一个功能方法,真实类,代理类都也需要重写,导致代码重复


静态代理用代理类对象调代理类方法来间接调真正类对象的方法,也在代理类中的通过重写方法实现了功能的增强,由此看出在代理模式中有两个重要的类,代理类和目标类。代理类是服务于目标类的,对其功能进行增强。


动态代理

  • 动态代理为了实现方法的增强,具体依赖实现InvocationHandler接口中的invoke方法 实现,invoke方法相当于静态代理中代理类重写的增强功能的方法

  • Proxy创建对象强转后调用方法,会触发实现InvocationHandler接口类的invoke()自动执行(具体在底层)

示例代码

接口IUser:

​ 实现类:UserImpl(真正类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UserImpl implements IUser {
@Override
public void show() {
System.out.println("实现类的展示");
}

@Override
public void creat() {
System.out.println("创造");
}

@Override
public void update() {
System.out.println("实现类的update");
}
}

实现接口InvocationHandler的类UserInvocationHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserInvocationHandler implements InvocationHandler {
IUser user;
public UserInvocationHandler() {
super();
}

public UserInvocationHandler(IUser user) {
this.user = user;
}

//invoke为了获取到外面调用的方法,得到Method
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("现在是invoke方法,调用了"+method.getName());
method.invoke(user,args);
return null;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
//直接连接调用
IUser user = new UserImpl();//接口类引用指向实现类对象
user.show();
//user.update();
System.out.println("====");

//静态代理
//IUser userProxy = new UserProxy(user);//传进真正的对象
//userProxy.show();
//userProxy.creat();


//动态代理
UserInvocationHandler userInvocationHandler = new UserInvocationHandler(user);//传user,为了调它的方法
//classloader,要代理的接口,要做的事情 (真正类的加载器,真正类实现的接口,处理器对象)
IUser userProxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userInvocationHandler);
userProxy.show();
System.out.println(userProxy.getClass());//class jdk.proxy1.$Proxy0 userProxy已经是一个代理对象了
}

联想截图_20250408211042

疑问

????静态代理用代理类对象调代理类方法来间接调真正类对象的方法,动态代理创建代理Proxy类对象后强转成接口类Iuser对象,再用这个Iuser对象调方法。。(那这这这和直接用接口类引用对象调方法有什么区别)(其实这儿不是Iuser对象,是代理对象~~)

这块的疑问的产生应该是没搞清动态代理的作用和它的底层逻辑咋实现的

下面参考原文链接:https://blog.csdn.net/xiao_yao_xian/article/details/146052177

java动态代理实现与原理详细分析 - Gonjian - 博客园

因为代理模式最后都是为了增强方法的功能,在静态代理中有手动创建代理类的比较容易理解,动态代理中我们的代理类并没有直接的展现在用户面前(它是在底层创建的),但是我们仍可以在调用方法时,获得增强的功能

底层

注意:下面接口为ServiceSell,要增强的方法为sell

一.

  • Proxy中的重要方法newProxyInstance,可以用来创建代理对象

    1
    2
    public static Object newProxyInstance ( ClassLoader loader, Class<?>[] interfaces,                                                  InvocationHandler handler);

    • 参数讲解

    loader:真正类的类加载器,通过目标对象的反射可获取

    interfaces:真正类实现的接口数组,通过目标对象的反射可获取

    ​ –这个接口相当于静态代理的接口,可以规范行为

    handler:调用处理器,也叫做方法拦截器,传的是一个实现了 InvocationHandler接口的 实现类的对象 它后面会在代理类中用到

    返回对象:经过一系列的操作后,返回的对象就是我们看不见的代理对象,非常重要!!!!!二中具体学习

  • 实现InvocationHandler接口的类中的invoke方法,它通过反射得到具体的方法,并实现了方法功能的增强

    1
    public Object invoke(Object proxy, Method method, Object[] args);

二.

通过参考上述文章得到了具体的代理类代码

  • 代理类的定义
1
2
public final class jdkProxy extends Proxy implements ServiceSell
//这里类的名字是上面文章中的文件名
    • extends Proxy:继承了代理类Proxy

    • implements ServiceSell: 实现了接口。 这儿的接口就是在Proxy类用newInstance创建对象时传的接口参数,这样才可以在自动生成这个类的时候实现该接口

    重点理解:和我们静态代理的代理类应该是一样的,因为实现了相同的接口

    这也契合了代理模式

  • 下面就是代理类中获得{要增强的方法}的具体方法,在底层就是通过反射获得了该方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21


    static {
    try {
    m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
    m2 = Class.forName("java.lang.Object").getMethod("toString");
    m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    m3 = Class.forName("dynamicproxy.jdk.ServiceSell").getMethod("sell", Integer.TYPE);
    }
    }

    -理解:我们知道,静态代码块是在类加载时就会执行,分析其执行的业务。

    从类的结构来看m0,m1,m2...都是Method的对象,表示一个方法

    这个静态代码块就是获得类的基本方法和根据接口实现的方法

    我们主要看m3

    m3 = Class.forName("dynamicproxy.jdk.ServiceSell").getMethod("sell", Integer.TYPE);根据这个接口我们得到了ServiceSell接口内的方法,也正是我们真正类的方法(需要增强的方法),因此我们可以理解m3就是真正类(Factory类)的sell

    发现在代理类中是有要增强的方法的

1
2
3
4
5
6
7
8
9
10
-sell()

public final void sell(int var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
}
}

super.h.invoke(this, m3, new Object[]{var1});

上面这是代理类中sell()方法的逻辑

super.h,在类的定义中,我们知道这个代理类是extends Proxy,所以想要了解这个h是什么,我们进入Proxy源码

联想截图_20250408214855

发现它是实现InvocationHandler接口的对象,而恰好在Proxy用newInstance创建对象时也传进去一个InvocationHandle类的对象!!

所以底层创建的代理类的sell方法(要增强的方法)中调用了InvocationHandle类的对象invoke方法,这个对象就是创建的实现InvocationHandler接口的对象,即示例代码组长讲的实现接口invocationHandler的类中重写的invoke方法,也是上述文章中的内部类方法

误区:调真正被代理的类的方法才触发动态代理类的invoke方法

代理类的具体代码是在底层中,proxy创建返回的就是代理类对象,它调方法就触发动态代理类的invoke方法


总结

核心:创建出的底层代理类它包含(要增强的)的方法中调用InvocationHandler 对象的invoke方法,创建时传参动态代理类对象—–>帮助我们实现增强方法功能

我们需要:

1.创建出底层代理类Proxy.newProxyInstance(类加载器,接口,InvocationHandler 对象)

2 .有一个实现接口InvocationHandler的类,创建出InvocationHandler 对象,他作为参数传进1中去(起连接的一个作用吧)

3 .重写invoke方法,在里面调用方法(使用反射的形式),并实现功能增强

  • 接口:底层代理类会实现这个接口(契合代理模式),且底层代理类中也会有接口中的方法(由反射获得),方法里面是调InvocationHandler对象的invoke
  • InvocationHandler 对象:==底层代理类包含的方法会调用InvocationHandler 对象的invoke方法,要是不传这个参数就没办法后续利用增强功能==(具体它其实是传到Proxy构造器了,然后代理类又是Proxy的子类,所以代理类方法中调invoke方法通过它就能拿出来利用了,去增强功能)

注意:Proxy.newProxyInstance(…)返回对象强转后是代理类型的(class jdk.proxy1.$Proxy0)

所以执行流程:

代理类对象调用方法—–>到底层代理类方法中调用InvocationHandler对象的invoke方法—–>到我们自己写的实现接口InvocationHandler的类中的invoke方法——>在invoke()中调用目标类的方法,并实现功能的增强


在反序列化中的一个作用

正常的话,这个O任意类型调目标方法:

A[O]->O.f 往A中传B即可实现调B的f

当这个O是动态代理类(实现接口InvocationHandler的),O它调的是别的方法不是目标方法时:

A[O]—->O.abc(虽然不是调的目标方法但这儿会触发O的invoke方法)

O[B]代理类传进去参数B—->invoke中调B.f 在invoke中增强功能,调目标f方法


静态代理&&动态代理
https://bxhhf.github.io/2025/04/09/静态代理&&动态代理/
作者
bxhhf
发布于
2025年4月9日
许可协议