程序中的代理
产生原因:为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如,异常处理、日志,计算方法和运行时间、事务管理等。
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
AOP:
系统中存在交叉业务(安全、日志、事物等功能要贯穿好多个模块中,所以,它们就是交叉业务。),一个交叉业务就是要切入到系统中的一个方面,如几乎所有业务模块都有安全、事务、日志问题。
用具体的程序代码来描述交叉业务:
method1 method2 method3
{ { {
———————————————————-切面(如事物开始)
— —- —- — —-
———————————————————切面(如事物结束)
} } }
交叉业务的编程问题即为面向方面的编程,AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
——————————————————————–切面
func1 func2 func3
{ { {
} } }
——————————————————————–切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!
JVM可以在运行期间动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态代理类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
- 在调用目标方法之前
- 在调用目标方法之后
- 在调用目标方法前后
- 在处理目标方法异常的catch块中
创建动态类及查看其方法列表信息:
要求:创建实现了Collection接口的动态类和查看其名称,分析
public static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces)throws IllegalArgumentException方法。
范例:列出动态类中所有构造方法,普通方法和参数签名
package com.zmcheng.Dy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//System.out.println(cls.getName());//com.sun.proxy.$Proxy0
//System.out.println(cls.getSimpleName());//$Proxy0
Constructor<?>[] cons = cls.getConstructors();
for(int i = 0;i<cons.length;i++){
System.out.println(cons[i]);
/*输出:
* public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
只有这一个有参构造方法
*/
}
Method[] methods =cls.getMethods();
for(int i=0;i<methods.length;i++){
System.out.println(methods[i]);
}
/*动态类包括继承Object和实现Collection接口的所有方法
* public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
public final boolean com.sun.proxy.$Proxy0.add(java.lang.Object)
public final boolean com.sun.proxy.$Proxy0.remove(java.lang.Object)
.....................
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
*/
}
}
要求:创建动态类的实例化对象及调用其方法:
java.lang.reflect.InvocationHandler接口
范例:调用构造方法创建实例化对象
package com.zmcheng.Dy;
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
Constructor< ?> con= cls.getConstructor(InvocationHandler.class);
class MyInvocationHandler1 implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
Collection Proxy1 = (Collection)con.newInstance(new MyInvocationHandler1());
System.out.println(Proxy1);//null
Proxy1.clear();
Collection Proxy2 = (Collection)con.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
System.out.println(Proxy2);//null
}
}
更简单的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException
不仅得到了Proxy的字节码,并且获得Proxy的实例化对象。
Java文档中是这么说的:
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
创建某一接口 Foo 的代理:
InvocationHandler handler = new MyInvocationHandler(…);
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
或使用以下更简单的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
Collection Proxy3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ArrayList target = new ArrayList();
long begintime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endtime = System.currentTimeMillis();
System.out.println(method.getName()+" suning time of"+(endtime-begintime));
return retVal;
}
});
Proxy3.add("zmc");
Proxy3.add("lalal");
System.out.println(Proxy3.size());
/*
* add suning time of0
add suning time of0
size suning time of0
0
*/
猜想分析动态生成的类的内部代码:
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法继承Object的方法和一个接收InvocationHandler参数的构造方法。
class $Proxy0
{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler){
this.handler = handler;
}
//实现了Collection接口的动态类的各个方法的代码又是什么样的?
//生成的Collection接口中的方法的运行原理
//调用InvocationHandler的invoke方法
/*
* Client程序调用objProxy.add("zmc")方法时,涉及三要素:objProxy对象,add方法,"zmc"参数
* public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
* method.invoke(proxy, args);//产生了死循环,正好说明了原理
* }
*/
int size() {
return handler.invoke(this, this.getClass().getMethod("size"), null);
}
void clear(){
return handler.invoke(this, this.getClass().getMethod("clear"),null);
}
.........
}
在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。
范例:动态生成代理类模板
Java
package com.zmcheng.Demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ServiceProxy implements InvocationHandler {
private Object target=null;
public Object getProxy(Object obj){
this.target = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
public void prepare(){
System.out.println("********操作前的准备:打开数据库连接*******");
}
public void end() throws Exception{
System.out.println("********操作后的收尾:关闭数据库连接*******");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object retValue = method.invoke(this.target, args);
return retValue;
}
}