设计模式
设计模式
代理模式
代理模式简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
代理模式有静态代理和动态代理两种实现方式:
静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)
从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
实现步骤:
1,定义一个接口及其实现类
2,创建一个代理类同样实现这个接口
3,将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情
静态代理就是写一个代理类,这个类实现和目标类一样的接口,然后在方法里,手动在调用目标方法的前后加上你想要的逻辑,比如日志、权限校验、时间统计等等。
动态代理
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口。Spring AOP、RPC 框架的实现都依赖了动态代理。
从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的
JDK的动态代理
InvocationHandler
接口和 Proxy
类是核心。
Proxy
中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
这个方法一共有 3 个参数:loader :类加载器,用于加载代理对象;interfaces : 被代理类实现的一些接口;h : 实现了 InvocationHandler
接口的对象。
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
这个方法一共有 3 个参数:proxy :动态生成的代理类;method : 与代理类对象调用的方法相对应;args : 当前 method 方法的参数。
实现步骤:
- 定义一个接口及其实现类;
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑; - 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象;
CGLIB 动态代理机制
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。我们可以用 CGLIB 动态代理机制来避免。
在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。
你需要自定义 MethodInterceptor
并重写 intercept
方法,intercept
用于拦截增强被代理类的方法。
这个方法一共有 4 个参数:obj : 被代理的对象(需要增强的对象);method : 被拦截的方法(需要增强的方法);args : 方法入参;proxy : 用于调用原始方法。
你可以通过 Enhancer
类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor
中的 intercept
方法。
实现步骤:
- 定义一个类;
- 自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的invoke
方法类似; - 通过
Enhancer
类的create()
创建代理类;
对比
灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
JVM 层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
其他
代理模式和适配器模式有什么区别?
目的不同:代理模式主要关注控制对对象的访问,而适配器模式则用于接口转换,使不兼容的类能够一起工作。
结构不同:代理模式一般包含抽象主题、真实主题和代理三个角色,适配器模式包含目标接口、适配器和被适配者三个角色。
应用场景不同:代理模式常用于添加额外功能或控制对对象的访问,适配器模式常用于让不兼容的接口协同工作。