introduction:
代理模式:
为另一个对象提供一个替身或占位符以控制对象的访问。
使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。
在真实的世界中,代理模式有许多变体,这些变体都有共通点:都会将客户对主题(subject)施加的方法调用拦截下来。
代理模式的结构类似装饰者,但目的是不同的。
代理模式与适配器模式最大的不同点是,代理模式实现相同的接口,适配器模式实现不同的接口。
装饰者模式为对象加上行为,而代理则是控制访问。
java内置代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器。
代理模式会造成设计中类的数量的增加。
远程代理:
远程代理可以作为另一个jvm上对象的本地代表。
所谓代理,就是代表某个真实的对象。在这个案例中,代理就像是某个对象一样,但其实幕后是它利用网络和一个远程的真正对象沟通。
所谓远程对象,就是一个对象活在不同的java虚拟机(jvm)堆中(更一般的说法为,在不同的地址空间运行的远程对象)。
所谓本地代表,就是由本地方法调用的对象,其行为会转发到远程对象中。
其实就是调用本地堆中的"代理"对象上的方法,再由代理处理所有网络通信的低层细节。
java rmi(remote method invovation):
rmi中存在4个对象,客户对象,客户辅助对象(stub),服务辅助对象(skeleton),服务对象
rmi提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法。
rmi的好处在于不必亲自写任何网络或I/O代码。
rmi提供了所有运行时的基础设施,让这一切正常工作。包括查找服务(lookup service),这个服务用语寻找和访问远程对象。
远程调用存在风险。
在java1.2以后就拜托skeleton了。
java5的rmi和动态代理结合使用,动态代理动态产生stub。
制作远程服务对象的步骤:
1.制作远程接口
2.制作远程的实现
3.利用rmic产生stub和skeleton
4.启动rmi registry
5.开始远程服务
客户在做lookup时,必须有stub类,否则一切都告吹。
对于rmi程序员常犯的三个错误:
1.忘记在启动服务之前,先启动rmiregistry
2.忘了让变量或返回值序列化
3.没有提供stub类
虚拟代理:
虚拟代理作为创建开销大的对象的代表。
虚拟代理经常直到我们真正需要一个对象的时候才创建它。
当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。
对象创建后,代理就会将请求直接委托给对象。
动态代理:
在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时创建的。
动态代理之所以动态,是因为运行时才创建出来。代码开始执行时还没有proxy类,它是根据传入的结果集创建的。
InvoationHandler是Proxy的一个帮助类,Proxy会把调用转发给它处理。Proxy本身是利用Proxy.newProxyInstance()方法在运行时创建的。
通过isProxyClass()可以判定一个类是否是代理类。
对于newProxyInstance()中的接口参数必须是public类型的,如果不是,这些接口必须和调用类必须在一个包中。并且这些接口中不允许有方法名和参数完全一样的方法。
保护代理:
根据访问权限决定客户是否可以访问对象的代理。
代理模式其他运用:
防火墙代理
智能引用代理
缓存代理
同步代理
复杂隐藏代理
写入时复制代理
demo:
1.首先演示最原始的代理模式(关系如下图所示):
Subject.java
public interface Subject {
void request();
}
RealSubject.java
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("调用真实的主题对象");
}
}
Proxy.java
public class Proxy implements Subject{
private Subject subject;
public Proxy(){
}
public Proxy(Subject subject){
this.subject = subject;
}
@Override
public void request() {
System.out.println("调用代理");
subject.request();
}
}
Test.java
public class Test {
public static void main(String[] args) {
Subject real = new RealSubject();
Subject proxy = new Proxy(real);
proxy.request();
}
}
输出结果:
调用代理
调用真实的主题对象
2.演示动态代理:
动态代理与原始的代理模式有一些不同,这里需要使用java api中的方法(类图如下):
java api中代理的方法交给InvocationHandler的invoke方法处理
Subject.java
public interface Subject {
void request();
}
RealSubject.java
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("调用真实的主题对象");
}
}
InvocatHandler.java
public class InvocatHandler implements InvocationHandler{
private Subject subject;
public InvocatHandler(){
}
public InvocatHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("通过动态代理调用");
return method.invoke(subject, args);
}
}
Test.java
public class Test {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
realSubject.request();
System.out.println("**********************");
InvocationHandler ih = new InvocatHandler(realSubject);
Subject subject = (Subject)Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
ih);
subject.request();
}
}
输出结果:
调用真实的主题对象
**********************
通过动态代理调用
调用真实的主题对象
3.远程代理(基于java api)
MyRemote.java
import java.rmi.Remote;
import java.rmi.RemoteException;
/*
* 对于远程调用来说,运用都是会存在风险的,因为低层调用的是I/O,这是不安全的。
*/
public interface MyRemote extends Remote{
/*
* 远程方法的变量和返回值,必须属于原语类型(primitive)或可序列化(Serializable)类型
* 使用java API中的内定类型不会有任何问题的,如果是自定义类,必须实现Serializable接口
*/
String sayHello() throws RemoteException;
}
MyRemoteImpl.java
/*
* 为了要成为远程服务对象,你的对象需要某些"远程的"功能。最简单的方式就是
* 扩展java.rmi.server.UnicastRemoteObject
*/
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
/**
*/
private static final long serialVersionUID = 1L;
protected MyRemoteImpl() throws RemoteException {
}
@Override
public String sayHello() throws RemoteException {
return "Server says, 'hey'";
}
public static void main(String[] args) {
try {
MyRemote service = new MyRemoteImpl();
Naming.rebind("rmi://127.0.0.1:1099/RemoteHello", service);
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyRemoteClient.java
import java.rmi.Naming;
/**
* Naming 提供在对象注册中存储和获取对远程对象引用的方法
*/
public class MyRemoteClient {
public void go(){
try {
MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello");
String s = service.sayHello();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
Server says, 'hey'