java 代理、静态代理、动态代理

2019-04-25  本文已影响0人  后尘L

代理

在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。

代理对象提供一种代理以控制对委托对象的访问控制,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

通常情况下代理类和委托类实现相同的接口或继承相同的父类,代理类中通过聚合方式持有委托类的对象,代理类对象本身并不真正实现服务,而是通过调用委托类对象的相关方法来提供相关服务,并做一些额外操作。

代理的优点:
一、可以隐藏委托类的实现,实现客户与委托类之间的解耦;
二、在不修改委托类代码的情况下能够做一些额外的处理;

按照代理类的创建时间,代理分为静态代理和动态代理。

静态代理

代理类在代码编译之前已经存在叫做静态代理,一般由程序员创建。

静态代理类通常只代理一个委托类。

/**
 * 静态代理类和委托类需要共同实现的接口
 * */
public interface Subject {

    void sell();

    void print(String msg);

}
/**
 * 委托类,实现了接口
 * */
public class RealSubject implements Subject {

    @Override
    public void sell() {
        System.out.println("in sell method");
    }

    @Override
    public void print(String msg) {
        System.out.println("in print method, msg " + msg);
    }

}
/**
 * 静态代理类
 * 也实现了接口,但接口方法和实现是调用了委托类的实现
 * 静态代理类在方法中对委托类的实现进行了增强,甚至过滤
 * */
public class ProxySubject implements Subject {

    private Subject subject;  // 代理类通过聚合持有委托类实现对象的引用

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void sell() {
        System.out.println("before");
        subject.sell();    // 代理类中调用委托类的实现
        System.out.println("after");
    }

    @Override
    public void print(String msg) {
        System.out.println("before");
        subject.print(msg);    // 代理类中调用委托类的实现
        System.out.println("after");
    }

}
public class Client {

    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();  // 事先已存在的委托类对象

        ProxySubject proxySubject = new ProxySubject(realSubject);  // 创建代理类,传入已存在的委托类对象
        proxySubject.sell();  // 调用代理类方法,代理类中调用委托类对象相关方法实现服务
        proxySubject.print("this is message");
    }

}

动态代理

代理类在程序运行时创建的代理方式叫做动态代理。

首先通过newProxyInstance方法创建代理类并获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

动态代理的接口和委托类与静态代理相同。

/**
 * 中介类
 * */
public class DynamicProxy implements InvocationHandler {

    // 中介类通过聚合方式持有委托类对象,subject为委托类对象
    // 外部对代理类对象的调用转化为对invoke的调用,invoke内再调委托类对象
    // 中介类与委托类构成了静态代理关系,在这里,中介类就是代理类,委托类就是委托类
    private Object subject;

    public DynamicProxy(Object obj) {
        this.subject = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(subject, args);
        System.out.println("after");
        return result;    // 可以返回 null
    }

}
public class Main {

    public static void main(String[] args) {
        // 创建被委托类对象
        RealSubject vendor = new RealSubject();
        // 创建中介类实例
        DynamicProxy inter = new DynamicProxy(vendor);
        // 加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        // 获取代理类实例sell
        // 动态生成代理类,代理类的方法由接口指定,代理类方法的调用由handler处理,handler中方法的处理由委托类处理
        Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {Subject.class}, inter);

        // 通过代理类对象调用代理类方法,实际上会转到invoke方法调用
        proxySubject.sell();
        proxySubject.print("this is message");
    }

}

首先,通过Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {Subject.class}, handler);在程序运行时生成动态代理类。参数1是生成动态代理类的类加载器;参数2是生成的代理类要实现的接口;参数3是调用代理类方法后实际处理方法调用的handler。

当调用代理类对象的方法时,这个“调用”会转送到handler的invoke(Object proxy, Method method, Object[] args)方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

中介类通过聚合方式持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,把外部对invoke的调用最终都转为对委托类对象的调用。这就像上面的静态代理的实现方式,实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。

动态代理与静态代理区别

一、动态代理类代理接口下的所有类,而静态代理通常只代理一个类;
二、动态代理只能代理接口,而静态代理还可以代理普通共同父类;
三、动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。而静态代理在代码编写时就知道;

动态代理的优势

相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

上一篇 下一篇

猜你喜欢

热点阅读