IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java动态代理学习 -> 正文阅读

[Java知识库]java动态代理学习

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

image-20220318231256944

这是比较简单的方法,然后接下来我们通过别的类间接调用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();
    }
}

回显

image-20220318231227194

但是静态代理有个缺陷,就是如果我们的IUser接口中有很多类,那么其实现类Userimpl就要将这些方法都实现,并且我们的间接类UserProxy也要将这些方法一一实现,这就比较繁琐了

image-20220318231554333

而动态代理可以解决这个问题

动态代理

调用链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,跟进看一下

image-20220318232342805

其需要三个参数,(类加载器,接口,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()

image-20220318232632182

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用法差不多

学习链接

  1. Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)_哔哩哔哩_bilibili

大佬讲得很好,建议看看原视频

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:29:52  更:2022-04-07 22:30:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:32:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码