//代理类实现
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
,或如果调用处理程序h
为null
-
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.Integer
或java.lang.Boolean
)的实例中。
返回:
-
从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为
null
并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出NullPointerException
。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出ClassCastException
。
抛出:
-
Throwable
- 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的throws
子句中声明的任一异常类型或未经检查的异常类型java.lang.RuntimeException
或java.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()即可
(哪里有问题欢迎大家指正)