java动态代理模式(jdk和cglib)

为什么需要代理

假设有一个计算器类,类中定义了加,减,乘,除操作,然后我希望在每个方法执行前后打印一些提示信息,比如 “XX方法开始执行。。”,“XX方法结束执行。。”,要做到这一点,普通的解决方式是在每个方法前后加上 System.out.println("XX方法正在执行。。"),System.out.println("XX方法结束执行。。"),这样写起来十分麻烦,如果我们以后要扩充这个类,要加上乘方,开方等方法,还要再加上提示信息,代码及其冗余,也不便于维护。

代理模式可以解决这种囧境,利用这种模式,可以将一些通用的操作抽取出来,比如记录日志,权限管理等等,这些操作在很多地方都能用到,将这些操作单独拿出来,运行时动态加进去,这时我们希望的。

代理分为静态代理:主要通过继承和组合的方式实现,以及下面主要介绍的动态代理。

此外代理模式是Spring框架中AOP的基础,所以理解代理模式非常重要。

使用jdk自带的库来实现动态代理

被代理的类需要实现特定的接口,我们先定义这个接口:

package com.hunan.proxy;

public interface Moveable {
	//move方法,参数为移动速度,返回移动的总路程
	public double  move(double speed);
}

接下来我们写一个Handler为所有实现这个接口的类做代理,上面的接口中声明了move方法,我们希望再执行move方法前后记录日志,利用代理来将日志处理模块提取出来:

package com.hunan.proxy;

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

public class LogHandler implements InvocationHandler {
	private Object target;
	
	public LogHandler(Object target) {
		super();
		this.target = target;
	}

	/*
	 * 参数说明:
	 * proxy 被代理的对象
	 * method 被代理对象的方法
	 * args 方法的参数
	 * 
	 * 返回值:
	 * Object  被代理的对象的方法的返回值
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 本例仅为演示动态代理,具体的日志处理过程就不具体实现了,仅打印一条提示信息。
		System.out.println("开始记录日志。。。");
		Object obj =  method.invoke(target, args);
		System.out.println("日志记录完毕。。。");
		return obj;
	}

}

注意:代理是代理某个特定接口的实现类,而不是代理某个特定类,所以尽量用Object引用指向对象,使代码有更好的通用性。

第三步实现接口,用线程休眠的方式模拟汽车行驶:

package com.hunan.proxy;

import java.util.Random;

public class Car implements Moveable {

	@Override
	public double move(double speed) {
		System.out.println("汽车行驶中。。。");
		int moveTime =  new Random().nextInt(3000);
		try {
			Thread.sleep(moveTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return speed*moveTime;
	}

}

最后写个main方法测试一下:

package com.hunan.proxy;

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

public class Main {

	public static void main(String[] args) {
		Car car = new Car();
		InvocationHandler h = new LogHandler(car);
		Class<?> cls = car.getClass();
		
		/*
		 * loader:类加载器
		 * interface:实现的接口
		 * h:InvocationHandler实例
		 */
		 Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
		double distance = m.move(50);
		System.out.println("move:  " +  distance + " meters....");
	}

}

运行结果如下:


基于cglib的动态代理

使用cglib来实现动态代理需要引入cglib的依赖包,这个网上很容易下载到。cglib的代理实现方式实际上是基于继承来实现的,它会创建一个被代理类的子类,在子类中添加日志处理等模块并调用父类的方法。这些子类由cglib帮我们管理。

仍然是代理前面的Moveable接口的实现类:

首先要实现MethodInterceptor接口:

package com.hunan.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class<?> clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		
		return enhancer.create();
	}
	
	/*
	 * 拦截所有目标类方法的调用
	 * obj:目标类的实例
	 * m: 目标方法的反射对象
	 * args:方法的参数
	 * proxy:代理类的实例
	 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
	 */
	@Override
	public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("日志开始。。。");
		
		//代理类调用父类方法
		Object distance= proxy.invokeSuper(obj, args);
		
		System.out.println("日志结束。。。");
		return distance;
	}

}

这个enhancer就是用于实现代理的子类了,不过cglib帮我们包装了一下。

接下来写一个Train类实现Moveable接口:

package com.hunan.cglibproxy;

import java.util.Random;

import com.hunan.proxy.Moveable;

public class Train implements Moveable {

	@Override
	public double move(double speed) {
		System.out.println("火车行驶中。。。");
		int moveTime =  new Random().nextInt(3000);
		try {
			Thread.sleep(moveTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return speed*moveTime;
	}

}

写一个main方法测试一下:

package com.hunan.cglibproxy;

public class Client {

	public static void main(String[] args) {
		CglibProxy proxy = new CglibProxy();
		Train t = (Train)proxy.getProxy(Train.class);
		double distance = t.move(100);
		System.out.println("火车行驶了: " + distance + "路程。。");
	}

}

打印输出如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值