代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
例如,我们要创建一个任务接口,以便后来的任务去实现
public interface Task {
public void dealTask(String taskName);
}
实现的接口,也就是委托类
public class RealTask implements Task {
@Override
public void dealTask(String taskName) {
System.out.println("开始执行.............");
try {
Thread.sleep(100);//让执行线程睡一段时间,代表正在执行任务
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们不想直接对这个类进行直接访问,或者是对这个类中的一些方法中进行一些附加的功能,如,对这个方法的计算时间做出运算,
public class ProxyTask implements Task {
private Task task;
@Override
public void dealTask(String taskName) {
long beginTime = System.currentTimeMillis();//计算当期时间,附加执行任务耗费多长时间
task.dealTask(taskName);
long endTime = System.currentTimeMillis();
System.out.println("执行时长" + (endTime - beginTime));
}
public ProxyTask(RealTask task) {
this.task = task;
}
}
由此我们可以看出,我们要去完成这个静态代理,需要我们去实现同一个接口, 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。再有代理对象的一个接口只服务于一种类型的对象,一旦要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
于是,我们再来看看动态代理。
动态代理技术:
1、JVM可以在运行期动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
2、JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理。
3、CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
Proxy的一些静态方法:
1、static InvocationHandler getInvocationHandler(Object proxy) ;获取指定代理对象所关联的调用处理器;
2、static Class getProxyClass(ClassLoader loader, Class[] interfaces);获取关联于指定类装载器和一组接口的动态代理类的类对象;
3、static boolean isProxyClass(Class cl) ;判断指定类对象是否是一个动态代理类;
4、static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);为指定类装载器、一组接口及调用处理器生成动态代理类实例
InvocationHandler:调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。其核心方法,该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个方法是调用参数。
Object invoke(Object proxy, Method method, Object[] args
实现步骤:
1、实现InvocationHandler接口;
2、给Proxy类提供类加载器和代理接口类型数组创建动态代理类;
3、以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数 ;
4、 利用动态代理类的构造函数创建动态代理类对象 。
而Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。 因此,我们只要实现InvocationHandler接口。如,
InvocationHandler handler = new RealProxy(task);//RealProxy是已实现InvocationHandler接口的类
Task proxy = (Task)Proxy.newProxyInstance(task.getClass().getClassLoader(),
task.getClass().getInterfaces(),
handler);
像这样,我们便能得到所要的代理类。因而,我们在把上面处理Task的例子用动态代理来试下。
首先是实现InvocationHandler接口,
public class RealProxy implements InvocationHandler{
Object obj;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long beginTime = System.currentTimeMillis();
method.invoke(obj,args);//代理对象、代理对象的方法,以及参数可以在其前后或者catch块中都附加系统功能代码
long endTime = System.currentTimeMillis();
System.out.println("任务执行时长"+(endTime - beginTime));
return null;
}
public RealProxy(Object obj) {
this.obj = obj;
}
}
再创建个生成代理类的功能,传入Task的类加载器、接口数组以及实现了InvocationHandler的类,
public static Task getProxy() {
Task task = new RealTask();
InvocationHandler handler = new RealProxy(task);
Task proxy = (Task)Proxy.newProxyInstance(task.getClass().getClassLoader(),
task.getClass().getInterfaces(),
handler);
return proxy;
}
}
这样,我们便能得到一个Task的代理类。即使Task接口中有不止一个方法,也不用再去每个方法都进行复写。
这里,InvocationHandler接口自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
invoke方法涉及的三个要素:
1、代理对象
2、方法;
3、参数。
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。以上仅以两个例子来表达我对动态代理的理解,该思考的还有很多,比如对于无参方法的调用,以及代理对象的接口中没有的方法是否可行,我都会一一进行试验和研究,希望能有更大的收获。