Java动态代理

本文深入介绍了Java动态代理的概念与原理,解析了动态代理如何在不修改原有代码的基础上增强功能,并详细阐述了其实现过程,包括关键接口与类的使用。

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

Java动态代理

1什么是动态代理

所谓代理,就是需要代理类和被代理类有相同的对外接口或者说成服务,所以代理类一般都必须实现了所有被代理类已实现的接口,因为接口就是制定了一系列对外服务的标准。 代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。正因为动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实主题类(RealSubject)相同的接口(interface),而是把这种实现推迟到运行时。为了能让DynamicProxy类能够在运行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带java.lang.reflect.InvocationHandler接口,该接口中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。

按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有interface都加载到JVM中,那么我们就可以创建一个被代理类的实例,获得该实例的类加载器ClassLoader。所谓的类加载器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包括继承树、方法区等等)。

更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。其中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作。这是动态代理最明显的优点。

2具体实现

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。

  • java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。
该方法负责集中处理动态代理类上的所有方法调用。
 第一个参数既是代理类实例,
 第二个参数是被调用的方法对象
 第三个方法是调用参数。
 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

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

从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

  • Java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy) 

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader, 
Class<?>... interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl) 

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader,
 Class<?>[] interfaces,InvocationHandler h)

它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

java动态代理创建对象的过程为如下步骤:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
  • 使用Proxy类中的newProxyInstance方法:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); 

// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 
     new Class[] { Interface.class }, 
     handler );

用实例来看动态代理的实现

  • 定义一个接口:
public interface SayThing {
		public void say();
		public void saything(String name);
		
}
  • 创建一个类实现这个接口:
public class SayThingImpl implements SayThing{

	@Override
	public void say() {
		System.out.println("被代理对象的say方法");
		
	}

	@Override
	public void saything(String name) {
			System.out.println(name);
		
	}
		
}
  • 定义一个动态代理类,要实现 InvocationHandler 这个接口:
public class Handlers implements InvocationHandler {
	SayThing sayThing;//要代理的真实对象
	public Handlers(SayThing sayThing) {
			this.sayThing = sayThing;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("在执行代理对象的方法之前");//代理对象方法前实现自己要添加的方法
		method.invoke(sayThing, args);//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
		System.out.println("在执行代理对象的方法之后");//代理对象方法后实现自己要添加的方法
		return null;
	}

}
  • 最后看主类:
public class Proxy_Test {

	public static void main(String[] args) {
			// 我们要代理的真实对象
			MyDothing myDothing = new MyDothing();
			// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
			InvocationHandler handler = new MyProxy(myDothing);
			/*
			 * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
			 * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
			 * 第二个参数myDothing.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
			 * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
			 */
			DoThing doThing = (DoThing) Proxy.newProxyInstance(handler.getClass().getClassLoader(), myDothing.getClass().getInterfaces(), handler);
			System.out.println(doThing.getClass().getName());
			doThing.say();
			doThing.saything("2333");
			

	}

}
  • 控制台输出:
在执行代理对象的方法之前
被代理对象的say方法
在执行代理对象的方法之后
在执行代理对象的方法之前
23333
在执行代理对象的方法之后
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值