<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; RMI</title>
	<atom:link href="https://blog.gmem.cc/tag/rmi/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Tue, 21 Apr 2026 10:40:56 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>代理模式</title>
		<link>https://blog.gmem.cc/proxy-pattern</link>
		<comments>https://blog.gmem.cc/proxy-pattern#comments</comments>
		<pubDate>Thu, 07 Dec 2006 07:40:54 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[RMI]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7689</guid>
		<description><![CDATA[<p>模式定义 代理模式为对象提供一个替身或者占位符，以控制对该对象的访问。 代理模式在GOF95中分类为结构型模式。 模式结构与说明 Proxy和RealSubject具有共同的接口Subject，以满足里氏替换原则 Proxy一般需要持有RealSubject的引用，以便在必要是将请求转发给真实对象 RealSubject通常是真正做事的对象，Proxy会访问Client对RealSubject的访问 所谓“替身/占位符”，就是它代表某个真实的对象，一个常见的例子的是远程代理，即远程对象的本地代表。所谓远程对象是指不是运行在同一个地址空间中的对象，不能通过直接的函数调用来访问。为了向Client屏蔽远程对象，在客户端所在地址空间，创建一个Proxy，提供与真实对象一致的接口即可，具体的进程间或者网络通信由代理对象负责。 代理可以提供多种控制访问功能，从而形成不同类型的代理： 远程代理控制访问远程对象 虚拟代理控制访问创建开销大的资源，并将RealSubject的创建尽量的推迟。虚拟代理一个变体是写入时复制（Copy-On-Write）代理，避免不必要的数据复制，Linux内核对也使用了写入时复制这一思想来增强fork()的性能 保护代理基于权限控制对资源的访问 缓存代理通过缓存来提升性能。在Web服务器中使用的很多 智能引用代理：在访问一个对象的时候可以执行一些Housekeeping操作，例如引用计数 …… 经典应用 Java的RMI RMI提供了客户辅助对象——存根（Stub），以及服务辅助对象——骨架（Steleton），用来为Client创建与服务对象相同的方法。新版的Java已经不需要在服务器端显示提供Steleton类。使用RMI时不需要编写任何网络和I/O代码，对于Client，就好像在调用本地JVM上的方法一样。下面是一个简单的例子： [crayon-69ea33f79282d735490532/] JDK动态代理机制 java.lang.reflect包中内置了代理的支持，利用该包可以在运行时动态创建一个代理类，实现一个或者多个接口。在此包中，Proxy角色已经被创建好了，我们需要提供InvocationHandler的实现来控制对真实对象的访问，Proxy上的任何方法调用都会转发到InvocationHandler。下面是一个基于JDK动态代理机制的保护代理的例子： [crayon-69ea33f792834639454357/] <a class="read-more" href="https://blog.gmem.cc/proxy-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/proxy-pattern">代理模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>代理模式为对象提供一个<span style="background-color: #c0c0c0;">替身</span>或者占位符，以<span style="background-color: #c0c0c0;">控制对该对象的访问</span>。 代理模式在GOF95中分类为结构型模式。</p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7694" src="https://blog.gmem.cc/wp-content/uploads/2006/12/patterns_ProxyPattern_1.png" alt="patterns_ProxyPattern_1" width="362" height="284" /></p>
<ol>
<li>Proxy和RealSubject具有共同的接口Subject，以满足里氏替换原则</li>
<li>Proxy一般需要持有RealSubject的引用，以便在必要是将请求转发给真实对象</li>
<li>RealSubject通常是真正做事的对象，Proxy会访问Client对RealSubject的访问</li>
</ol>
<p>所谓“替身/占位符”，就是它代表某个真实的对象，一个常见的例子的是远程代理，即远程对象的本地代表。所谓远程对象是指不是运行在同一个地址空间中的对象，不能通过直接的函数调用来访问。为了向Client屏蔽远程对象，在客户端所在地址空间，创建一个Proxy，提供与真实对象一致的接口即可，具体的进程间或者网络通信由代理对象负责。</p>
<p>代理可以提供多种<span style="background-color: #c0c0c0;">控制访问</span>功能，从而形成不同类型的代理：</p>
<ol>
<li><span style="background-color: #c0c0c0;">远程代理</span>控制访问远程对象</li>
<li><span style="background-color: #c0c0c0;">虚拟代理</span>控制访问创建开销大的资源，并将RealSubject的创建尽量的推迟。虚拟代理一个变体是写入时复制（Copy-On-Write）代理，避免不必要的数据复制，Linux内核对也使用了写入时复制这一思想来增强fork()的性能</li>
<li><span style="background-color: #c0c0c0;">保护代理</span>基于权限控制对资源的访问</li>
<li><span style="background-color: #c0c0c0;">缓存代理</span>通过缓存来提升性能。在Web服务器中使用的很多</li>
<li>智能引用代理：在访问一个对象的时候可以执行一些Housekeeping操作，例如引用计数</li>
<li>……</li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java的RMI</span></div>
<p>RMI提供了客户辅助对象——存根（Stub），以及服务辅助对象——骨架（Steleton），用来为Client创建与服务对象相同的方法。新版的Java已经不需要在服务器端显示提供Steleton类。使用RMI时不需要编写任何网络和I/O代码，对于Client，就好像在调用本地JVM上的方法一样。下面是一个简单的例子：</p>
<pre class="crayon-plain-tag">//JDK1.2以后不需要产生skeleton，RMI直接使用反射把客户端调用分发给远程服务
//JDK1.5以后，连stub都不需要了，结合RMI和动态代理机制，远程对象的Stub成为java.lang.reflect.Proxy的实例。rmic命令已经退出历史舞台

//客户端和服务器公共代码
import java.rmi.Remote;
import java.rmi.RemoteException;

//支持远程方法调用的服务端必须继承RMI提供的接口
public interface RemoteEchoService extends Remote
{
    String echo( String request ) throws RemoteException;//接口方法必须声明跑出RMI规定的异常
}

//服务器代码
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

//远程服务类至少实现一个Remote接口，并且为远程对象定义一个构造器
//继承UnicastRemoteObject类，自动导出为远程对象
//只有导出的对象才能接受RMI调用
public class RemoteEchoServiceImpl extends UnicastRemoteObject implements RemoteEchoService
{
    private static final long serialVersionUID = 1L;
    //由于对象导出本身就会跑出RemoteException，因此构造器需要声明抛出
    protected RemoteEchoServiceImpl() throws RemoteException
    {
        super();
    }

    public String echo( String request ) throws RemoteException
    {
        return request;
    }

}
public class ServerMain
{
    public static void main( String[] args )
    {
        try
        {
            //创建RMI注册表，监听默认的1099端口
            LocateRegistry.createRegistry(1099);  
            //初始化服务类
            RemoteEchoService service = new RemoteEchoServiceImpl();
            //注册到RMI注册表（rmiregistry），客户端可以通过//host/objectname形式查找到此远程对象
            Naming.rebind( "remoteEchoService", service );
            //服务器端自动以守护模式运行，等待远程调用请求到达
        }
        catch ( Exception e )
        {
            e.printStackTrace();
        }
    }
}

//客户端代码
public class ClientMain
{
    public static void main( String[] args )
    {
        try
        {
            String url = "//localhost/remoteEchoService";
            RemoteEchoService service = (RemoteEchoService) Naming.lookup( url );
            service.echo( "Hello" );  //service是动态代理对象
        }
        catch ( Exception e )
        {
            e.printStackTrace();
        }
    }
}</pre>
<div class="blog_h3"><span class="graybg">JDK动态代理机制</span></div>
<p>java.lang.reflect包中内置了代理的支持，利用该包可以在运行时动态创建一个代理类，实现一个或者多个接口。在此包中，Proxy角色已经被创建好了，我们需要提供InvocationHandler的实现来控制对真实对象的访问，Proxy上的任何方法调用都会转发到InvocationHandler。下面是一个基于JDK动态代理机制的保护代理的例子：</p>
<pre class="crayon-plain-tag">public interface Person
{
    public String getName();
    public String getHobby();
    public int getAge();
    public String getTel();

}
class PersonImpl implements Person
{
    ...
}
public class BrowserInvocationHandler implements InvocationHandler
{
    private PersonImpl person;
    public BrowserInvocationHandler( PersonImpl p )
    {
        person = p;
    }
    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
    {
        try
        {
            //禁止浏览时获取电话
            if ( method.getName().equals( "getTel" ) )
            {
                throw new IllegalAccessException();
            }
            else
            {
                return method.invoke( person, args );
            }
        }
        catch ( InvocationTargetException e )
        {
            return null;
        }
    }

}
//这里是一个交友网站，用户在浏览时，只能访问到会员的基本信息
public class DatingProtectionProxy
{
    public PersonImpl createProxy( PersonImpl p )
    {
        ClassLoader cl = p.getClass().getClassLoader();
        Class&lt;?&gt;[] interfaces = p.getClass().getInterfaces();
        //通过静态工厂创建代理的实例
        return (PersonImpl) Proxy.newProxyInstance( cl, interfaces, new BrowserInvocationHandler( p ) );
    }
}</pre>
<div class="blog_h3"><span class="graybg">字节码生成器</span></div>
<p>Java开源街的字节码生成器允许以编程的方式，在运行时动态的决定类的结构， 比起动态代理，它允许Proxy继承RealSubject，更加灵活易用（JDK动态代理强制要求Proxy必须实现某些接口）。</p>
<p>这些字节码生成器中比较著名的有cglib、Javassist，ORM框架Hibernate就使用过这两个框架，来实现持久化对象的虚拟代理：</p>
<p><img class="aligncenter size-full wp-image-7705" src="https://blog.gmem.cc/wp-content/uploads/2006/12/patterns_ProxyPattern_Hibernate_1.png" alt="patterns_ProxyPattern_Hibernate_1" width="665" height="657" /></p>
<p>LazyInitializer类层次中包含了虚拟代理的大部分逻辑——仅仅在必须时才到持久化存储中加载实体对象。</p>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>与装饰模式的区别：意图不同，装饰者是为对象添加行为；代理则是控制对象的访问。此外代理可能会负责（延迟的）创建被包裹的对象；装饰器则从来不创建被包裹的对象</li>
<li>与工厂模式的联用：可以通过工厂来创建Proxy</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/proxy-pattern">代理模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/proxy-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
