JavaSE——代理

本文介绍了Java中的代理模式,包括静态代理和动态代理。静态代理通过创建逻辑处理类的子类,在方法前后添加额外操作。动态代理则利用`InvocationHandler`接口,动态生成代理类,在调用接口方法时进入拦截器执行,实现方法日志追踪等需求,避免重复代码。动态代理类实现了接口,调用接口方法会进入代理类并转向处理类的`invoke`方法,从而调用真实处理类。

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。

代理

什么是代理?代理可以帮我们解决什么问题?

静态代理可以干的事情:

有时候我们有这样一种操作,我们去调用逻辑,但是逻辑方法内,我只写逻辑,不做其他事情。这个数据有效性验证我们不希望与逻辑混在一起。于是我们剥离开来。

当调用逻辑完毕后,我们还要判断逻辑是否正常完毕,如果抛出异常,还需要怎么操作。也就是我们需要在逻辑方法的前后做一些处理善后。

我们要在方法前后做事情。就需要调用逻辑的地方,先不调用逻辑,而是先调用数据有效性验证方法,验证完毕后去调用逻辑。或是调用完毕后再去调用逻辑。

调用逻辑后,在判断方法是否抛出异常,在进行善后操作。

这个操作,我们可以这样:

image

调用数据验证,调用逻辑。如果抛出异常,怎么处理。如果没有异常怎么处理。

这样就做到了,在方法前后前后做了操作。我们还可以使用静态代理操作。

静态代理做法:

image

申请接口,申请方法

image

对接口的实现。这是逻辑方法,我们需要在这个逻辑方法前后执行操作

image

写类继承与逻辑实现类

当看到这里,已经大概明白什么意思了。先执行操作,再调用父类的逻辑处理,调用处理完后,再执行操作。

image

很明显,这里就不能直实例化实现类,而是去调用实现类的子类。调用子类的server。

这就是静态代理的类代理,在方法的前后进行了操作。

静态代理的接口代理:

image

这里不再继承于类,而是实现与接口,重写接口的方法

image

现在的操作就是这样:实例化继承于接口代理类,在server方法中,完成预处理,拿获得的真正的server处理对象去调用逻辑,在做收尾操作。

静态代理的类代理是这样操作:调用server类的子类,由子类预处理,super调用父类逻辑,调用完毕后再收尾处理。

静态代理的接口代理是这样操作:调用类,由类预处理,获得server对象去调用server,调用完毕后再收尾处理。

类代理与接口代理是一致的,都是不直接调用处理类。由其他类或子类处理完后,在调用具体的处理类

动态代理解决的问题:

代理做到的操作是这样:比如我们要执行方法的日志追踪。我们需要在调用方法前,日志输出,再调用方法,结束前再做日志输出

为什么要方法日志追踪?当出问题时,可以根据日志分析。所以方法日志追踪还是很有必要的。

但是我们的方法上百上千,如果一一给每个方法做日志,那样首先考虑的问题是:这样的操作会不会太重复了。加上成百上千次日志。

这个操作可以由动态代理解决。

动态代理做法:

image

书写接口

image

实现接口,重写方法

image

实现InvocationHandler借口。Invocation翻译:调用。Handler翻译:处理。这是调用处理接口

这里有3个参数,一个proxy:真正的代理对象。Method触发代理时调用的方法,args方法的参数

处理什么调用呢?后面再看,这里还有一步操作是构造器要求对象,有什么用呢?待会再看

image

获得other接口的实现类的Class。Proxy.newProxyInstance生成动态代理类,需求参数:

实现的classloader实现类的加载器
实现类的接口
处理类

参数我们从class中获得再传入。这样就完成了代理。返回的对象转换为接口。当接口调用方法,会进入拦截器执行。

我们在处理类构造器中,要求参数obj,在这里我们将other对象传进去了。

查看方法invoke。当进入处理类,一律的请求都是发向invoke方法。此方法可以决定是否继续往后调用,像拦截器一样。

如果我们要往后调用,则拿获得的obj对象,method的invoke选择调用obj的这个方法

这个操作是这样的:接口调用方法,并不会进入实现类,而是进入处理类的invoke方法。这个方法可以决定是否继续往后调用。

这个方法上有method方法对象,这个方法对象,是接口调用的那个方法。是那个对象,如果我们要使用method继续往后调用。需要考虑一个问题。

调用method需要考虑的问题,这个method是调用哪个对象的?于是我们将other实现类传递过去,就是为了在这个时候,进行预处理后,method去调用获得的obj对象,就调用到处理类上了。调用完毕后,在做收尾工作。

关于方法的第一个参数与第二个参数,我的猜测是:

有一个实验是这样的:newProxyInstance得到的动态代理类,如果转换为other实现类报错,提示类不匹配,转换出错。如果转接口才会正常运行。

我的想法是:这里生成的动态代理类,是接口的实现类,与接口产生了关系。所以可以转换为接口,而转实现类是不可以的。

所以第二个参数,要求的是interferes,这里就传递了other实现类的接口,我觉得是为了让代理类实现于接口,接口在调用时,就是进入动态代理类。由代理类,转到第三个参数处理类上去。

但是获得代理类转换为接口,再获得的class,从interfaces得到的接口是class对象,但我这里的猜测是他与要代理的类的接口有关系

至于第一个参数类的加载器,没有拿捏的确认想法,但是他获得这个类加载器,应该是为了做一些配置而需要。

当把这三个参数ok后,动态代理就拿下了。第一个给它做参数的配置,第二个用来实现于接口,第三个是明确得到处理类。

届时一旦接口调用方法时,触发进入动态类,动态类转到处理类的invoke方法。如果要真实的调用到业务处理类上,处理类再获得业务处理类的对象,invoke方法上有method对象。如果method.invoke将对象传递,表示调用这个对象的这个method对象方法,则就调用到了业务实际处理类,在方法前后完成了预处理与善后操作。

这里动态代理的好处在那里呢?动态代理类不需要我们写,生成即可,进入处理类进行代理操作。

动态代理类的好处在于,代理类不需要我们写。我们直接在处理类中操作

我们于是就可以这样干,将处理类分出去,给其余要产生代理类的使用。这样他们产生代理类后,就会进入我们的处理类。

动态代理类与静态代理的区别这里:静态代理类需要自己写,不论是接口代理还是类代理。而动态代理是自己生成,届时我们只需要在处理类中操作即可。

如果我们要动态代理一个类,那么我们每次都需要newProxyInstance生成。每代理一个类都需要产生一个代理类。但处理类可以只写一份实例化多份分出去使用。但也可以写有多个。

这就是动态代理,其过程,并不会真正调用到处理类,是调用接口的方法时,会进入代理类,在转到处理类。要调用真正的处理类,还需要我们手动操作。在我们调用前后,可以由我们操作。动态代理,类不需要我们生成,调用接口进入代理类转到处理类,我们只需要关系一个处理类的操作即可。

代理又称切面,为什么叫切面?是这样的,我们本来的处理是执行的好好的,但我们要在执行的操作前与后执行操作。这就像切下去一样,本来的操作,横切一刀进行操作。所以叫切面。这是我的解释,很多名词都是与日常有联系的。

为什么不论是静态代理,还是动态代理,都是以接口来操作。都是面向接口来操作的。我们可以这样写的:

ServerIF ser=new xxx(); //接口
Server ser=new xxx(); //实现类

使用接口的目的也很好理解,接口可以不变,但类是需要变的,接口的变,无非是增加代码,类的类就可能会影响其余的地方。但是使用接口,其余地方使用调用,更换了接口的实现类,其余地方正常使用。但如果使用的是类,对类改变,比如更换类,更换子类,那么其余对类的地方也需要修改。这个时候就只能改源码了。而接口增加也是增加功能呢,不会影响其他地方调用。增加个子类,返回子类,无需改源码,功能就扩展。这就是我觉得的解耦与开闭原则的意思

这就是代理:

静态类代理:接口的内存地址是逻辑类的子类,接口调用方法,转到子类,子类处理转父类

静态接口代理:接口的内存地址是接口的实现类,接口调用转到实现类,实现类拿逻辑类的对象调用逻辑类

两种代理,都是不调用逻辑类,转发过去调用。只是两种方式不同。

动态代理:代理类不需要我们写,我们只需要关系处理类。

猜测:代理类实现于接口,接口调用时,转到代理类,处理类再转到处理类。我们在处理类中操作。处理类如果要调用逻辑类,需要处理类获得逻辑类对象,去调用。

动态代理类,有点像静态代理的接口代理,负责帮我们拦截,但真正要操作过去,还需要获得逻辑类对象去操作

这就是代理,在方法的前与后可以进行操作。这种横切一刀插入的操作,又称切面编程。

下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用二维数组...
源码来自:https://pan.quark.cn/s/a4b39357ea24 在VC++开发过程中,对话框(CDialog)作为典型的用户界面组件,承担着与用户进行信息交互的重要角色。 在VS2008SP1的开发环境中,常常需要满足为对话框配置个性化背景图片的需求,以此来优化用户的操作体验。 本案例将系统性地阐述在CDialog框架下如何达成这一功能。 首先,需要在资源设计工具中构建一个新的对话框资源。 具体操作是在Visual Studio平台中,进入资源视图(Resource View)界面,定位到对话框(Dialog)分支,通过右键选择“插入对话框”(Insert Dialog)选项。 完成对话框内控件的布局设计后,对对话框资源进行保存。 随后,将着手进行背景图片的载入工作。 通常有两种主要的技术路径:1. **运用位图控件(CStatic)**:在对话框界面中嵌入一个CStatic控件,并将其属性设置为BST_OWNERDRAW,从而具备自主控制绘制过程的权限。 在对话框的类定义中,需要重写OnPaint()函数,负责调用图片资源并借助CDC对象将其渲染到对话框表面。 此外,必须合理处理WM_CTLCOLORSTATIC消息,确保背景图片的展示不会受到其他界面元素的干扰。 ```cppvoid CMyDialog::OnPaint(){ CPaintDC dc(this); // 生成设备上下文对象 CBitmap bitmap; bitmap.LoadBitmap(IDC_BITMAP_BACKGROUND); // 获取背景图片资源 CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = m...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值