java_proxy

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'


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值