代理模式的一个使用是RMI(Remote Method Invocation)。
当远程调用一个对象的方法时,我们需要编写一些处理networking,I/O之类的代码。这个时候我们只需要用一个代理来专门处理这些非业务相关的逻辑就好了。而RMI就帮我们做了这些事情,不需要我们自己再创建代理。下面是例子:
1. 下面是一个接口和它的实现
public interface MyRemote extends Remote {
String doSomething() throws RemoteException;
}
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
public MyRemoteImpl() throws RemoteException {}
public String doSomething() throws RemoteException {
System.out.println("Hello World");
return "Hello World";
}
}
2. Server端代码,创建并注册对象
public class RmiService {
public static void main(String ...args) {
try {
MyRemote service = new MyRemoteImpl();
Naming.rebind("RemoteHello", service);
LocateRegistry.createRegistry(1099);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
3. Client端代码
public class MyRemoteClient {
public static void main(String ...args) {
new MyRemoteClient().go();
}
public void go() {
try {
// 通过Naming.lookup获取Server端对象的代理
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/RemoteHello");
String s = service.doSomething(); // 就想在访问本地对象的方法一样
System.out.println(s);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
在Server端创建对象之前需要使用工具rmic来产生代理类。通过调用rmic MyRemoteImpl会产生一个名为MyRemoteImpl_Stub的代理类,而实际上Client端访问的也是这个代理。
代理模式的另外一个应用是动态代理。通过使用动态代理,你可以在运行时产生代理类,而不需要自己编写代理。所以动态代理是十分灵活,有弹性的,并且被大量地运用在一些框架中。比如说AspectJ,Spring AOP...下面介绍动态代理的使用:
public interface SomeBean {
void doSomething(String msg);
}
public class SomeBeanImpl implements SomeBean {
public void doSomething(String msg) {
System.out.println("SomeBeanImpl: " + msg);
}
}
public class TestDynamicProxy {
public static void main(String ...args) {
final SomeBeanImpl someBeanImpl = new SomeBeanImpl();
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
proxy = someBeanImpl;
// Adding some codes here to perform a pre-excution behavior
Object ret = method.invoke(proxy, args);
// Adding some codes here to perform a post-excution behavior
return ret;
}
};
SomeBean someBean = (SomeBean) Proxy.newProxyInstance(SomeBean.class.getClassLoader(),
new Class[]{ SomeBean.class },
invocationHandler);
someBean.doSomething("Testing");
createProxyClassFile();
}
private static void createProxyClassFile() {
String name = "SomeBeanProxy";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{SomeBean.class});
try {
FileOutputStream out = new FileOutputStream( name + ".class" );
out.write( data );
out.close();
}
catch( Exception e ) {
e.printStackTrace();
}
}
}
通过Proxy.newProxyInstance,我们就可以获取一个代理类的实例。我们可以在InvocationHandler::invoke方法中获取到Method对象,这样就可以在方法执行前后添加额外的行为,比如说日志,授权...。 通过ProxyGenerator我们可以把运行时产生的对象存成class文件,下面是反编译之后的代码片段:
public final class SomeBeanProxy extends Proxy implements SomeBean {
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public SomeBeanProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void doSomething(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("com.allen.demo.test.dynamicProxy.SomeBean").getMethod("doSomething", new Class[]{Class.forName("java.lang.String")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}