代理模式
为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。
静态代理
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
接口:
1 2 3
| public interface HelloInterface { void sayHello(); }
|
被代理类:
1 2 3 4 5 6
| public class Hello implements HelloInterface{ @Override public void sayHello() { System.out.println("Hello proxy!"); } }
|
代理类:
1 2 3 4 5 6 7 8 9
| public class HelloProxy implements HelloInterface{ private HelloInterface helloInterface = new Hello(); @Override public void sayHello() { System.out.println("Before invoke sayHello" ); helloInterface.sayHello(); System.out.println("After invoke sayHello"); } }
|
代理类调用:
被代理类被传递给了代理类HelloProxy
,代理类在执行具体方法时通过所持用的被代理类完成调用。
1 2 3 4 5 6 7 8 9
| public static void main(String[] args) { HelloProxy helloProxy = new HelloProxy(); helloProxy.sayHello(); } 输出: Before invoke sayHello Hello proxy! After invoke sayHello
|
使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
动态代理
利用反射机制在运行时创建代理类。接口、被代理类不变,我们构建一个handler
类来实现InvocationHandler
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import proxy.UserService; import proxy.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class Client2 { public static void main(String[] args) throws IllegalAccessException, InstantiationException { // 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名 //System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // 1. 创建被代理的对象,UserService接口的实现类 HelloInterface hello = new Hello(); // 2. 获取对应的 ClassLoader ClassLoader classLoader = userServiceImpl.getClass().getClassLoader(); // 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService, Class[] interfaces = hello.getClass().getInterfaces(); // 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用 // 这里创建的是一个自定义的处理器,须传入实际的执行对象 hello InvocationHandler helloHandler = new HelloHandler(hello); /* 5.根据上面提供的信息,创建代理对象 在这个过程中, a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码 b.然后根据相应的字节码转换成对应的class, c.然后调用newInstance()创建代理实例 */ HelloInterface proxy = (HelloInterface) Proxy.newProxyInstance(classLoader, interfaces, helloHandler); // 调用代理的方法 proxy.sayHello(); // 保存JDK动态代理生成的代理类,类名保存为 HelloProxy // ProxyUtils.generateClassFile(proxy.getClass(), "HelloProxy"); } }
|
InvocationHandler
和Proxy
的主要方法有
java.lang.reflect.InvocationHandler
Object invoke(Object proxy, Method method, Object[] args)
定义了代理对象调用方法时希望执行的动作,用于集中处理在动态代理类对象上的方法调用
java.lang.reflect.Proxy
1 2 3 4 5 6 7
| static InvocationHandler getInvocationHandler(Object proxy)用于获取指定代理对象所关联的调用处理器
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)返回指定接口的代理类
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 构造实现指定接口的代理类的一个新实例,所有方法会调用给定处理器对象的 invoke 方法
static boolean isProxyClass(Class<?> cl)返回 cl 是否为一个代理类
|
动态代理具体步骤:
- 通过实现
InvocationHandler
接口创建自己的调用处理器;
- 通过为 Proxy 类指定
ClassLoader
对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
- 调用方法的时候通过
super.h.invoke(this, m1, (Object[])null);
调用,其中的 super.h.invoke
实际上是在创建代理的时候传递给 Proxy.newProxyInstance
的 Handler
对象,它继承 InvocationHandler
类,负责实际的调用处理逻辑。
Handler
的 invoke
方法接收到 method、args 等参数后,进行一些处理,然后通过反射让被代理的对象 target 执行方法。
参考
https://zhuanlan.zhihu.com/p/347141071?msclkid=0fbfe08dc46311ecae26c4e44894f809
https://www.jianshu.com/p/9bcac608c714?msclkid=b40c8c68c44511ecb19300522d8d7118