设计模式之代理模式(Proxy )

概述

为其他对象提供一种代理来控制这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式主要分为两种:一种是静态代理,一种是动态代理。

1.静态代理

UML类图:
这里写图片描述
上面UML图涉及的角色:
抽象角色(Subject):声明真实对象和代理对象的共同接口。
真实角色(RealSubject):代理角色所代表的真实对象,是我们最终要引用的对象。
代理角色(Proxy):代理对象角色内部含有对真实对象的引用,从而可以操作真实对象。同时代理对象提供与真实对象相同的接口以便在任何时候都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

代码示例:

package interview;

interface Subject{
    void operation();
}

class RealSubject implements Subject{

    @Override
    public void operation() {
        System.out.println("真实角色的请求方法被调用!");
    }
}

class Proxy implements Subject{

    private Subject subject;

    private void preOper(){
        System.out.println("代理前执行操作!!");
    }

    @Override
    public void operation() {
        preOper();
        if(subject == null){
            subject = new RealSubject();
        }
        subject.operation();
        afterOper();
    }

    private void afterOper(){
        System.out.println("代理后执行!!");
    }
}
public class MainTest {

    public static void main(String arg[]) {
        Subject subject = new Proxy();
        subject.operation();
    }
}

代码内容很简单,客户实际需要调用的是RealSubject中的operation方法,现在用Proxy来代理RealSubject类,同样达到目的。同时还封装了preOper()和afterOper()方法。
如果按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但实际使用时,一个真实的角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀,此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

2.动态代理

在Java的动态代理机制中,有两个非常重要的类或接口,一个是InvocationHandler接口,另一个是Proxy类。

每一个动态代理类都必须要实现InvocationHandler接口,当我们通过代理对象调用一个方法时,这个方法的调用就会被转发为由InvocationHandler的invoke方法来进行调用。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

上面方法接收了三个参数:
proxy:我们所代理的真实对象
method:我们所要调用真实对象的某个方法的Method对象
args:调用真是对象某个方法时接受的参数

继续来看Proxy这个类:
作用就是动态创建一个代理对象的类,它提供了许多的方法,使用最多的是newProxyInstance 方法。

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

上面的方法同样接收了三个参数:
loader:类的加载器,来对生成的代理对象进行加载
interfaces:接口对象的数组,表示将要给需要代理的对象提供一组接口,如果我提供了一组接口给它,那么这个代理对象就实现了该接口,这样就能调用这组接口中的方法了。
h:InvocationHandler的对象,表示当我这个动态代理对象在调用方法的时候,会关联到那个InvocationHandler对象上。

代码示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Subject{

    void rent();
    void hello(String str);
}

class RealSubject implements Subject{

    public void rent() {
        System.out.println("i want to rent my house");
    }

    public void hello(String str) {
        System.out.println("hello "+ str);
    }

}
class DynamicProxy implements InvocationHandler {

    //要代理的真实对象
    private Object subject;
    //构造方法
    public DynamicProxy(Object subject){
        this.subject = subject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 在代理真实对象前我们可以添加一些自己的操作
        System.err.println("before rent my house");

        System.err.println("Method:" + method);

        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);

        //  在代理真实对象后我们也可以添加一些自己的操作
        System.err.println("after rent my house");

        return null;
    }

}

public class Main {

    public static void main(String args[]) {

        //        我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //        将需要代理的对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        //   创建我们的代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);

        System.out.println(subject.getClass().getSimpleName());
        subject.rent();
        subject.hello("LiSi");
    }

}

执行输出结果:

$Proxy0
before rent my house
Method:public abstract void Subject.rent()
after rent my house
before rent my house
Method:public abstract void Subject.hello(java.lang.String)
after rent my house
i want to rent my house
hello LiSi

System.out.println(subject.getClass().getSimpleName()); 返回的并不是subject的对象,也不是InvocationHandler对象。它是通过Proxy.newProxyInstance创建的代理对象,在jvm运行时动态生成的一个对象,以$开头,Proxy为中,最后一个数字表示对象的标号。
由于我们在newProxyInstance第二个参数提供了一组接口,那么这个代理对象就会实现这组接口,所以可以强制转化为这组接口中的任意一个,即可以转化为Subject对象。

接着下面两个方法:
subject.rent();
subject.hello(“LiSi”);

这两个方法都是通过method.invoke(subject, args);执行的。这里是通过代理对象调用实现接口的方法,这时程序会跳转到代理对象关联到的handler中的invoke方法中执行。而handler中又接受了realSubject的参数,所以method.invoke(subject, args);执行的也就是realSubject的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值