Java 代理模式详解

前言:

使用IP代理,可以让国内IP翻墙访问国外IP的网站,其原理就是有一个代理IP,将原来自己的IP隐蔽起来,让服务器看到的是代理IP,这个代理IP是国外的,所以就能访问国外IP的网站了。那,我们来看看Java中的代理又是怎样的把。

定义:

代理模式: 为另一个对象提供一个替身或占位符以控制对这个对象的访问。

角色:

代理模式中扮演着三种角色
1.抽象角色(代理IP和原IP都是IP):声明真实对象和代理对象的共同接口;

2.代理角色(相当于IP代理中的代理IP):代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

3.真实角色 (相当于IP代理中的原IP):代理角色所代表的真实对象,是我们最终要引用的对象。

静态代理

所谓静态,就是写死的东西,需要自己手动去修改。根据上面三种角色的描述,我们可以写个例子来瞅瞅。
1.先创建一个公共的接口:

public interface PubObj {
  void   say();
}

2.创建真实角色:

public class realObj implements PubObj{

    @Override
    public void say() {
        System.out.println("real object");
    }
}

3.创建代理角色

public class ProxyObj implements PubObj {
    PubObj pub ;
    public ProxyObj( PubObj pub ){
            this.pub = pub;
    }
    @Override
    public void say() {
        System.out.println("真实角色方法调用前,做的一些操作");
        pub.say();
        System.out.println("真实角色方法调用后,做的一些操作");
    }
}

4.组合代码。

 public static void main(String args[]){
        RealObj realObj = new RealObj();
        PubObj pub = new  ProxyObj(realObj);
        pub.say();
    }

输出结果:
真实角色方法调用前,做的一些操作
real object
真实角色方法调用后,做的一些操作

感觉还是蛮清晰易懂的。Java代理与IP代理有相似之处,都是代理角色代替真实角色,保护真实角色,控制真实角色的访问。ProxyObj虽然是个代理角色,但把它当成是个真实角色就好。因为内部还是调用了真实对象的方法,功能上看上去根本没啥区别。而且还能做一些预处理和安全等的后续工作。

如果要按照上述的方法使用代理模式,那真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须 对应一个代理角色,如果大量使用会导致类的急剧膨胀;所以,没有必要使用代理的,还是不使用为好。

动态代理

因为代理大部分使用在封装框架的的时候,一般会出现这个情况,我都不知道真实的角色到底是什么,我又如何去代理呢。哎,不要急,动态代理来帮忙了。所谓的动态,就是有一个引用,暂时代替着,动态代理只有等到代码运行时,赋值给他的真实角色是什么,他就代理什么。也不多说了,来看例子吧:


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

//1.先创建一个公共的接口
interface PubObj {
    void say();
}

//2.创建代理角色
class ProxyObj<T> implements InvocationHandler {
    private T pub;

    public ProxyObj() {

    }

    public ProxyObj(T pub) {
        this.pub = pub;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("真实角色方法调用前,做的一些操作");
        method.invoke(pub, args);
        System.out.println("真实角色方法调用后,做的一些操作");
        return null;
    }
}
//3.创建真实角色
class RealObj implements PubObj {
    public RealObj() {
    }

    @Override
    public void say() {
        System.out.println("real object");
    }
}
//4.组合代码
public class ProxyModel {
//动态生成代理角色。
    public static <T> PubObj create(Class<T> clazz) throws Exception {
        PubObj realObj = (PubObj) clazz.newInstance();
        return (PubObj) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                clazz.getInterfaces(),
                new ProxyObj(realObj));
    }

    public static void main(String[] args) {
        try {
            create(RealObj.class).say();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

从上面的代码中,发现ProxyObj实现的是一个InvocationHandler接口,重写了invoke方法。在invoke方法中使用反射,调用了真实角色的方法。除了InvocationHandler接口外,还多了两个类:一个是Proxy ,一个是 Method 他们都是java.lang.reflect包下的,Method类与反射相关,反射还不太懂的同学,可以参考Java易忘,易错,重难点整合。下面我来瞧瞧InvocationHandler和 Proxy。

1、InvocationHandler
它是一个接口,该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,即代理角色,method是被代理的方法,如上例中的say(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。

2、 Proxy
该类即为动态代理类,该类中主要包含以下几个方法:   
  Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);
  返回代理类的一个实例,返回后的代理类可以当作被代理类使用,这个代理类就是动态生成的。其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组,为啥是接口数组呢,很容易想明白,因为有可能一个类实现了多个接口。
  
  Static Class getProxyClass (ClassLoader loader, Class[] interfaces);
  获得一个代理类,这个方法在 newProxyInstance()方法中被调用,具体的代码如下:
         

 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler invocationHandler)
            throws IllegalArgumentException {

        if (invocationHandler == null) {
            throw new NullPointerException("invocationHandler == null");
        }
        Exception cause;
        try {
            return getProxyClass(loader, interfaces)
                    .getConstructor(InvocationHandler.class)
                    .newInstance(invocationHandler);
        } catch (NoSuchMethodException e) {
            cause = e;
        } catch (IllegalAccessException e) {
            cause = e;
        } catch (InstantiationException e) {
            cause = e;
        } catch (InvocationTargetException e) {
            cause = e;
        }
        AssertionError error = new AssertionError();
        error.initCause(cause);
        throw error;
    }

That’s all!
也许,你现在用不到这个模式,也许,你看完后还迷迷糊糊的,只希望当你开发中用到的时候,会给你帮助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章