Featured image of post JDK动态代理

JDK动态代理

JDK动态代理

静态代理

理解为租客,中介和房东之间的关系,中介就是代理

先定义一个接口

1
2
3
4
5
6
7
8
package Proxy;

public interface IUser {
    void show();
//    void create();
//    void update();

}

然后写一个类来实现

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

public class UserImpl implements IUser {
    public UserImpl() {
    }
    @Override
    public void show() {
        System.out.println("show");
    }
//    @Override
//    public void create() {
//        System.out.println("create");
//    }
//    @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 Proxy;

public class UserProxy implements IUser {
    IUser user;

    public UserProxy() {
    }

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

    @Override
    public void show() {
        user.show();
        System.out.println("show");
    }
//    @Override
//    public void create() {
//        user.create();
//        System.out.println("create");
//    }
//    @Override
//    public void update() {
//        user.update();
//        System.out.println("update");
//    }
}

然后调用这个代理类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        IUser user = new UserImpl();
//        user.show();
        IUser userProxy = new UserProxy(user);
        userProxy.show();
    }
}

缺点就是如果我们接口需要多写一个功能,就要写大量重复的代码,而且想到通过反射来实现,我们获取不到对应的方法,也实现不了

动态代理

首先就是要用到Proxy这个类,然后里面的newProxyInstance方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(loader, interfaces);

        return newProxyInstance(cons, h);
    }

可以看到它需要一个类加载器,若干个接口数组,第三个就是我们想要用这个代理做的事情

前两个参数都可以这样获取

1
Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),h);

然后下面我们写第三个参数,新建一个类继承InvocationHandler接口

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

所以我们这个类就是要重写invoke方法

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

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

public class UserInvocationHandler implements InvocationHandler {
    IUser user;

    public UserInvocationHandler() {
    }

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(user,args);
        return null;
    }
}

接着就可以填上前面缺的第三个参数了

1
2
UserInvocationHandler handler = new UserInvocationHandler(user);
IUser userProxy= (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),handler);

想要实现什么重复的功能,只需要再前面那个类中写就行了,因为它会帮我们获取方法

反序列化中动态代理的应用

假设有一个最终能利用的类B

B.f能命令执行,然后我们现在有一个入口类A,

我们希望有一个参数O,使得A.[O] -> O.f来实现,但是实战往往不能实现

如果这个参数O是一个动态代理类,我们可以用重写invoke方法,调用f方法,来实现利用

这跟readObject方法一样,invoke方法在动态代理会自动执行

使用 Hugo 构建
主题 StackJimmy 设计