代理这个词语应该在现实生活中路见不鲜。现在在软件设计上,也有所谓的代理机制,比如要为多个具有相同接口的目标类的各个方法增加一些系统功能eg:异常处理,日志,计算方法的运行时间,事物的管理等等。那么在编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用的时候加上系统功能代码。
这个图是用来反映代理的原理,代理类和目标类实现了共同的接口,Client本来是要调用目标类来实现功能的,现在通过调用代理类就可以实现目标类的功能,并且其中还加入了一些系统的功能。
代理是AOP编程的核心和关键技术。对于AOP编程,是aspect oriented program简写,它的目标是使交叉业务模块化,交叉业务就是可以看做对于不同的方法或者不同的模块都要有某些代码实现的功能是相同的,这样就感觉在纵向看这些模块的时候,那些模块都要实现的功能把他们纵向给截断了,而这就叫交叉业务。
动态代理:为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理确实太累了。JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类,只能用作具有相同接口的目标类的代理。
代理类的各个方法中通常除了要调用目标类的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
①在调用目标方法之前
②在调用目标方法之后
③在调用目标方法前后
④在处理目标方法异常的catch块中
动态生成代理代码:
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //生成一个实现了Collection接口的字节码;
Constructor cons = clazzProxy.getConstructor(InvocationHandler.class); //获得构造方法
class MyIvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
return null;}}
Collection c = (Collection)cons.newInstance(new MyIvocationHandler()); //实例化
System.out.println(c.toString());
c.clear();
在上面jvm创建动态类以及实例对象的时候分开来进行的,而需要给它提供:
① 这个代理类要实现的接口
② 产生的字节码必须有一个与之关联的类加载器
③ 生成实例对象的时候还要传一个实现了InvocationHandler接口的对象
现在能否用一步来实现这个两步才能实现的呢,在Proxy中有一个这样的方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h)throwsI llegalArgumentException
该方法可以直接一步来生成代理类的实例对象;
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 startTime = System.currentTimeMillis();
Object revalue = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "runing " + (endTime - startTime));
return revalue;
}
});
proxy3.add("1");
proxy3.add("2");
proxy3.add("3");
System.out.println(proxy3.size());
这个代码中要传一个实现了InvocationHandler的的对象,这个对象中只有一个方法,
方法:Object invoke(Object proxy,--------------------------代理类对象
Method method,---------------------被调用的目标类的方法
Object[] args)---------------------该方法的参数数组
throws Throwable
代理类实例调用add方法的时候,是这样的一个过程,把代理实例对象,要执行的方法和参数传给InvocationHandler的实现类的invoke方法,在该方法中进行目标类方法的执行,并附加一些系统要实现的功能。其实代理类内部调用的时候应该是这样的:
boolean add(String){
Return this.handle.invoke(this,this.getClass().getMethod(“add”),”1”);}
就是在调用的时候会自动的去调用这个类的invoke方法的。这个返回的值是返回给了代理。这里有个小问题,就是在该代理类调用getClass的方法的时候,返回的应该是那个类呢?通过以上的理解就是,这个getClass应该去找那个目标然后就应该返回那个目标类的类名应该,然而返回却是这个代理的类名这是为什么呢,因为在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。
动态代理的原理:
Client客户端调用代理,代理的构造方法接受一个Handler,然后client调用方法的时候就会通过这个接受的handler的invoke方法来返回到目标的相应方法。在图中画圈的地方老师讲,这个部分的应该不是固定的,就是说我可以随便加功能的,然后我只需要把这个功能通过对象的方法传进来就可以,这样就比较高效了如果直接把功能嵌入进去的话,那么我们这个代理类做的代理就没有什么实际意义通用性不强。所以要把这些想要加入的功能封装为类,然后把对象传入进来,在通过对象的方法就可以得到我们想要的功能了。由此这个部分就有一个名字,叫契约。这个契约是以接口的形式来定义的,必须有一些限定的方法。
首先把获得代理的代码写入到一个方法中:
private static Object getProxy(final Object target,final Advice advice) {
//传入目标类和要加入功能的契约类,然后返回一个可以在目标类执行的时候添加契约类功能的代理类
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(), //传入目标的加载器
target.getClass().getInterfaces(), //传入目标的接口
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);//执行契约类的的before方法
Object revalue = method.invoke(target, args);
advice.afterMethod(method);//执行契约类的after方法
return revalue;//返回给代理
}
});
return proxy3;//返回一个代理
}
然后在定义个契约类的接口:
import java.lang.reflect.Method;
public interface Advice {
public void beforeMethod(Method method);
//在目标类执行方法之前执行的方法
public void afterMethod(Method method);
//在目标类执行方法之后执行的方法
}
实现该接口的一个实现类:
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long startTime;
@Override
public void afterMethod(Method method) {
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "runing " + (endTime - startTime));
System.out.println("结束了。。。。。。。。");
}
@Override
public void beforeMethod(Method method) {
System.out.println("开始了。。。。。。。。");
startTime = System.currentTimeMillis();
}
}
然后客户端只要给我一个目标和一个契约,那么可以得到一个代理类:
final ArrayList target = new ArrayList();
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
总的来说这部分我觉得还是比较不好理解,要通过图来好好分析下这个理论呀。还要继续努力。