一、代理模式介绍 代理模式在不改变原始代理类的情况下,通过引入代理类来给原始类附加功能。 代理模式的主要结构如下:
Subject:抽象主题类,通过接口或抽象类声明主题和代理对象实现的业务方法
RealSubject:真实主题类,实现Subject中的具体业务,是代理对象所代表的真实对象
Proxy:代理类,其内部含有对真实主题的引用,它可以访问、控制或扩展RealSubject的功能
Client:客户端,通过使用代理类来访问真实的主题类按照上面的类图,可以实现如下代码:
//主题类接口 public interface Subject { void Request(); } //真实的主题类 public class RealSubject implements Subject{ @Override public void Request() { System.out.println("我是真实的主题类"); } } //代理类 public class Proxy implements Subject{ private RealSubject realSubject; @Override public void Request() { if (realSubject == null) { realSubject = new RealSubject(); } realSubject.Request(); } } //客户端 public class Client { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.Request(); } }
代理模式有比较广泛的使用,比如Spring AOP、RPC、缓存等。在 Java 中,根据代理的创建时期,可以将代理模式分为静态代理和动态代理,下面就来分别阐述。
二、代理模式实现 动态代理和静态代理的区分就是语言类型是在运行时检查还是在编译期检查。
2.1 静态代理
静态代理是指在编译期,也就是在JVM运行之前就已经获取到了代理类的字节码信息。即Java源码生成.class文件时期。 由于在JVM运行前代理类和真实主题类已经是确定的,因此也被称为静态代理。 在实际使用中,通常需要定义一个公共接口及其方法,被代理对象(目标对象)与代理对象一起实现相同的接口或继承相同的父类。 2.2 动态代理
动态代理,也就是在JVM运行时期动态构建对象和动态调用代理方法。常用的实现方式是反射。反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及其中包含的属性及方法。比如JDK Proxy。 此外动态代理也可以通过ASM(Java 字节码操作框架)来实现。比如CGLib。 2.2.1 JDK Proxy
这种方式是JDK自身提供的一种方式,它的实现不需要引用第三方类,只需要实现InvocationHandler接口,重写invoke()方法即可。代码实现如下所示:
public class ProxyExample { static interface Car { void running(); } static class Bus implements Car { @Override public void running() { System.out.println("bus is running"); } } static class Taxi implements Car { @Override public void running() { System.out.println("taxi is runnig"); } } //核心部分 JDK Proxy 代理类 static class JDKProxy implements InvocationHandler { private Object target; public Object getInstance(Object target) { this.target = target; //获得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(target, args); return result; } } public static void main(String[] args) { JDKProxy jdkProxy = new JDKProxy(); Car instance = (Car) jdkProxy.getInstance(new Taxi()); instance.running(); } }
实际上是通过invoke()方法来触发代理的执行方法。最终使得实现Invocation接口的类具有动态代理的能力。动态代理的好处在于不需要和静态代理一样提前写好公共的代理接口,只需要实现Invocation接口就可拥有动态代理能力。