Aop切面编程(2)--代理模式

1、代理模式的理解:不修改A对象的代码的基础上,对A代码块进行拓展。通过创建ProxyA代理对象,拓展A对象并调用A对象的核心功能;

即:不修改对象的源码基础上,创建代理对象,进行功能的附加和增强;

2、代理的分类:1)静态代理;2)动态代理(jdk,cglib);

3、静态代理:在编译的过程中就已经将代理对象、被代理对象、接口确定下来了,class文件已经生成了;

实现步骤:
1、定义一个接口及其实现类;
2、创建一个代理类同样实现这个接口
3、将目标对象注注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。

3.1)定义一个接口及方法ParentPrint:

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

3.2)定义一个被代理的类,并实现接口ParentPrint,其中print方法就是我们需要代理的核心方法;

package com.example.demo.proxy;public class Printer implements  ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}

3.3)定义代理类,构造一个有参数的构造器,并重写print方法(其中通过传入的参数调用被代理对象的print方法)。

package com.example.demo.proxy;/*
1、首先需要代理类ProxyStaticPrinter和被代理类Printer同时实现一个共同的接口 ;
2、代理类ProxyStaticPrinter需要在构造方法中注入接口定义对象,并重写代理的方法print(前置后置处理),在其中加入注入对象的print方法;
3、实例化代理对象ProxyStaticPrinter,实例化被代理对象Printer;
4、将被代理对象通过参数注入到代理对象(ProxyStaticPrinter)有参构造器中;
5、代理对象调用处理过的方法;
*/
public class ProxyStaticPrinter implements ParentPrint {private ParentPrint parentPrint;public ProxyStaticPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic void print(String content) {System.out.println("前置操作");parentPrint.print(content);System.out.println("后置操作");}}

3.4)测试,定义一个代理对象,将代理对象通过参数传入被代理的对象中;

    //测试public static void main(String[] args) {ParentPrint parentPrint = new Printer();ParentPrint ProxyParentPrint = new ProxyStaticPrinter(parentPrint);ProxyParentPrint.print("测试");}

运行

3.5)总结:

目标对象注注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问;

优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。

4、动态代理:动态代理包含了jdk动态代理和cglib动态代理;

4.1)jkd动态代理:

实现步骤:
1、定义一个接口及其实现类;
2、自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
3、通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;

4.1.1)定义一个接口

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

4.1.2)定义一个接口及方法ParentPrint:

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

4.1.3)定义一个被代理的类,并实现接口ParentPrint,其中print方法就是我们需要代理的核心方法;

package com.example.demo.proxy;public class Printer implements  ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}

4.1.4)定义一个代理类ProxyJdkPrinter并实现InvocationHandler,实现invoke方法,最终调用method.invoke(parentPrint,args);

注意该Proxy类中是静态方法,且接收的三个参数依次为:

ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的。
Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型。
InvocationHandler:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。

package com.example.demo.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*jdk动态代理:前提目标类必须有父接口(接口:ParentPrint,目标类:Printer)
1、创建ProxyJdkPrinter类,继承接口InvocationHandler创建代理类的调用处理程序;
2、同样引入代理类的参数对象private ParentPrint parentPrint;
3、实现invoke方法,最终调用method.invoke(parentPrint,args);
4、测试:
实例化代理类p1;
将p1放入代理类ProxyJdkPrinter;
通过Proxy类的WuDaInvocationHandler方法创建代理对象;
代理对象调用被代理的方法;
* */
public class ProxyJdkPrinter implements InvocationHandler {private ParentPrint parentPrint;public ProxyJdkPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("jdk增加操作");}return method.invoke(parentPrint,args);}}

4.1.5)测试,invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。

    public static void main(String[] args) {ParentPrint p1 = new Printer();ProxyJdkPrinter proxyJdkPrinter = new ProxyJdkPrinter(p1);//newProxyInstance:创建代理实例对象//三个参数://1、 目标类的类加载器 2、目标类的父接口  3、handlerParentPrint proxyPrint = (ParentPrint)Proxy.newProxyInstance(p1.getClass().getClassLoader(), p1.getClass().getInterfaces(), proxyJdkPrinter);proxyPrint.print("测试");}

运行

4.1.6)总结:

通过java提供的Proxy类帮我们创建代理对象,基于接口的动态代理需要利用JDK中的API,在JVM内存中动态的构建Proxy对象;
优点:可以生成所有实现接口的代理对象
缺点:JDK反射生成代理必须面向接口, 这是由Proxy的内部实现决定的。生成代理的方法中你必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。

4.2)cglib动态代理:当目标没有实现类的时候,可以使用;

实现步骤:

1、定义一个类;
2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
3、通过 Enhancer 类的 create()创建代理类;

4.2.1)定义一个被代理类Printer

package com.example.demo.proxy;public class Printer1 {public void print(String content) {System.out.println(content);}
}

4.2.2)定义一个代理类ProxyCglibPrinter,并实现MethodInterceptor

package com.example.demo.proxy;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*cglib动态代理:(目标对象不需要实现接口)
实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。
1.创建目标对象target;
2.创建interceptor对象
3.创建Enhancer对象,它以目标类和interceptor作为原料,生产出代理对象
4、enhancer设置参数setSuperclass,setCallback
5、创建代理类proxy,调用需要代理的方法;
* */
public class ProxyCglibPrinter implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("cglib增加操作");}methodProxy.invokeSuper(o,objects);return null;}}

4.2.3)测试

    public static void main(String[] args) {Printer1 target = new Printer1();ProxyCglibPrinter interceptor = new ProxyCglibPrinter();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(interceptor);Printer1 proxy = (Printer1)enhancer.create();proxy.print("测试");}

运行

4.2.4)总结:

无需代理类实现接口,使用Cblib中的Enhancer来生成代理对象子类,并实现MethodInterceptor中的intercept方法,在此方法中可以实现增强功能。

如果目标对象需要实现接口,则使用JDK代理。
如果目标对象不需要实现接口,则使用Cglib代理。

5、综上所述,再次总结:

静态代理:

jdk动态代理:

cglib动态代理:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/44466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

端到端拥塞控制的本质

昨天整理了一篇 bbr 的微分方程组建模(参见 bbr 建模)&#xff0c;算是 bbr 算法终极意义上的一个总结&#xff0c;最后也顺带了对 aimd 的描述&#xff0c;算是我最近比较满意的一篇分享了。那么接下来的问题&#xff0c;脱离出具体算法&#xff0c;上升到宏观层面&#xff0c…

git reset hard和soft的使用和区别

在Git中&#xff0c;git reset命令用于撤销提交、回溯版本和调整工作目录或暂存区状态&#xff0c;而不是gitrestore。git reset主要有三种模式&#xff1a;--soft、--mixed&#xff08;默认&#xff09;和--hard。以下是关于--hard和--soft两种模式的使用方法和区别的详细解释…

uniapp微信小程序 TypeError: $refs[ref].push is not a function

我的写法 this.$refs.addPopup.open();报错 打印出来是这样的 解决 参考未整理 原因 在当前页面使用的v-for循环 并且循环体内也有组件使用了ref&#xff08;而我没有把每个ref做区别命名&#xff09; 这样就导致了我有很多同名的ref&#xff0c;然后就报错了 解决办法&a…

AI人工智能作词,为音乐注入未来之力

在当今的音乐世界中&#xff0c;创新的力量不断推动着边界的拓展&#xff0c;而人工智能作词正以其独特的魅力&#xff0c;成为引领音乐走向未来的强大动力。 “妙笔生词智能写歌词软件&#xff08;veve522&#xff09;”无疑是这股浪潮中的璀璨明星。它利用先进的人工智能技术…

记录一次Android推流、录像踩坑过程

背景&#xff1a; 按照需求&#xff0c;需要支持APP在手机息屏时进行推流、录像。 技术要点&#xff1a; 1、手机在息屏时能够打开camera获取预览数据 2、获取预览数据时进行编码以及合成视频 一、息屏时获取camera预览数据&#xff1a; ①Camera.setPreviewDisplay(SurfaceH…

通过 Azure OpenAI 服务使用 GPT-35-Turbo and GPT-4(win版)

官方文档 Azure OpenAI 是微软提供的一项云服务&#xff0c;旨在将 OpenAI 的先进人工智能模型与 Azure 的基础设施和服务相结合。通过 Azure OpenAI&#xff0c;开发者和企业可以访问 OpenAI 的各种模型&#xff0c;如 GPT-3、Codex 和 DALL-E 等&#xff0c;并将其集成到自己…

input上传--upload

1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>上传文件</title><link rel"…

《C++并发编程实战》笔记(一、二)

一、简介 抽象损失&#xff1a;对于实现某个功能时&#xff0c;可以使用高级工具&#xff0c;也可以直接使用底层工具。这两种方式运行的开销差异称为抽象损失。 二、线程管控 2.1 线程的基本控制 1. 创建线程 线程相关的管理函数和类在头文件&#xff1a; #include <…

数据结构——线性表(C语言实现)

写在前面&#xff1a; 在前面C语言的结构体学习中&#xff0c;我提及了链表的操作&#xff0c; 学习数据结构我认为还是需要对C语言的数组、函数、指针、结构体有一定的了解&#xff0c;不然对于结构体的代码可能很难理解&#xff0c;特别是一些书籍上面用的还是伪代码&#xf…

OpenGL笔记一之基础窗体搭建以及事件响应

OpenGL笔记一之基础窗体搭建以及事件响应 总结自bilibili赵新政老师的教程 code review! 文章目录 OpenGL笔记一之基础窗体搭建以及事件响应1.运行2.目录结构3.main.cpp4.CMakeList.txt 1.运行 2.目录结构 01_GLFW_WINDOW/ ├── CMakeLists.txt ├── glad.c ├── main…

Linux基于centos7指令初学3

date指令 作用&#xff1a; date指令可以查看时间 这个指令可以进行格式化 格式&#xff1a;date %想要的内容 Y&#xff1a;年份 m&#xff1a;月份 d&#xff1a;日 H&#xff1a;时 M&#xff1a;分 S&#xff1a;秒 时间分界线可以由…

GIT相关操作,推送本地分支到远程仓库流程记录学习

git流程 切换到源文件夹&#xff1a;cd 源文件夹克隆远程仓库&#xff1a;git clone [ssh]进入项目文件夹&#xff1a;cd .\project\查看本地分支&#xff1a;git branch获取远程仓库更新&#xff0c;使远程同步&#xff1a;git fetch查看所有分支&#xff08;包括远程分支&am…

OJ-0712

示例1&#xff1a; input 8 123 124 125 121 119 122 126 123 output 1 2 6 5 5 6 0 0示例2&#xff1a; input 2 95 100 output 1 0示例3&#xff1a; input 2 100 95 output 0 1package com.wsdcode.od;import java.util.Scanner;public class Main {public static void m…

LabVIEW比例压力控制阀自动测试系统

开发了一套基于LabVIEW编程和PLC控制的比例控制阀自动测试系统。该系统能够实现共轨管稳定的超高压供给&#xff0c;自动完成比例压力控制阀的耐久测试、流量滞环测试及压力-流量测试。该系统操作简便&#xff0c;具有高精度和高可靠性&#xff0c;完全满足企业对自动化测试的需…

安装jenkins最新版本初始化配置及使用JDK1.8构建项目详细讲解

导读 1.安装1.1.相关网址1.2.准备环境1.3.下载安装 2. 配置jenkins2.1.安装插件2.2.配置全局工具2.3.系统配置 3. 使用3.1.配置job3.2.构建 提示&#xff1a;如果只想看如何使用jdk1.8构建项目&#xff0c;直接看3.1即可。 1.安装 1.1.相关网址 Jenkins官网&#xff1a;https…

RabbitMq如何保证消息的可靠性和稳定性

RabbitMq如何保证消息的可靠性和稳定性 rabbitMq不会百分之百让我们的消息安全被消费&#xff0c;但是rabbitMq提供了一些机制来保证我们的消息可以被安全的消费。 消息确认 消息者在成功处理消息后可以发送确认&#xff08;ACK&#xff09;给rabbitMq&#xff0c;通知消息已…

Hadoop-25 Sqoop迁移 增量数据导入 CDC 变化数据捕获 差量同步数据 触发器 快照 日志

章节内容 上节我们完成了如下的内容&#xff1a; Sqoop MySQL迁移到HiveSqoop Hive迁移数据到MySQL编写脚本进行数据导入导出测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机…

计算机的错误计算(二十九)

摘要 &#xff08;1&#xff09;讨论近似值的错误数字个数。有时&#xff0c;遇到数字9或0, 不太好确认近似值的错误数字个数。&#xff08;2&#xff09;并进一步解释确认计算机的错误计算&#xff08;二十八&#xff09;中一个函数值的错误数字个数。 理论上&#xff0c;我…

py2neo常用语句

1.连接数据库 Neo4j服务器默认的端口号就是7474,所以本地的主机就是"http://localhost:7474" 。 默认的用户名密码都是neo4j&#xff0c; # 连接数据库&#xff0c;输入个人配置 graph Graph("http://localhost:7474//browser/", auth("neo4j"…

百日筑基第十九天-一头扎进消息队列2

百日筑基第十九天-一头扎进消息队列2 消息队列的通讯协议 目前业界的通信协议可以分为公有协议和私有协议两种。公有协议指公开的受到认可的具有规 范的协议&#xff0c;比如 JMS、HTTP、STOMP 等。私有协议是指根据自身的功能和需求设计的协 议&#xff0c;一般不具备通用性&…