JDK动态代理

静态代理

首先定义一个接口,实现类,以及一个代理类

1
2
3
4
5
package com.sherlock;

public interface IUser {
void show();
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.sherlock;

public class UserImpl implements IUser{
public UserImpl(){

}

@Override
public void show() {
System.out.println("展示");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.sherlock;

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");
}
}

下面我们就来测试一下

1
2
3
4
5
6
7
8
9
10
package com.sherlock;

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

静态代理其实就相当于起到了日志记录的功能,比如上述例子中打印调用了show这句话不可能在实现类中出现,只能够在代理类中输出

但是静态代理有一个劣势,就是接口有时候所需要的实现方法不止一个,然后即使是进行重复性的方法代理(如同样加入一个记录日志的功能),也需要在代理类中把方法一个一个都实现了,过于繁琐,那么要怎么解决呢

JDK底层为我们提供了一个解决方案,那就是动态代理

动态代理

一个重要的方法,用于代理一个对象:

1
Proxy.newProxyInstance

我们跟进方法,参数需要一个类加载器对象,一个接口数组对象以及一个InvocationHandler对象;返回的是Object对象

image-20241107150229917

对于InvocationHandler对象,重写一下该invoke方法,此处的invoke()方法就是在于解决静态代理中需要重复重写方法的缺陷,动态代理的好处就是能获取到外部调用的那个方法,然后通过反射来到内部来执行被代理对象的方法

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

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;
}

//invoke方法怎么获取到的method我们不需要了解
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//打印所调用的方法名
System.out.println("调用了"+method.getName());
//user为我们所代理的对象
method.invoke(user, args);
return null;
}
}

InvocationHandler中的invoke方法当外面有调用方法的时候都会自动执行

然后我们简单测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.sherlock;

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

public class ProxyTest {
public static void main(String[] args) {
IUser user = new UserImpl();
//动态代理
InvocationHandler userinvocationhandler = new UserInvocationHandler(user);
//代理了user对象,userProxy可以反射调用user对象的所有方法
IUser userProxy = (IUser)Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),userinvocationhandler);
userProxy.update();
}
}

其实newProxyInstance的第二个参数是获取到一个接口数组,所以我们也可以写成下面这种方式

1
2
3
IUser userProxy = (IUser)Proxy.newProxyInstance(user.getClass().getClassLoader(),
new Class[]{IUser.class},
userinvocationhandler);