Java代理

Java代理 —— 动态代理

有一个表示接口的Class对象,它的类型在运行阶段才能确定。想要实例化实现这个接口类,就需要动态的构建一个类

动态构建类有下面两种方法:

  1. 自动生成代码,然后调用编译器,然后再加载编译后的类。很显然效率不高
  2. Java的代理机制。

下面,重点介绍一下Java的代理机制。

补充知识

在介绍完整内容之前,先介绍一下反射中的一些小知识。

每一个方法,都可以用一个Method对象封装表示,同样,通过这个Method对象就可以调用这个方法。

Method对象的invoke方法:public Object invoke(Object implicitParameter, Object[] explicitParameters)
@implicitParameter方法的隐式参数(调用该方法的对象)
@explicitParameters方法的显式参数(调用方法时传递的参数)
一个方法的调用,可以等价的使用invoke调用:
t.add(1, 2)m.invoke(t, new Object[] {1, 2})是等价的调用

	Test t = new Test();
	Method m = Test.class.getMethod("add", int.class, int.class);
	t.add(1, 2);
	m.invoke(t, new Object[] {1, 2});

对于静态方法来说,没有隐式参数,invoke的第一个参数就是null

Java代理

可以看到,使用Java代理就是要动态的实例化一个实现了某个接口的类(动态,就是在运行期间才决定使用哪一个接口)

也就是说,使用代理机制会创建一个新的类,并且实例化这个动态生成的类

动态生成的类有哪些方法?

  1. 指定的接口。(就是我们前面说的在运行阶段决定要实现的接口)
  2. Object类中的全部方法。

创建一个代理对象

代理对象,也就是上面说的“创建的新类”的实例。使用一个静态方法static Object newProxyInstace(ClassLoader loader, Class<?>[]interfaces, InvocationHandler handler)

参数:

ClassLoader loader : 类加载器,关于类的加载器,以后再介绍
Class<?>[] interfaces : 一个Class对象的数组,表示代理对象要实现的接口
InvocationHandler handler : 调用处理器

下面详细的说一下调用处理器:

调用处理器是一个实现了InvocationHandler接口的类的实例,InvocationHandler中有一个方法Object invoke(Object proxy, Method method, Object[] args)

代理对象实现了指定的接口(通过上面说的interfaces参数指定),因此,代理对象可以调用接口定义的方法,但是,代理对象调用方法时,并不是直接调用,而是通过这个调用处理器的invoke方法间接调用(因为虽然指定了实现哪些接口,但是对于这些方法,并没有给出方法的实现)

也就是说,代理对象每次调用一个方法时,其实都是调用了调用处理器的invoke方法

举个例子:假设我们创建了一个代理对象proxy,代理对象中有一个doSomething()方法
proxy.doSomething()在处理时,调用了handler.invoke(this, m, args)handler是刚说过的调用处理器 先不用管this的问题,看后两个参数(这里写的不规范),m是封装doSomething()这个方法的对象,args是调用这个方法时的参数,handler.invoke()会调用m.invoke(),这样就实现了间接调用。

现在来讨论间接调用的实现,需要实现handler的invoke方法

class ProxyHandler implements InvocationHandler{

	private Object realSubject;//被代理的角色
	
	public ProxyHandler(Object realSubject) {
		this.realSubject = realSubject;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl {
		return method.invoke(realSubject, args);//间接调用
	}
}

间接调用:method.invoke(realSubject, args);实现了间接调用,再重复一次,这个方法在代理对象调用方法时,实际会调用调用处理器的invoke方法,传递的参数上面也说过了。

分析间接调用:调用处理器中为什么要有realSubject?

method.invoke(realSubject, args)就相当于调用了realSubject.XXX(args),可见,这个参数必不可少,再来分析下本质原因,还有一个问题没有解决,创建代理对象需要指定接口,但是这个接口的方法的具体实现还没有解决,所以需要一个实现了接口的对象(被代理的对象),在调用时,通过这个被代理的对象调用相应的方法。

加深理解

在一开始就说过,代理的就是要动态的生成一个代理类,并且将它实例化。

这个生成的代理类的父类是java.lang.reflect.Proxy,java.lang.reflect.Proxy有一个字段,用来保存java.lang.reflect.InvocationHandler的成员,也就是说,动态生成的代理类中,保存着这个代理类的调用处理器。

理解代理对象方法的调用

代理对象调用方法时,会调用调用处理器的invoke方法,然后通过Method.invoke完成真实的调用。生成的代理类的方法的可能实现(可能是这样的,助于理解),假设生成的类的类名为$Proxy

class $Proxy{
	//生成的代理类的方法(继承了某个接口,这个接口中有个doSomething方法)
	void doSomething(Object arg1, Object arg2){
		//将参数(arg1,arg2)封装带一个args的数组中,准备调用调用处理器的invoke

		//获得封装这个方法的对象
		Method m = this.getClass().getMethod("doSomething", Object.class, object.class);
		//调用调用处理器的invoke方法
		h.invoke(this, m, args);
	}
}

h.invoke(this, m, args) 现在大家知道这个this是怎么来的了吧,调用处理器中的invoke方法中的第一个参数,就表示动态生成的对象。

将调用过程用对象图表示:
在这里插入图片描述

总结

好了,现在终于把 newProxyInstance中的最重要的参数介绍完了,接下来举个完整的例子:

//代理类要实现的接口
interface Subject{
	void doSomething();
}

//接口的实现类,它的实例的引用要保存在调用处理器中
class RealSubject implements Subject{

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

//调用处理器
class ProxyHandler implements InvocationHandler{

	private Object realSubject;
	
	public ProxyHandler(Object realSubject) {
		this.realSubject = realSubject;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return method.invoke(realSubject, args);
	}
}

public class Client {
	public static void main(String[] args) {
		Subject realSubject = new RealSubject();
		ProxyHandler handler = new ProxyHandler(realSubject);
		Subject proxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), new Class[] {Subject.class}, handler);
		
		proxy.doSomething();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值