代理

一.      代理的概念

        代理就是有多个类,这些类拥有同一个接口,为这些类中的方法增加一些系统功能,例如异常处理,计算方法的运行时间等等,这样我们在客户端使用该类时,配合工厂模式和修改配置文件的方式可以选择使用代理类,还是目标类,我们代理的结构图如下:

        类似于这种使用代理的编程思想叫做面向方面的编程,简称AOPAspect Oriented Program,专业说法是交叉业务的模块化,就是为不同对象加载相同功能。

在代理类中,我们要添加的功能性代码一般放在以下位置:

        ①   调用目标方法之前

        ②   调用目标方法之后

        ③   调用目标方法前后

        ④   在处理目标方法异常catch块中

二.      动态代理

        要为系统中的各个接口下的类增加代理功能,需要许多的代理类,但是在反射包中有这么一个类,Proxy他可以动态的生产类的字节码文件,也就是说我们可以在运行时期动态的获得一个类,这个类往往被用作代理类,这就是动态代理。

        虚拟机生成的动态类必须实现一个或者多个接口,所以虚拟机生成的动态类只能用作具有相同接口的目标类的代理,如果某个类没有实现接口,我们可以使用CGLIB库,他可以动态的生成一个类的子类,这个动态生成的子类可以用作该类的代理。

三.      动态代理的使用

1.Proxy介绍

        Proxy类是动态代理的基石,此类可以动态生成类的字节码文件,下面来看一下他最为常用的方法。

        (1)Proxy.getProxyClass(ClassLoader loader,Class<?>… interfaces)

        此方法接收一个类加载器和一个或者多个接口的字节码,返回一个该接口下的动态字节码文件,该字节码的名称为:$Proxy+编号,这个字节码只有一个有参构造方法,参数为InvocationHanderler handler

        (2)Proxy.newProxyInstace(ClassLoader loader ,Class<?>[] interfaces, InvocationHandler handler)

        此方法用于直接创建某个接口下的实例对象

      这两种方法对代理的实现方法都是通过InvocationHandler对象来实现的,InvocationHandler是一个接口,当我们拿到一个字节码之后,通过创建一个InvocationHandler对象把目标类方法和我们需要添加的内容进行整合。在此接口中只有一个方法:invoke,他的参数列表为:Object proxy(代表哪个对象)Method method(哪个方法)Object[] args(方法接收的参数)

      代理的实现流程为:代理—>InvocationHandler>目标,而目标把返回结果再逆序返回给代理。对于接口或者代理类中从Object继承的方法除去hashCode(),equals(),toString()之外都不派发给handler完成。

/*
 * 代理类
 * 通过动态创建一个Collection接口下的动态类是演示代理类的使用
 * ① 首先通过反射包下的一个类Proxy来动态生成Collection类字节码
 * 	通过他的方法Proxy.getProxyClass(loader, interfaces)生成
 * 
 * ②拿到Collection接口的字节码后可以做的事情就多了,比如用它来创建一个实例对象
 * 
 * ③我们还可以利用Proxy的静态方法直接new一个接口下的实例对象
 * 
 * ④无论哪一种方法都要返回InvocationHandler对象,一般采用匿名内部类的形式,在handler对象中
 * 的invoke方法是代理要调用的,而我们对原方法进行功能的加强就是写在invoke方法中
 * */
class ProxyTest {

	public static void main(String[] args) throws Exception{
		//①动态获取从Collection接口下生成的代理类字节码
		Class proxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		System.out.println(proxy.getName());
		//②创建Collection接口下的实例对象,他没有空参的构造方法,所以只能通过构造器去创建
		Constructor constructor = proxy.getConstructor(InvocationHandler.class);
		
		//通过匿名内部类的方式来创建该实例对象
		Collection coll   = (Collection)constructor.newInstance(new InvocationHandler() {
			ArrayList al = new ArrayList();
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
					long start = System.currentTimeMillis();
					Object value = method.invoke(al, args);
					Thread.sleep(300);
					long end = System.currentTimeMillis();
					System.out.println(method.getName()+"方法运行时间为"+(end - start));
					
				return value;
			}
		});
		coll.add("adsf");
		coll.size();
		
		//③利用Proxy的静态方法newProxyInstance直接new对象出来
		Collection coll2 = (Collection)Proxy.newProxyInstance(
						//Collection的类加载器
						Collection.class.getClassLoader(),
						new Class[]{Collection.class},
						//在InvocationHandler的匿名内部类中完成代理功能,此处用来计算方法所耗费的时间
						new InvocationHandler() {
						//在此处定义目标,无论coll2运行什么方法他的目标都是al
						ArrayList al = new ArrayList();							
							@Override
							public Object invoke(Object proxy, Method method, Object[] args)
									throws Throwable {
								long startTime = System.currentTimeMillis();
								Object value = method.invoke(al, args);;
								long endTime = System.currentTimeMillis();
								System.out.println(method.getName()+"方法的运行时间为:"+(endTime - startTime));
								return value;
							}
						});
	}
}

2.动态加载系统功能

      InvocationHandlerinvoke方法是代理功能的实现地,我们采用动态加载代理的功能,也就是不再invoke方法中把功能写死,而是通过接收参数的形式来接收用户具体指定的功能,实际应用中,目标一般也是由用户指定。

      代理的功能,也就是方法,在此我们的做法是传入一个对象,在JavaScript中可以传入字符串,把字符串解析为方法命令,java暂时无此功能,因此采用传入对象的方式,把代理功能代码封装成一个对象,一般我们都是以接口的形式来获取该接口下对应的对象,所以首先定义一个接口,在这个接口中定义系统功能的方法声明。

//为动态代理服务的类
public interface Advice {
	
	void startMethod(Method method);
	void endMethod(Method method);
}

自定义一个类实现该接口,并且编写真正的代理功能代码。

/*
 * 自定义类实现接口以便传入dialing方法中
 * */
public class MyAdvice implements Advice {

	//因为要使用到同一资源,所以定义成成员变量
	long startTime = 0;
	
	//复写前端方法
	@Override
	public void startMethod(Method method) {
		startTime = System.currentTimeMillis();
	}

	@Override
	public void endMethod(Method method) {
		long endTime = System.currentTimeMillis();
		System.out.println(method.getName()+"方法的运行时间为:"+(endTime - startTime));

	}
}


 

在真正使用时,构建方法,将自定义类的对象、目标类都作为参数传递给该方法,这两个参数的可变性造就代理功能的可变性。

public class ProxyTest {

	public static void main(String[] args) throws Exception{
		//①动态获取从Collection接口下生成的代理类字节码
		Class proxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		System.out.println(proxy.getName());
/*对代理中附加的功能进行动态化编程,就是不写死,通过加载参数的形式来让用户指定代理的功能
		 * 具体做法:将代理和目标转为参数传递给proxy
		 * ①把生成代理的方法整合成一个方法getProxy
		 * ②编写一个接口Adice,这个接口中有前端方法和后端方法,要传入代理方法中对象都是该接口下的对象
		 * ③编写一个类MyAdvice实现我们自定义的接口,并真正的定义前端方法和后端方法
		 * ④将自定义类的对象传入方法中
		 */
		
		//定义目标和代理功能
		final ArrayList al = new ArrayList();
		final MyAdvice md = new MyAdvice();
		
		//这就是整合后的方法
		Collection coll3 = (Collection)getProxy(al,md);
		
		coll3.add("小绿");
		System.out.println(coll3.size());
		coll3.remove(2);
	}
	
	//这个方法就可以接收Advice接口下的方法,而且可以动态的接收,用户可以通过修改MyAdcice来实现不同的代理功能
	private static Object getProxy(final Object target,final Advice a) {
		Object clazz = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						a.startMethod(method);
						Object value = method.invoke(target, args);;
						a.endMethod(method);
						return value;
					}
				});
		return clazz;
	}
}




 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值