java动态代理学习
学习资料: Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)_哔哩哔哩_bilibili
首先有个接口IUser
package proxy;
//定义一个接口
public interface IUser {
void show();
void create();
void update();
}
然后我们建一个类Userimpl
去实现这个接口
package proxy;
//定义一个类去实现这个接口
public class Userimpl implements IUser {
public Userimpl(){
}
@Override
public void show() {
System.out.println("调用了show()方法");
}
public void create() {
System.out.println("调用了create方法");
}
public void update() {
System.out.println("调用了update方法");
}
/* public void execute(){
method.invoke(user,args);
}*/
}
直接调用
有类之后,我实例化一个对象,然后调用其中的方法
package proxy;
public class Test {
public static void main(String[] args) {
IUser user = new Userimpl();
//调用Userimpl类的show()方法
user.show();
}
}
这是比较简单的方法,然后接下来我们通过别的类间接调用Userimpl
中的方法
间接调用
首先来说间接调用,Userimpl
是目标类,我们要创建一个间接类(跳板),用这个间接类去调用Userimpl
中的方法
间接类UserProxy
调用链UserProxy-->Userimpl-->show()
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("在UserProxy中调用了show()方法");
}
public void create() {
user.create();
System.out.println("在UserProxy中调用了create方法");
}
public void update() {
user.update();
System.out.println("在UserProxy中调用了show()方法");
}
}
创建好间接类之后,测试Test.java
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args){
IUser user = new Userimpl();
//调用Userimpl类的show()方法
//user.show();
//静态代理
//IUser是个接口
//用代理,user可以直接调用show()方法,但是我们用代理去整
IUser userproxy = new UserProxy(user);
userproxy.show();
}
}
回显
但是静态代理有个缺陷,就是如果我们的IUser接口中有很多类,那么其实现类Userimpl
就要将这些方法都实现,并且我们的间接类UserProxy
也要将这些方法一一实现,这就比较繁琐了
而动态代理可以解决这个问题
动态代理
调用链InvocationHandler-->Userimpl-->show()
动态代理需要用到一个接口:InvocationHandler
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
我们要实现这个接口,用UserInvocationHandler
类去实现它
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//注意这里实现了InvocationHandler接口
public class UserInvocationHandler implements InvocationHandler {
IUser user;
public UserInvocationHandler(){
}
public UserInvocationHandler(IUser user){
this.user = user;
}
//重写InvocationHandler接口的invoke方法
//作用是 不管外部调用了什么,都会用这个invoke方法,将其作为method传进来
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(user,args);
return null;
}
}
然后开始运行ProxyTest.java
注意一个函数Proxy.newProxyInstance,跟进看一下
其需要三个参数,(类加载器,接口,InvocationHandler),前两个参数通过java反射可以得到,而后面这个InvocationHandler
我们已经通过类去实现接口,并且实例化为b,传进去
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args){
IUser user = new Userimpl();
//动态代理,,当有多个方法需要使用的时候
//需要Proxy类
//传 接口,要做的事情,类加载器
InvocationHandler a = new UserInvocationHandler(user);
//下面这个返回值是个Object
//下面这个的第二个参数可以是数组接口数组(new Class<?>[]{Iuser.class}),也可以是单个的接口
IUser b = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),a);
//然后调用show()方法
//可以看到虽然我们的UserInvocationHandler类中没有定义这些方法,但是一个invoke方法就可以将其全部解决,这就是和静态代理的区别
b.show();
b.create();
}
}
然后就可以通过b来进行调用,比如b.show(),b.create()
invoke
注意,这里我们的间接类UserInvocationHandler
其实只实现了invoke()这一个方法(构造方法除外)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(user,args);
return null;
}
具体的细节不用深究,这个方法会将外部调用的方法的名字(比如show()),传入method,执行
最后的思考
为什么要这个动态代理?
比如我们需要调用B.f,入口类A,可以向A传进去Object,A[O]
当O是一个动态代理类的时候,O中有invoke方法,利用invoke的自动执行
O[B].invoke–>B.f
invoke当遇到外部调用的时候,就会自动执行
这个和反序列化的时候自动调用readObject用法差不多
学习链接
大佬讲得很好,建议看看原视频