马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
一、什么是代理模式
在港片中,经常能看到一些酷炫的大哥被警察抓了,警察会试图从他们口中套出一些关键信息。但这些大哥们通常会非常冷静地回应:“我有权保持沉默,我要找我的律师。”
这个律师就像是大哥的“法律盾牌”,全权处理全部法律事务。律师的脚色不但仅是代理大哥发言,更是在法律的战场上为大哥披荆斩棘。详细来说,律师会做以下几件事情:
- 准备法律文件:律师会经心准备各种法律文件,确保每一份文件都无懈可击,为大哥的辩护打下坚固的底子。
- 与检察官沟通:律师会与检察官举行多轮沟通,夺取对大哥最有利的条件,乃至大概会在法庭上展开一场猛烈的辩论。
- 法庭辩护:在法庭上,律师会代表大哥发言,用专业的法律知识和丰富的经验,为大哥举行有力的辩护,确保大哥的权益不受侵占。
- 提供法律发起:律师还会向大哥提供专业的法律发起,资助他了解自己的权利和义务,让他在法律的框架内做出最明智的决定。
这个过程就像是一场经心策划的战役,律师作为代理,不但代理了大哥的发言,还在操纵执行前后添加了诸多增强处理,确保大哥在法律的保护下安然无恙。这种场景在港片中家常便饭,不但展示了法律的严肃性,还增添了一丝戏剧性和紧张感,让人看得津津有味。
港片中的大哥,就是运用代理模式的行家能手。下面来说代理模式,代理模式是一种结构型的操持模式,代理对象(律师)具备真实对象(犯事大哥)的功能,并代替真实对象完成相应操纵(律师代表大哥讲话),同时可以在操纵执行前后举行增强处理(准备法律文件、与检察官沟通等)。
二、为什么用代理模式
通过一个形象的例子我们知道了代理模式,通过这个例子我们可以聊一下为什么要用代理模式(请律师):
- 权限控制(保护目标对象):保护客户的隐私和权益,制止不当操纵。律师作为代理,可以控制犯罪猜疑人与外界的交流,确保全部信息的传递都在法律框架内举行。比方,律师可以决定哪些信息可以透露给检察官,哪些信息必要保密。
- 优化性能(提高效率):代理模式可以实现资源的延迟加载和缓存,提高资源利用效率,减少不必要的资源斲丧。律师在必要时才会准备详细的法律文件,而不是一开始就举行大量的准备工作。比方,只有在确定案件进入法庭审理阶段时,律师才会准备详细的辩护词。
- 增加功能(减轻目标对象的负担):代理模式可以在代理对象中添加额外的功能,而不必要修改原始对象的代码。
- 简化客户端代码:客户端可以更专注于核心业务,而不必处理复杂的细节。犯罪猜疑人只必要与律师沟通,而不必要直接处理复杂的法律事务。
三、代理模式示例
代理对象分为2类分别是静态代理和动态代理
3.1 静态代理
静态代理必要我们自己去实当代理模式。下面以请律师为demo实现一个静态代理的示例。从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是认真实类的方法越来越多的时间,这样构建的代理类的代码量优劣常大的,所以就引进动态代理。
1、定义法律援助接口
- public interface LegalService {
- void representClient();
- }
复制代码 2、目标对象(大哥)
- public class Suspect implements LegalService {
- @Override
- public void representClient() {
- System.out.println("我是大哥,我需要律师为我进行辩护.");
- }
- }
复制代码 3、实当代理(律师)
- public class LawyerProxy implements LegalService {
- private Suspect suspect;
- public LawyerProxy(Suspect suspect) {
- this.suspect = suspect;
- }
- @Override
- public void representClient() {
- prepareLegalDocuments();
- communicateWithProsecutor();
- suspect.representClient();
- provideLegalAdvice();
- }
- private void prepareLegalDocuments() {
- System.out.println("准备法律文件.");
- }
- private void communicateWithProsecutor() {
- System.out.println("与检察官进行战略沟通,协商有利条件.");
- }
- private void provideLegalAdvice() {
- System.out.println("为嫌疑人提供专业法律建议,确保其权利得到保护.");
- }
- }
复制代码 4、 场景
- public class Main {
- public static void main(String[] args) {
- Suspect suspect = new Suspect();
- LegalService lawyer = new LawyerProxy(suspect);
- lawyer.representClient();
- }
- }
- //输出场景内容
- 准备法律文件.
- 与检察官进行战略沟通,协商有利条件.
- 我是大哥,我需要律师为我进行辩护.
- 为嫌疑人提供专业法律建议,确保其权利得到保护.
复制代码 3.2 动态代理
动态代理是开源工具提供给我们实现好的工具可以直接利用(动态代理允许利用一种方法的单个类(代理类)为具有恣意数量方法的恣意类(真实类)的多个方法调用提供服务。其实运用的是反射机制)。目前来说接触到的动态代理主要有2种分别对应的是jdk实现的动态代理 和 cglib动态代理。
3.2.1 jdk动态代理(接口代理)
3.2.1.1 jdk相干类和核心方法简单描述
jdk动态代理主要利用的是 java.lang.reflect包。
核心类:InvocationHandler接口 和 Proxy类
核心方法:invoke 和 newProxyInstance
- public Object invoke(Object proxy,Method method,Object[] args) throws Throwable
- public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
复制代码 3.2.1.2 jdk动态代理代码示例
法律援助接口和目标对象(大哥)不用变利用静态代理的代码,其他举行修改。
1、实现InvocationHandler ,作为代理处理器
InvocationHandler,这个处理器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- public class LawyerProxyHandler implements InvocationHandler {
- private final LegalService realLawyer;
- public LawyerProxyHandler(LegalService realLawyer) {
- this.realLawyer = realLawyer;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // 在调用真实方法之前执行的逻辑
- preHandle(args);
- // 调用真实方法
- Object result = method.invoke(realLawyer, args);
- // 在调用真实方法之后执行的逻辑
- postHandle(args);
- return result;
- }
- private void preHandle(Object[] args) {
- System.out.println("律师正在准备法律文件和证据.");
- }
- private void postHandle(Object[] args) {
- System.out.println("律师已完成对委托人的代理.");
- }
- }
复制代码 2、创建详细代理对象(律师)
- import java.lang.reflect.Proxy;
- public class LegalServiceProxy {
- public static LegalService getProxyInstance(LegalService realLawyer) {
- return (LegalService) Proxy.newProxyInstance(
- realLawyer.getClass().getClassLoader(), // 目标类的类加载器
- new Class<?>[]{LegalService.class}, // 目标类实现的接口
- new LawyerProxyHandler(realLawyer) // 代理处理器
- );
- }
- }
复制代码 3、利用场景
- public class Main {
- public static void main(String[] args) {
- // 创建真实对象
- LegalService dg = new RealLawyer();
- // 创建代理对象
- LegalService lawyerProxy = LegalServiceProxy.getProxyInstance(dg);
- // 通过代理对象调用方法
- lawyerProxy.representClient();
- }
- }
- //输出
- 律师正在准备法律文件和证据.
- 我是大哥,我需要律师为我进行辩护.
- 律师已完成对委托人的代理.
复制代码 3.2.2 cglib动态代理(继承代理)
从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时间就必要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码举行修改和动态生成。CGLIB通过继承方式实当代理。
1、大哥类要改一下,因为没有接口
- public class Suspect{
- @Override
- public void representClient() {
- System.out.println("我是大哥,我需要律师为我进行辩护.");
- }
- }
复制代码 2、 实现MethodInterceptor,作为代理处理器
实现MethodInterceptor接口,这个拦截器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- public class LawyerProxyInterceptor implements MethodInterceptor {
- private final Suspect suspect;
- public LawyerProxyInterceptor(Suspect suspect) {
- this.suspect = suspect;
- }
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- // 在调用真实方法之前执行的逻辑
- preHandle(args);
- // 调用真实方法
- Object result = proxy.invokeSuper(obj, args);
- // 在调用真实方法之后执行的逻辑
- postHandle(args);
- return result;
- }
- private void preHandle(Object[] args) {
- System.out.println("律师正在准备法律文件和证据.");
- }
- private void postHandle(Object[] args) {
- System.out.println("律师已完成对委托人的代理.");
- }
- }
复制代码 3、 创建详细代理对象(律师)
利用CGLIB的Enhancer类创建代理对象。这个代理对象将代理真实对象,并在调用方法时通过MethodInterceptor举行拦截。
- import net.sf.cglib.proxy.Enhancer;
- public class LegalServiceProxy {
- public static RealLawyer getProxyInstance(Suspect suspect) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(suspect.getClass()); // 设置目标类
- enhancer.setCallback(new LawyerProxyInterceptor(suspect)); // 设置回调
- return (Suspect) enhancer.create(); // 创建代理对象
- }
- }
复制代码 4、利用场景
- public class Main {
- public static void main(String[] args) {
- // 创建真实对象
- Suspect suspect = new Suspect();
- // 创建代理对象
- Suspect lawyerProxy = LegalServiceProxy.getProxyInstance(suspect);
- // 通过代理对象调用方法
- lawyerProxy.representClient();
- }
- }
- //输出
- 律师正在准备法律文件和证据.
- 我是大哥,我需要律师为我进行辩护.
- 律师已完成对委托人的代理.
复制代码 3.2.3 动态代理总结
JDK动态代理和CGLIB动态代理是Java中实现动态代理的两种常用机制,虽然它们都可以为目标对象创建代理对象并拦截方法调用,但它们的工作原理和利用场景有所不同。以下是它们的区别:
1. 基于接口 vs 基于类继承
- JDK动态代理:
- 基于接口:JDK动态代理要求被代理的类必须实现一个或多个接口。假如类没有实现任何接口,JDK动态代理将无法工作。
- 长处:实现简单,利用Java内置API、无需依赖第三方库。
- 缺点:只能代理接口,不能代理平凡类。方法调用时利用反射,性能相对较低。
- CGLIB动态代理:
- 基于类集成:CGLIB动态代理通过生成目标类的子类来实当代理。它不要求目标类必须实现接口,因此它实用于没有实现接口的类。
- 长处:可以代理没有接口的类。方法调用性能较高,制止了反射调用。
- 缺点:创建代理类时必要举行字节码操纵,性能开销较大。必要依赖cglib和ASM库。
2. 实现机制
- JDK动态代理:
- 利用反射机制,通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实当代理。代理对象仅代理接口中的方法。
- 当调用代理对象的方法时,代理类会拦截方法调用,并通过 InvocationHandler.invoke() 方法执行额外的逻辑。
- CGLIB动态代理:
- 基于字节码操纵,利用 CGLIB(Code Generation Library)生成目标类的子类并重写目标类的方法来实当代理。通过继承方式拦截全部非 final 方法的调用。
- CGLIB 利用的是 ASM 字节码生成框架,生成的是字节码级别的代理类,因此性能相对较好,但生成代理类的开销比JDK动态代理略大。
3. 性能差异
- JDK动态代理:
- 对于实现了接口的类来说,JDK动态代理在创建代理对象时开销较小,因为它仅依赖反射机制来处理接口方法的调用。
- 对于频繁调用代理方法的场景,JDK动态代理大概比CGLIB略慢,因为每次调用都涉及反射。
- CGLIB动态代理:
- 由于CGLIB是通过字节码生成来创建代理类,生成代理类的开销比JDK动态代理高一些,尤其是在代理类较多的情况下。
- 但CGLIB代理的实际方法调用性能更高,因为它通过字节码操纵,减少了反射调用的开销。
4. 利用场景
- JDK动态代理:
- 实用于接口驱动的编程,假如目标类实现了接口,那么利用JDK动态代理是首选方式。
- 适合在不必要对类举行直接代理的场景,通常在应用中,业务逻辑往往是通过接口定义的,因此JDK代理在实际项目中更常用。
- CGLIB动态代理:
- 实用于没有实现接口的类,比方一些现有类或者第三方库的类没有提供接口的情况下,可以利用CGLIB动态代理。
- 实用于对类举行代理时,但必要留意类不能是 final,否则CGLIB无法生成代理子类。
5. Spring AOP中的利用
- JDK动态代理:
- 在Spring AOP中,假如目标对象实现了接口,Spring默认利用JDK动态代理。这是因为Spring AOP的核心头脑是基于接口的面向切面编程(Aspect-Oriented Programming)。
- CGLIB动态代理:
- 假如目标对象没有实现任何接口,Spring AOP会自动利用CGLIB动态代理。在Spring设置中,你也可以强制利用CGLIB代理(通过设置 proxyTargetClass=true)。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |