设计模式 - 代理模式

发布于 2023-01-08
静态代理其实代理类和被代理类实现同一个接口,然后将被代理类的实例通过构造函数传入代理类,在代理类中,调用接口上的方法时,去调用被代理类的同方法即可,在调用前、后、异常等场景可以加入你的代理逻辑。

静态代理

静态代理其实代理类和被代理类实现同一个接口,然后将被代理类的实例通过构造函数传入代理类,在代理类中,调用接口上的方法时,去调用被代理类的同方法即可,在调用前、后、异常等场景可以加入你的代理逻辑。

如:

public class SimpleStaticProxyDemo {

    static interface IService {
        public void sayHello();
    }

    static class RealService implements IService {

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static class TraceProxy implements IService {
        private IService realService;

        public TraceProxy(IService realService) {
            this.realService = realService;
        }

        @Override
        public void sayHello() {
            System.out.println("entering sayHello");
            this.realService.sayHello();
            System.out.println("leaving sayHello");
        }
    }

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = new TraceProxy(realService);
        proxyService.sayHello();
    }
}

在这个例子中,IService 是公共接口,实际对象是 RealService,代理类为 TraceProxyTraceProxy 内部持有一个 IService 类型的成员变量,指向实际对象,在构造函数中初始化,对于 sayHello 的调用,它转发给了实际对象,在调用前后输出了一些跟踪信息。

程序输出为:

entering sayHello
hello
leaving sayHello

静态代理实现了代理模式的核心需求:"在不修改原类的情况下增强其功能" 不过,输出跟踪日志是一个很常见的需求,可能不止 RealService 需要,如果更多的类需要的话,就要为每个实际对象创建代理,代理要实现所有接口,整个过程非常繁琐。所以就有了动态代理。

动态代理 -JDK

和静态代理不一样,动态代理的代理类是动态生成的,如:

public class SimpleJDKDynamicProxyDemo {

    static interface IService {
        public void sayHello();
    }

    static class RealService implements IService {

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;

        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("entering " + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("leaving " + method.getName());
            return result;
        }
    }

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
                new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService));
        proxyService.sayHello();
    }
}

只需要创建一个类,实现 InvocationHandler 接口的 invoke 方法即可,方法第一个参数是代理类,也就是生成的代理对象,第二个参数是调用的方法,第三个参数是方法参数。

那么如何调用这个它来生成代理类呢,使用 JDK 自带的 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  • loader:类加载器,使用和实际类实现的接口的类加载器即可。
  • interfaces:实际类实现的接口列表。
  • h:就是上面我们实现 InvocationHandler 接口的实现类 SimpleInvocationHandler

JDK 动态代理的原理是自动创建一个类,实现的接口为上面的 interfaces 中的接口列表,也就是实际类实现的接口列表,生成的代理类代码大概如下:

final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        return ((Boolean) this.h.invoke(this, m1,
                new Object[] { paramObject })).booleanValue();
    }

    public final void sayHello() {
        this.h.invoke(this, m3, null);
    }

    public final String toString() {
        return (String) this.h.invoke(this, m2, null);
    }

    public final int hashCode() {
        return ((Integer) this.h.invoke(this, m0, null)).intValue();
    }

    static {
        m1 = Class.forName("java.lang.Object").getMethod("equals",
                new Class[] { Class.forName("java.lang.Object") });
        m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
                .getMethod("sayHello",new Class[0]);
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    }
}

这个类通过构造方法传入一个 InvocationHandler 类型的对象,调用所有方法时,包含 Object 中的 hashCode,equals 和 toString 方法,都会调用 InvocationHandlerinvoke 方法,也就是这个类其实只做转发作用。

通过 JDK 的动态代理,可以不用再为每个实际类手动创建一个代理类了,上面我们是获取 IService 的一个代理类:

IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
                new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService));

实际上我们也可以获取其他接口实现的代理类,如 IServiceAIServiceB

IServiceA proxyService = (IServiceA) Proxy.newProxyInstance(IServiceA.class.getClassLoader(),
                new Class<?>[] { IServiceA.class }, new SimpleInvocationHandler(realService));
IServiceB proxyService = (IServiceB) Proxy.newProxyInstance(IServiceB.class.getClassLoader(),
                new Class<?>[] { IServiceB.class }, new SimpleInvocationHandler(realService));

动态代理 - CGLIB

JDK 动态代理的局限是只能为接口创建代理,返回的代理类也只能转为某个接口实现类,如果一个类没有接口,或者向代理非接口中定义的方法,就没办法实现了。

cglib 使用其他方式避免了这些局限,如:

public class SimpleCGLibDemo {
    static class RealService {
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method,
                Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("entering " + method.getName());
            Object result = proxy.invokeSuper(object, args);
            System.out.println("leaving " + method.getName());
            return result;
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new SimpleInterceptor());
        return (T) enhancer.create();
    }

    public static void main(String[] args) throws Exception {
        RealService proxyService = getProxy(RealService.class);
        proxyService.sayHello();
    }
}

注:上述代码引用了 cglib 的包,以 maven 为例:

<dependency>  
    <groupId>cglib</groupId>  
    <artifactId>cglib</artifactId>  
    <version>3.3.0</version>  
</dependency>

RealService 为被代理的类,它没有接口, getProxy 方法接收一个 Class 对象,方法返回一个代理类。

getProxy 方法中使用了 cglib 的 Enhancer 类,Enhancer 类的 superclass 设置成了被代理的类。setCallback 设置被代理类的 public 非 final 方法被调用时的处理类。上例用使用的处理类是 SimpleInterceptor 继承 MethodInterceptor 并实现 intercept 方法。

  • 第一个参数是 object,是代理类对象
  • 第二个参数是 method,是被调用的方法。
  • 第三个参数是 args,是被调用的方法参数。
  • 第四个参数是 methodProxy,方法中使用它的 invokeSuper (object, args) 来调用被代理类的方法,注意别写成了 invoke (object, args),不然它调用的是代理类,然后又进入了这个方法,会无限循化。

与 JDK 动态代理不同的是,cglib 的用法,我们在 main 方法中也没有创建被代理类的对象,创建的对象直接就是代理对象。

实现原理:

cglib 不同于 jdk 动态代理,它是通过继承实现的,它是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有 public 非 final 方法,改为调用 callback 中的相关方法,在上例中,调用 SimpleInterceptor 的 intercept 方法。

比较

从原理上来看:

  • JDK 动态代理面向的是一组接口,它为这些接口创建了一个实现类,接口的具体实现是通过自定义的 InvocationHandler 实现的,也就是说,一个代理类背后都不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择。局限性是要求被代理类必须实现了接口。
  • CGLIB 动态代理面向的是一个具体的类,它为这个类动态创建一个子类,父类所有 public 非 final 方法,然后在这些方法中调用 MethodInterceptor 的实现,这个实现中定义了代理逻辑。不要求代理类实现接口。

从代理的角度来看:

  • JDK 动态代理:代理的是对象,需要首先有一个实际对象,然后使用自定义 InvocationHandler 引用该对象。
  • CGLIB:只会创建代理类,所有的操作都是针对代理类,无需手动创建实际对象。

参考链接

  • https://www.cnblogs.com/swiftma/p/6869790.html