代理模式在设计模式中是一种比较重要的设计模式,关于它的变种很多,比如远程代理,虚拟代理,动态代理,但是其本质都是一致的,即为另一个对象提供一个替身或占位符以控制这个对象的访问,现在就代理模式进行一个总结
下图是代理模式的一般类图
RealSubject通常是真正做事的对象,proxy会控制对RealSubject的访问
Proxy持有Subject的引用,所以必要时它可以将请求转发给Subject,并且负责创建RealSubject对象
Proxy和RealSubject都实现了Subject接口,这允许任何客户都可以像处理RealSubject对象一样地处理proxy对象
1. 远程代理:
即是对远程对象的代理,客户方法调用本地代理对象上的方法,再由代理处理所有网络通信的低层细节,实现对远程对象的方法调用,java的RMI已经实现了远程调用的功能
如何使用java RMI
(1) 远程接口
import java.rmi.*;
public interface MyRemote extends Remote {
public StringsayHello() throws RemoteExceptoin;
}
远程方法的变量和返回值,必须属于原语类型或者实现了可序列化
(2)远程实现
public class MyRemoteImpl extends UnicastRemoteimplementsMyRemote {
public String sayHello() {
Return “server say hello”;
}
public MyRemoteImpl() throws RemoteException{ }
public static void main(String[] args) {
try {
MyRemote service =new MyRemoteImpl();
Naming.rebind(“RemoteHello”, service); //注册这个实现对象时,RMI注册的是stub
}catch (Exception ex) {
ex.printStackTrace();
}
执行rmic,为服务类产生stub和skeleton
rmic MyRemoteImpl 产生两个新类:MyRemoteImpl_Stub.class和MyRemoteImpl_Skel.class
执行rmiregistry
rmiregistry
java MyRemoteImpl 实例化服务对象,然后到RMIregistry中注册
客户端代码
import java.rmi.*;
public class MyRemoteClient {
public staticvoid main(String[] args) {
try {
MyRemote service =(MyService)Naming.lookup(rmi://127.0.0.1/RemoteHello); //客户需要到RMI registry中去寻找,RMI返回stub给客户端
System.out.println(service.sayHello());//客户通过stub来调用远程方法
} catch (Exception ex) {
ex.printStackTrace();
}
}
远程代理类如何套用RMI框架
服务端
public interface ServiceRemote extendsRemote {
public String hello()throws RemoteException;
}
要代理的类实现这个远程接口
pulic class Service extendsUnicastRemoteObject implements ServiceRemote {
public Service()throws RemoteException {}
public Stringhello() { return “Hello”;}
}
客户端
代理类实现
Import java.rmi.*;
Public class ProxyService {
ServiceRemoteser;
PublicProxyService(ServiceRemote ser) {
this.ser = ser;
}
public String SayHello(){
try {
ser.hello();
} catch (RemoteExceptoin e) {
e.printStacekTrace();
}
}
2.什么是虚拟代理
当需要创建一个开销比较大的对象的时候,同时需要给客户返回一个请等待的信息,这时可用到虚拟代理,当对象在创建前和创建中时,由虚拟代理来扮演对象的替身,对象创建后代理就会将请求直接委托给对象
当创建开销大的对象时,一般是新开一个线程来创建对象,当对象创建完毕时再通过事件来通知代理,对象创建成功,这时代理可再次调用对象的方法
3.什么是动态代理
利用java的反射机制,我们可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到所指定的类,实际的代理类是在运行时创建的,所以叫做动态代理
Proxy是由java产生的,而且实现了完整的Subject接口,我们可以提供一个InvocationHandlerImpl的实现类,Proxy上的任何方法调用都会被传入此类,InvocationHandlerImpl控制对RealSubject方法的访问
代码实现举例
public interface Hello {
void sayHello(Stringstr);
}
public class HelloImpl implements Hello {
public void sayHello(Stringstr) {
System.out.println(str);
}
importjava.lang.reflect.*;
public classHelloInvocationHandler implements InvocationHandler {
Object delegate;
public OwnerInvocationHandler ( Object hello){
this.delegate = hello;
}
//通过代理调用的方法最终都会调用invoke
public Object invoke ( Object proxy, Methodmethod, Object[] args) throws IllegalAccessException {
try{
System.out.println(“before invoke”);
method.invoke(delegate, args);
System.out.println(“before invoke”);
}catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//动态生成代理类
public Hello getHelloProxy(Hello hello) {
Return (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
this);
}
Public class ProxyTest {
public static void main(String[] args) {
Hello impl = new HelloImpl();
HelloInvocationHandler handler = new HelloInvocationHandler (impl);
//这里把handler与impl新生成的代理类相关联
Hello hello = handler. getHelloProxy(impl);
//这里无论访问哪个方法,都是会把请求转发到handler.invoke
hello.print("All the test");
hello.sayHello("Denny");
}
Java动态代理实现机制分析:
通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
对照代码是:
/ InvocationHandlerImpl 实现了InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = newInvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader,new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor =clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy =(Interface)constructor.newInstance(new Object[] { handler });