设计模式-结构型模式-代理模式

103 阅读3分钟

代理模式

简介

由于一些原因,需要给某个对象提供一个代理以此来控制对该对象进行访问,此时访问该对象需要一些限制,故不适合直接引用该对象,因为需要代理对象作为访问目标对象的一个媒介。
java中的代理按照代理类生成的时机不同又分为静态代理动态代理两种。静态代理代理类在代码的编译器就生成,但是动态代理代理类是在java运行时动态生成。其中动态代理又有JDK代理CGlib代理这两种。

代理模式的结构

  1. 抽象主题(Subject)类:通过接口或者抽象类声明真实主题和代理对象的业务方法
  2. 真实主题(Real Subject)类:实现类抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象
  3. 代理类(proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用。它可以访问扩展真实主题的功能

静态代理

image.png 由上图我们可以看到Proxy 实现了Subject的同时又聚合了RealSubject类,Proxy实现的功能,其实是调用了RealSubject的功能,同时又可以在其前后添加一些其没有的功能,对其进行增强。而Client是对Proxy进行访问,不是直接对RealSubject进行访问,对其有一个保护作用。

JDK动态代理

JDK动态代理,java中提供了一个动态代理类Proxy,该Proxy与上述说的代理对象的类不同,而是提供了一个创建代理对象的静态方法(newProxyInstance())来获取代理对象。

//Subject抽象主题类

public interface SellTickets {
    void sell();
}

//RealSubject 真实主题类

public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

// 代理工厂,用来创建代理对象

public class ProxyFactory {

    private TrainStation station = new TrainStation();

    public SellTickets getProxyObject() {
        //使用Proxy获取代理对象
        SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                        InvocationHandler中invoke方法参数说明:
                            proxy : 代理对象
                            method : 对应于在代理对象上调用的接口方法的 Method 实例
                            args : 代理对象调用接口方法时传递的实际参数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
                        //执行真实对象
                        Object result = method.invoke(station, args);
                        return result;
                    }
                });
        return sellTickets;
    }
}

以上可知JDK代理的对象的类需要实现一接口,如果没有接口是不能使用JDK代理。

CGlib动态代理

没有接口是不能使用JDK代理,但是可以使用CGlib代理。CGLib原理是动态生成被代理类的子类。
在JDK1.8,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。

public class TrainStation {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代理工厂
public class ProxyFactory implements MethodInterceptor {

    private TrainStation target = new TrainStation();

    public TrainStation getProxyObject() {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer =new Enhancer();
        //设置父类的字节码对象
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        TrainStation obj = (TrainStation) enhancer.create();
        return obj;
    }

    /*
        intercept方法参数说明:
            o : 代理对象
            method : 真实对象中的方法的Method实例
            args : 实际参数
            methodProxy :代理对象中的方法的method实例
     */
    public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
        TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
        return result;
    }
}

其中ProxyFactory实现了MethodInterceptor,函数增强的方法是在intercept()函数中构写。 其中CGlib代理中内部实现逻辑是和JDK差不多,只不是是把真实对象类当作接口(类比但不相同)