从内存的角度谈谈JDK的动态代理

本文通过实例详细介绍了静态代理和动态代理的概念与实现方式。通过明星与其经纪人的类比,阐述了代理模式的设计思想,展示了如何使用Java实现这两种代理模式,并重点讲解了JDK动态代理的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是代理

    想要弄清楚动态代理,首先得明白代理是什么。下面直接看静态代理的小Demo,解释代理的含义。

    1.定义一个Star("明星")接口

package com.study.staticProxy;

public interface Star {
	void conversation();
	void signContract();
	void bookTicket();
	void sing();
	void collectMoney();
}

    2.定义一个类JayZ("周杰伦")实现 Star 接口

package com.study.staticProxy;

public class JayZ implements Star {

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

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

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

	@Override
	public void sing() {
		System.out.println("***周杰伦在唱歌****");
	}

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

}

    3.定义一个类StarProxy(类似"经纪人"的角色)实现Star接口

package com.study.staticProxy;

public class StarProxy implements Star {
	//
	private Star star;
	//这两步是关键步骤
	public StarProxy(Star star) {
		this.star=star;
	}
	
	@Override
	public void conversation() {
		System.out.println("StarProxy.conversation()");
	}

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

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

	@Override
	public void sing() {
		star.sing();
	}

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

}

    4.定义Client("用户")测试类

package com.study.staticProxy;

public class Client {
	public static void main(String[] args) {
		Star jayz=new JayZ();
		Star proxy=new StarProxy(jayz);
		
		proxy.conversation();
		proxy.signContract();
		proxy.bookTicket();
		proxy.sing();
		proxy.collectMoney();
	}
}

    输出结果:

StarProxy.conversation()
StarProxy.signContract()
StarProxy.bookTicket()
***周杰伦在唱歌****
StarProxy.collectMoney()

    代理就是一种“中介”,类比上面现实生活的例子,“经纪人”就是“明星”的代理。“明星”的核心业务只管好sing()唱歌就行,其他琐碎的事,交给经纪人来做。代理这种设计模式的意义也在于此,它可以使真正的业务逻辑和琐碎的流程分开,每个类专注自己所要做的事。感叹面向对象编程的奇妙,只需一个接口,两个实现类,就可实现如此深远的意义!

代理的分类
  • 静态代理,前面的栗子(静态定义代理类,我们自己定义的代理类)。
  • 动态代理(指由程序自动生成代理类):JDK自带的动态代理,Javaassist字节码操作库实现,CGLIB,ASM(底层使用指令,可维护性较差)

JDK的动态代理

   好了,对代理有个概念之后,现在来谈谈JDK的动态代理。

  • 什么叫动态的代理?-->就是指代理对象是个“变量”,它可以根据需求再产生
  • 怎么产生动态代理呢?-->首先,想产生对象,那么必须得有个模板,那就是“类”。而类又是怎么来的呢,通常,我们是通过类加载器加载硬盘上的.class文件加载到内存中,准确的说是方法区中,类的信息就是这样来的。然后,再通过类信息在中创建出对应的Class对象。而现在硬盘上,我们可没有代理类的.class文件哦,其实我们是根据程序Proxy.newInstance()创建出对应的类信息以及类对象。

    还是用上面的栗子,Star接口、JayZ类不变

    1.添加StarHandler处理类(注意:处理类不是代理类

package com.study.dynamicProxy;

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

public class StarHandler implements InvocationHandler{
	
	private Star star;
	
	public StarHandler(Star star) {
		this.star=star;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("谈判");
		if(method.getName().equals("sing")) {
			method.invoke(star, args);
		}
		System.out.println("付钱");
		return null;
	}

}

    2.修改原来的Client测试类:

package com.study.dynamicProxy;

import java.lang.reflect.Proxy;

public class Client {
	public static void main(String[] args) {
		Star jayz=new JayZ();
		StarHandler handler=new StarHandler(jayz);
		Star proxy=(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] {Star.class}, handler);
		proxy.conversation();
		System.out.println("-----------------");
		proxy.sing();
	}
}

    打印结果:

谈判
付钱
-----------------
谈判
***周杰伦在唱歌****
付钱
StarHandler类:
  • 实现InvocationHandle接口的类是一个“中间类”,或者说是“处理类”,本身不是代理类,作用是:把真正的对象和代理对象连接起来,就像混凝土一样把两块砖连接起来。
Proxy.newProxyInstance(loader, interfaces, h)
  • 第一个参数类加载器,无所谓哪个类的类加载器;
  • 第二参数是想要在字节码里面加载的接口,以便于得到接口的方法;
  • 第三个参数InvocationHandle ih相当于是.class字节码里面的属性,以便接口各个方法调用ih.invoke()方法。从内存的角度就很好理解了(画栈堆方法区图)。
主要作用:
  • 创建一个“新的”类信息(字节码信息,在字节码里面加载相关的接口);
  • 在根据此类信息创建对象于堆上;
  • 把堆中的对象赋给栈中的方法。

栈堆图分析:如下图所示,“新的”Proxy代理类的信息结构


    当客户端调用proxy.sing()时,就是在调用方法区里面的sing()方法,从而会激活处理类StarHandler的invoke()方法,再根据invoke()方法里的条件判断,看是否激活method.invoke(star, args)。

    此处主要用到了反射和类加载的知识,首先得把这两块的知识点摸个明白。

    好了,主要流程已经讲完了,纪念一下我第一篇博客!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值