总结一下反射的一个应用--动态代理

      把《Java核心技术》与《Java编程思想》翻过去。反射一部分讲的大同小异。总体来说《Java核心技术》讲得比较拖沓,但是比较全面,《Java编程思想》讲得比较简略,第一遍看可能看不懂。

      因为之前有仔细研究过几个框架中XML配置文件的语法,发现反射中代理类的应用在框架中虽然是透明的,但是我依然能够感受它的存在。我甚至怀疑AOP的对象不是由编译器生成合成对象实现的,而是由代理对象实现的,因为面向接口编程的思想到处都是。

 

     一、 什么是代理类。

 

     现实中不可能为每个接口都准备好一个适配器类,毕竟Java并不支持多基类继承。有时候我们可能需要动态地生成一个对象,它的内部实现只是对某些方法的一个封装。通过这些封装我们可以实现AOP,比如在某个类的方法执行前、执行后写日志等等。它包含一个类加载器、一些接口列表和一个调用处理器类。

 

     一句话来讲,代理类就是在接口和接口实现类之间加上一个中间层,实现一些中间层特有的操作。

 

 

     二、如何构造一个调用处理器类?

 

 

    1 代理类必须实现InvocationHandler接口,幸运的是,我们只需要重写它的一个抽象方法--Invoke()。

 

    2 这个代理类中必须有一个局部动态代理对象--注意区分,代理处理类中还自带一个局部动态代理对象。这一般可以通过构造器获得。

 

    3 Invoke方法的写法:

        参数1 proxy对象。

        参数2 一个method对象--这是一个高阶函数式的写法。

        参数3 一个arg数组。

 

        必须抛出Throwable类型的异常。

 

 

        函数体内:

 

         (1) 加入自己想要的内容。

         (2) return method.invoke(proxy,args)。即使用反射的方法确实地执行这个方法method。

 

        一句话来讲,调用处理类就是必须以接口实现类为构造参数,对接口的每一个方法都要invoke一下的类。

 

 

     三、如何创建动态代理对象?

 

 

    使用静态工厂的方法,Proxy.newProxyInstance()。

    它的参数:

    1 一个类加载器,一般使用一个已经被加载的对象a上使用 a.class.getClassLoader()获取。

    2 一个希望实现的接口列表。注意,是一个Class类型的数组。

    3 以及一个调用处理器的对象,必须用一个实际上实现了接口的引用对象对它进行初始化。

 

   创建完毕后,记得将它转型为想要实现的某个接口类型,才能给引用赋值。

 

 

    四、动态代理对象如何工作?

 

    当这个代理对象x被赋予某种接口a的引用,然后调用a.b()方法时,虚拟机会执行以下操作:

 

   1 将请求转发到这个动态代理对象的调用处理器类。这个调用处理器类的构造器获得x的引用,将它赋予自己的局部动态代理对象X。b

   2 执行invoke方法,第一个参数传入局部动态代理对象X,第二个参数传入b(),第三个参数传入接口调用时赋予的参数(如果有的话)。

   3 执行invoke方法体,退出。

 

   每次调用x的任何一个方法,invoke方法都会被调用,每次调用第一个参数都是一样的。

 

 

 

 

刚写的一段示例代码:

 

 

 

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

/*
 * 这个类很像AOP的一种用法,在方法执行以前加入某些操作,只有中间层才能做到。*/

interface A
{
	void b();
	void c(String d);
}

class E implements A
{
	public void b()
	{
		System.out.println("This is b.");
	}
	
	public void c(String d)
	{
		System.out.println("This is c's d" + d);

	}

}

class myInvocationHandler implements InvocationHandler
{
	private Object obj;
	public myInvocationHandler(Object obj)
	{
		this.obj = obj;
	}
	
	public Object invoke(Object proxy, Method method,Object[] args) throws Throwable
	{
		System.out.println("This is the object of " + proxy.getClass().getName());
		System.out.println("The method name is  " + method.getName());
		if(args != null)//如果没有这个句子,会出现一个空指针异常。
				for(Object arg : args)
						System.out.println("The argument is  " + arg);

		return method.invoke(obj, args);//极度要注意,这里的obj与proxy不同,proxy的传值我们没有办法控制,我们只能控制object的。
	}
}
public class MyProxy {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		E e = new E();
		A a = (A) Proxy.newProxyInstance(e.getClass().getClassLoader(),//这个classloader的获取做法很奇怪。但是却不能用null。 如果不是强制转换为A类型的话,就可以了。
				new Class[] {A.class}, 
				new myInvocationHandler(e));//这就把e传进调用处理器里面去了。
		Object b = Proxy.newProxyInstance(null,new Class[] {Comparable.class }, new myInvocationHandler(e));//如果要实现的接口不是自定义的,就可以使用null作为类加载器了。如果使用a.class,则必须使用a.getClass.getClassLoader()方法。
		a.b();
		a.c("liang");
		a.c("123");

	}

}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值