博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaWeb学习-动态代理-2-invoke()方法和动态代理Waiter类练习
阅读量:4301 次
发布时间:2019-05-27

本文共 3419 字,大约阅读时间需要 11 分钟。

这篇来学习invoke()方法中三个参数具体含义和基本使用,然后做一个动态代理的练习,写一个服务员类相关的动态代理实现过程。

 

invoke()方法

详细来看看这个,前面知道InvocationHandler是一个接口,这个接口只有一个方法,那就是invoke()。

我们开详细看看这个invoke()方法中三个参数和返回值。我们先来在A接口中写一个aaa()方法。

package proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import org.junit.Test;public class Demo1 {	@Test	public void fun1() {		// 1.获取ClassLoader		ClassLoader loader = this.getClass().getClassLoader();		// 2.调用处理器		InvocationHandler handler = new InvocationHandler() {			public Object invoke(Object proxy, Method method, Object[] args) 					throws Throwable {				System.out.println("你好,动态代理");				return "xxx";			}		};				// 3.得到代理对象		Object o = Proxy.newProxyInstance(loader, new Class[] {A.class, B.class}, handler);				// 强制转换成A 或者B 类型		A a = (A) o;		B b = (B) o;		// 以下代码证明:代理对象的几乎所有方法都会调用InvocationHandler的invoke()方法		/*		 * a.a(); a.aa(); b.b(); b.bb(); System.out.println(a.getClass());		 */		Object result = a.aaa("hello", 100);		System.out.println(result);	}}interface A {	public void a();	public void aa();	public Object aaa(String s, int i);}interface B {	public void b();	public void bb();}

运行上面单元测试,输出

你好,动态代理xxx

为什么会输出xxx呢,我们来看看下面这张图。

上面这张图,我们来看三个参数和代理对象a调用方法aaa()的一一对应关系。

Object proxy: 这个就是只代理对象,也就是当前对象,上面代码中a这个对象。

Method method: 当前被调用的方法,例如aaa()方法

Object[] args: 实参,例如aaa(“hello”,100), “hello”和100就是实参。

这三个参数,在代码运行中会自动作为参数传入,最终调用的就是invoke()方法,返回什么取决于,invoke()方法中的返回值。

      

       了解了动态代理使用过程中的核心,也就是newProxyInstance()方法的详细过程。我们发现我们使用代理对象来调用目标方法,结果无论调用什么方法,最终都是执行invoke()方法。那么我们就产生疑问了,我们在前面代码中写的A类的a() aa()这些方法还有什么作用,既然invoke()方法中用不到a()方法,岂不是白写了这段方法代码。本篇就来解开这个问题。

 

创建Waiter接口

开始新建一个Waiter接口,表示服务员,里面有一个方法server().

package demo;// 描述一个服务员public interface Waiter {	// 服务	public void serve();}

 

创建一个ManWaiter类实现Waiter接口

我们来创建一个男服务员并实现serve()方法。

package demo;public class ManWaiter implements Waiter {	public void serve() {		System.out.println("服务中...");	}}

在没学习代理之前,我们只能创建一个ManWaiter具体的对象,然后对象调用serve()方法,结果就是打印“服务中…”。这里我们认为,这太单调了,我们要新加需求。要实现的场景是,调用serve()方法,可以打印“你好,欢迎光临!” ,然后执行相关服务,最后,执行打印“再见!”。

 

上面的需求就是需要给serve()方法增强,说到增强,我们可以能会想起装饰者模式和当前学习的动态代理对象去解决,下面我们就用动态代理来实现这个需求。

 

新建一个Demo1类

新建一个Demo1类来写测试代码。

package demo;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import org.junit.Test;public class Demo1 {	Waiter manWaiter = new ManWaiter(); // 目标对象		@Test	public void fun1() {		// 给出三个参数,来调用方法得到代理对象		ClassLoader loader = this.getClass().getClassLoader();		Class[] interfaces = {Waiter.class};		InvocationHandler h = new WaiterInvocationHandler(manWaiter);		// 代理对象		Waiter waiterProxy = (Waiter)Proxy.newProxyInstance(loader, interfaces, h);		waiterProxy.serve();	}}class WaiterInvocationHandler implements InvocationHandler {	private Waiter waiter;		public WaiterInvocationHandler(Waiter waiter) {		this.waiter = waiter;	}		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		System.out.println("您好,欢迎光临!");		waiter.serve();		System.out.println("再见!");		return null;	}	}

运行这个单元测试,输出结果如下:

您好,欢迎光临!服务中...再见!

 

这里我们看到,虽然在代理对象代码中我们调用了serve()方法,但是由于我们自己处理过了InvocationHandler中的invoke()方法,所以我们通过对invoke()方法的代码增强,来到达对代理对象的增强。

 

上面的例子,回答了文章开头的问题,就是接口类的具体实现类的方法代码不是白写,虽然调用invoke()方法,但是我们可以在invoke()方法中去调用执行目标方法,就可以使用这些方法。

 

这里我们通过新写一个类实现InvocationHandler接口并重写invoke()方法,在新写的类的构造方法中,我们把接口对象作为参数传入。来复习下文章提到几个概念

代理对象:需要目标对象,然后在目标对象上添加了增强后的对象

目标对象:被增强的对象

目标方法:增强的内容,也就是新写的invoke()方法

 

代理对象 = 目标对象 + 增强

转载地址:http://zqxws.baihongyu.com/

你可能感兴趣的文章
Python debug 报错:SystemError: unknown opcode
查看>>
Python将树结构转换成字典形式的多级菜单结构,写入json文件
查看>>
关闭linux防火墙让windows宿主机访问ubuntu虚拟机web服务以及docker
查看>>
pycharm 找不到同目录文件,但是终端中正常的小记
查看>>
安装了grpc但是无法导入:ImportError: No module named 'grpc'
查看>>
Python中logging模块的基本用法
查看>>
Python查看第三方库、包的所有可用版本,历史版本
查看>>
一键将Python2代码转成Python3小记,
查看>>
Python要求O(n)复杂度求无序列表中第K的大元素
查看>>
Python 各种进制互相转换的函数
查看>>
python的单例理解、__new__、新式类object以及python2和python3下__new__的区别。
查看>>
Python动态规划以及编辑距离——莱文斯坦距离小记
查看>>
pycharm控制台报错:xmlrpc.client.Fault: Fault 0: 'java.lang.NullPointerException
查看>>
Python打印二叉树的左视图、右视图
查看>>
OpenStack Mitaka Horizon 主题开发
查看>>
OpenStack Mitaka keystone 分页(pagination)实现
查看>>
OpenStack删除Cinder盘失败解决办法
查看>>
Linux cpu 详解
查看>>
GitHub + Hexo 搭建个人博客
查看>>
Linux Ubuntu 修改网卡名字
查看>>