动态代理解析

//代理类实现
4public class TestProxy implements InvocationHandler{
 private Object obj;
 //绑定代理对象
2 public Object bind(Object obj){
  this.obj = obj;
  return Proxy.newProxyInstance(obj.class.getClassLoader, obj.getClass().getInterfaces(), this)
 }
 //实现代理(实现InvocationHandler中的invoke方法)
5 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
  Object result = obj;
  try {
   //前置语句
6   result = method.invoke(obj, args);
   //后置语句(代理对象方法的前后处理) 
  } catch(Exception e) {
   e.printStackTrace();
  }
  return result;
 }
}
//代理类调用
public class Test {
 public static void main(String[] args){
  TestProxy testProxy = new TestProxy();
1  实际被代理类接口 变量1 = (实际被代理类接口)testProxy.bind(实际被代理对象);
3  变量1.实际被代理对象接口方法名();
 }
}

按上面标注的顺序理解一下具体流程;

1.用代理对象绑定被代理对象

2.

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
     Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });
 

Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与Proxy.getProxyClass 相同。

参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
抛出:
IllegalArgumentException - 如果违反传递到 getProxyClass 的参数上的任何限制
NullPointerException - 如果 interfaces 数组参数或其任何元素为 null,或如果调用处理程序 hnull

3.代理实例调用方法。代理实例中并没有后面调用的方法,此时会被实现了的InvocatinHandler接口处理,该接口的作用是:对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke 方法(也就是实现了InvocationHandler接口的里面的invoke方法,该方法和具体代理实例有关,也就是后面的第四.五步)。

4.实现InvocationHandler接口

5.重写invoke方法:该方法会将第三步调用的方法名传进来作为参数method的值;如果第三步调用的方法中有参数,则将其作为args的值。

Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable
在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integerjava.lang.Boolean)的实例中。
返回:
从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException
抛出:
Throwable - 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的 throws 子句中声明的任一异常类型或未经检查的异常类型 java.lang.RuntimeExceptionjava.lang.Error。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws 子句中声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的 UndeclaredThrowableException
另请参见:
UndeclaredThrowableException

6.该invoke方法不是InvocationHandler接口的invoke方法,是Method类的invoke方法。该方法会将被代理对象当作obj的值。也就是在运行时,在代码此处动态的调用被代理对象的方法。结合第五步,被代理对象,被代理对象方法,被代理对象方法的参数都被动态的传了进来。


如此便实现了动态代理,主程序中什么地方要用,绑定被代理对象,用代理实例调用被代理对象的方法(方法有参数的话要传入)即可。




如果类或者方法要加额外功能的时候,可以采用代理;如果多个类或方法都要加同一个额外功能,这是采用代理就比较麻烦了,要写多个代理类,这时候可以采用动态代理,只需要一个代理类;如果多个类或方法要加多个额外功能又比较麻烦了,可以修改代理类或者再写一个新功能的代理类,不过这样还是比较麻烦,最好的方式是模仿aop的实现原理去处理。

例:

public interface Advice{
	public void before();
	public void after();
}

public class TimeAdvice implements Advice{
	long startTime;
	long endTime;

	public void before(){
		startTime = System.nanoTime();
	}

	public void after(){
		endTime = System.nanotime();
		System.out.println("计算程序运行时间:" + (endTime - startTime) + "ns");
	}
}



客户端调用绑定的时候把new TimeAdvice()当作参数传进去,并在后面的invoke方法中合理的加入该对象的方法即可。
这样的话,如果有另外一个功能被要求加入时,可以直接实现Advice接口,然后在主程序中绑定的时候传参数进去即可。


如果希望在同一个方法或者类上增加两个功能,只需继承前面一个实现了Advice接口的类即可,在before和after方法中调用super.before()和super.after()即可


(哪里有问题欢迎大家指正)




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值