java代理

代理

当我们在用别人写的类的时候,如果想在某些方法上附加一些功能。比如在一个方法上添加这个方法运行时间的功能。我们无法更改别人的源代码,这时就用到了代理。

代理的原理如下图所示。

target是原类;

$Proxy1是这个类的代理类,里面具有同target相同的方法;

InvocationHandler是代理类的构造方法需要传入的一个参数对象;

client是客户,它来调用代理类。

当客户实例化代理类时,需要传入一个InvocationHandler对象。当客户调用代理类对象的某个方法时,这个方法中调用InvocationHandler中的invoke方法,invoke方法中再去调用target的对应方法,返回值原路返回。

在InvocationHandler的invoke方法中,我们就可以加入自己想要的附加功能了。



动态代理类:

第一种方式实例化代理类

我们只要指定代理类要实现的接口和类加载器,JVM就会自动给我们生成一个动态代理类。

		//创建一个动态类,实现Collection接口
		Class clazzProxy = 
				Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

然后调用构造方法实例化这个代理类,需要传入InvocationHandler对象,用匿名内部类的方式实现。

下面的例子中加入了方法运行时间附加功能。

Collection colProxy = 
				(Collection)clazzProxy.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
					List target = new ArrayList();
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						long begintime = System.currentTimeMillis();
						Object obj = method.invoke(target, args); //调用target的对应方法
						long endtime = System.currentTimeMillis();
						System.out.println(method.getName()+"----"+(endtime-begintime));
						return obj;
					}
					
				});
用代理类对象调用方法:

		colProxy.add("abc");
		colProxy.add("haha");
		System.out.println(colProxy.size());

第二种方式实例化代理类

创建动态类及其实例对象我们需要三个参数:

1. 指定类加载器

2. 需要实现的接口(可以是多个)

3. InvocationHandler对象

所以,java中还有一种实例化代理类的方式

		Collection colProxy = (Collection)Proxy.newProxyInstance(
				//类加载器参数
				Collection.class.getClassLoader(), 
				//需要实现的接口
				new Class[]{Collection.class}, 
				//InvocationHandler对象
				new InvocationHandler(){
					List target = new ArrayList();
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						long begintime = System.currentTimeMillis();
						Object obj = method.invoke(target, args);
						long endtime = System.currentTimeMillis();
						System.out.println(method.getName()+"----"+(endtime-begintime));
						return obj;
					}
				});

InvocationHandler有三个传入参数:

1. Object proxy 这是使用InvocationHandler的代理类的对象;

2. Method method 这是用户在调用代理类的哪个方法;

3. Object[] args 这是method中传入的参数。


以上两种方式实现的功能是一样的。第二种更简单。

AOP(Aspect Oriented Program)面向方面编程

在上面的例子中,附加功能是直接写在InvocationHandler里面的,但是我们每个人都有不同的附加功能需求,那我们就应该把这部分代码提取出来,封装成对象。
所以我们先写一个Advice类来说明我们想要实现的附加功能
public class Advice {
	long begintime = 0;
	public void methodBefore(Method method){
		begintime = System.currentTimeMillis();
	}
	public void methodAfter(Method method){
		long endtime = System.currentTimeMillis()+10;
		System.out.println(method.getName()+"----"+(endtime-begintime));
	}
}

然后将Advice类的实例对象作为参数传入操作代理类的函数。target是原类,也可以作为参数传入。

	public static Object proxydemo4(final Object target, final Advice advice){
		
		Object colProxy = Proxy.newProxyInstance(
				//类加载器参数
				target.getClass().getClassLoader(), 
				//需要实现的接口
				target.getClass().getInterfaces(), 
				//InvocationHandler对象
				new InvocationHandler(){

					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						//Advice类中定义的附加功能方法
						advice.methodBefore(method);
						//调用target的对应方法
						Object obj = method.invoke(target, args);
						//Advice类中定义的附加功能方法
						advice.methodAfter(method);
						return obj;
					}
				});
		return colProxy;
	}

然后我们在主程序里面就可以调用这个函数了

		final Advice advice = new Advice();
		final List target = new ArrayList();
		List reVal = (List)proxydemo4(target, advice);
		reVal.add("abc");
		reVal.add("haha");
		System.out.println(reVal.size());

上面的实现方法是是将原先的定死的代理功能变成可以自定义的功能,用户只要编写Advice类就可以用proxydemo4这个方法。
这就是面向方面的编程思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值