Java-代理模式

1、什么是代理模式

代理:自己不做,找别人帮你做

代理模式:在一个原有功能的基础上添加新的功能

分类:静态代理和动态代理

2、静态代理

原有方式:就是将核心业务和服务方法都编写在一起

package com.AE.service;public class TeamService{public void add() {try {System.out.println("开始事务");System.out.println("TeamService---add-----");System.out.println("提交事务");} catch (Exception e) {e.printStackTrace();System.out.println("事务回滚");}}
}

2.1基于类的静态代理

将服务型代码分离出来,核心业务--保存业务中只有保存功能

package com.AE.service;public class TeamService{public void add() {System.out.println("TeamService---add-----"); // 核心业务}
}
import com.AE;/*** 基于类的静态代理:*     要求继承被代理的类*     弊端:每次只能代理一个类*/
public class ProxyTeamService extends TeamService {public void add(){try {System.out.println("开始事务");super.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成System.out.println("提交事务");}catch (Exception e){System.out.println("回滚事务");}}
}

使用:

public static void main(String[] args) {TeamService ser=new ProxyTeamService();ser.add();}

这种基于类的静态代理最大的问题就是代理类之呢个代理一个类。

2.2、基于接口的静态代理

为核心业务(保存add)创建一个接口,通过接口暴露被代理的方法

要求:代理类和被代理类都实现了同一个接口

package com.AE;
/*** 接口定义核心方法*/
public interface IService {void add();
}
package  com.AE;
public class TeamService implements IService{@Overridepublic void add(){System.out.println("TeamService---- add----");// 核心业务}
}package com.AE;
public class UserService implements IService{@Overridepublic void add() {System.out.println("UserService---- add-----");}
}
package com.AE;
/*** 基于接口的静态代理:* 代理类和被代理类实现同一个接口*/
public class ProxyTranService implements IService {private IService service;//被代理的对象public ProxyTranService(IService service) {this.service = service;}@Overridepublic void add() {try {System.out.println("开始事务");service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成System.out.println("提交事务");}catch (Exception e){System.out.println("回滚事务");}}
}
package com.AE;public class ProxyLogService implements IService {private IService service;//被代理对象public ProxyLogService(IService service) {this.service = service;}@Overridepublic void add() {try {System.out.println("开始日志");service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成System.out.println("结束日志");}catch (Exception e){System.out.println("异常日志");}}
}

测试类:

public static void main(String[] args) {TeamService teamService=new TeamService();//被代理对象UserService userService=new UserService();//被代理对象ProxyTranService tranService=new ProxyTranService(userService);//事务代理对象--一级代理//tranService.add();//代理对象干活ProxyLogService logService=new ProxyLogService(tranService);//日志的代理对象--二级代理logService.add();}

2.3、提取出切面代码,作为AOP接口

就是将业务前后执行的代码,以及异常后执行代码和finally代码提取出来

package com.AE;/*** 切面:服务代码,切入到核心代码中,切入到哪里,给了四个位置*/
public interface AOP {void before();void after();void exception();void myFinally();
}
package com.AE;
public class TranAOP implements AOP {@Overridepublic void before() {System.out.println("事务----before");}@Overridepublic void after() {System.out.println("事务----after");}@Overridepublic void exception() {System.out.println("事务----exception");}@Overridepublic void myFinally() {System.out.println("事务----myFinally");}
}
package com.AE;
public class LogAop implements AOP{@Overridepublic void before() {System.out.println("日志----before");}@Overridepublic void after() {System.out.println("日志----after");}@Overridepublic void exception() {System.out.println("日志----exception");}@Overridepublic void myFinally() {System.out.println("日志----myFinally");}
}
package com.AEpublic class ProxyAOPService implements IService {private IService service;//被代理对象private AOP aop;//要加入切面public ProxyAOPService(IService service, AOP aop) {this.service = service;this.aop = aop;}@Overridepublic void add() {try {aop.before();service.add();//被代理对象干活aop.after();}catch (Exception e){aop.exception();}finally {aop.myFinally();}}
}

测试类:

@Testpublic void test02(){IService teamService=new TeamService();//被代理对象--核心内容AOP logAop=new LogAop();//切面-服务内容AOP tranAop=new TranAOP();IService service=new ProxyAOPService(teamService,logAop); //代理对象--一级代理IService service2=new ProxyAOPService(service,tranAop);//代理对象--二级代理service2.add();}

2.4、静态代理总结

1、在不修改目标对象的功能前提下,对目标对象功能扩展

2、缺点:因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。一旦接口增加方法,目标对象与代理对象都要维护。

3、动态代理

静态代理:要求代理类一定存在

动态代理:程序运行的时候,根据要被代理的对象动态生成代理类,省去代理类的编写。

类型:1、基于JDK的动态代理

           2、基于CGLIB的动态代理

有接口就用JDK的动态代理,没有就用CGLIB的动态代理

3.1、基于JDK的动态代理

3.1.1、直接编写

/*** newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)*        ClassLoader :类加载器,因为动态代理类,借助别人的类加载器。一般使用被代理对象的类加载器。*        Class<?>[] interfaces:接口类对象的集合,针对接口的代理,针对哪个接口做代理,一般使用的就是被代理对象的接口。*        InvocationHandler:句柄,回调函数,编写代理的规则代码* * public Object invoke(Object arg0, Method arg1, Object[] arg2)*        Object arg0:代理对象*        Method arg1:被代理的方法*        Object[] arg2:被代理方法被执行的时候的参数的数组*/
public static void main1(String[] args) {TeamService teamService = new TeamService();// 被代理对象// JDK提供的IService proxyService = (IService) Proxy.newProxyInstance(teamService.getClass().getClassLoader(),teamService.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {System.out.println("开始事务");Object invoke = method.invoke(teamService, args); // 核心业务System.out.println("提交事务");return invoke;} catch (Exception e) {e.printStackTrace();System.out.println("回滚事务");}  finally {System.out.println("finally---");}return null;}});proxyService.add();System.out.println(teamService.getClass());System.out.println(proxyService.getClass());}

3.1.2、结构化设计

方式一

提前写好InvocationHandler,并且使用切面来简化。

public class ProxyHandler implements InvocationHandler {private IService service;// 被代理对象、目标对象private AOP aop;public ProxyHandler(IService service, AOP aop) {this.service = service;this.aop = aop;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {aop.before();Object invoke = method.invoke(service, args); // 核心业务aop.after();return invoke;} catch (Exception e) {e.printStackTrace();aop.exception();throw e;}  finally {aop.myFinally();}}
}

然后只需要将被代理类和切面类传进去即可,然后通过动态返回的代理类进行核心业务的执行。

public static void main2(String[] args) {// 目标对象IService teamService = new TeamService();// 准备切面AOP aop = new TranAop();IService service = (IService) Proxy.newProxyInstance(teamService.getClass().getClassLoader(),teamService.getClass().getInterfaces(),new ProxyHandler(teamService, aop));service.add();System.out.println(service.getClass());}

方式二

再次简化代码的编写,因为可以直接获取动态返回的代理类,因为参数都可以通过被代理类和切面类得到。

public class ProxyFactory {private IService service;private AOP aop;public ProxyFactory(IService service, AOP aop) {this.service = service;this.aop = aop;}/*** 获取动态代理对象的实例*/public Object getProxyInstance(){return Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {aop.before();Object invoke = method.invoke(service, args); // 核心业务aop.after();return invoke;} catch (Exception e) {e.printStackTrace();aop.exception();throw e;}  finally {aop.myFinally();}}});}
}

然后通过返回的代理类进行核心业务的执行,然后假如有多个服务代码进行,则可以通过二次代理进行使用。

    public static void main(String[] args) {// 目标对象IService teamService = new TeamService();// 准备切面AOP aop = new TranAop();AOP logAop = new LogAop();// 获取代理对象IService service = (IService) new ProxyFactory(teamService, aop).getProxyInstance();IService service1 = (IService) new ProxyFactory(service, logAop).getProxyInstance();service1.add();}

3.2、基于CGLIB的动态代理

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。

  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception。

  • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉

3.2.1、代码编写

public class NBAService {public int add(String name,int id){System.out.println("NBAService---- add----");return id;}
}
public static void main(String[] args) {//目标对象:没有接口NBAService nbaService=new NBAService();//创建代理对象:选择cglib动态代理NBAService proxyService= (NBAService) Enhancer.create(nbaService.getClass(),new MethodInterceptor() {//回调函数编写代理规则@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {try {System.out.println("开始事务");Object invoke = methodProxy.invokeSuper(o, objects);//核心System.out.println("提交事务");return invoke;}catch (Exception e){System.out.println("事务回滚");throw e;}finally {System.out.println("finally------------");}}});//代理对象干活int res=proxyService.add("AE",12);System.out.println(res);}

3.2.2、结构化设计

public class CglibProxyFactory {public Object getProxyInstance(NBAService nb, AOP aop) {// cglib代理return Enhancer.create(nb.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {try {aop.before();Object o1 = methodProxy.invokeSuper(o, objects);aop.after();return o1;} catch (Exception e) {aop.exception();e.printStackTrace();} finally {aop.myFinally();}return null;}});}
}
    public static void main(String[] args) {NBAService nb = new NBAService();// 没有接口的目标对象AOP aop = new TranAop();// 创建切面// 创建代理对象:选择CGLIB动态代理NBAService proxyInstance = (NBAService) new CglibProxyFactory().getProxyInstance(nb, aop);int ae = proxyInstance.add("AE", 12);System.out.println(ae);System.out.println(proxyInstance.getClass());}

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

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

相关文章

《云原生安全攻防》-- K8s集群安全风险分析

在这个数字化快速发展的年代&#xff0c;云原生安全变得越来越重要。我明白对于很多朋友来说&#xff0c;这是一个既新奇又复杂的领域。因此&#xff0c;我整合了以往的专业积累&#xff0c;精心打造了一个专门讲解云原生安全的系列课程&#xff0c;目的是能给大家带来有价值的…

微信小程序备案流程详细操作指南

自2023年9月1日起&#xff0c;所有新上架的微信小程序均需事先完成备案手续&#xff0c;方能成功上线。而对于已经上架的存量小程序&#xff0c;也需要在2024年3月31日前完成备案工作。若在规定时间内未完成备案&#xff0c;平台将依据备案相关规定&#xff0c;自2024年4月1日起…

TC16-161T+ 音频 信号变压器 RF Transformers 600kHz-160MHz 射频集成电路 Mini-Circuits

Mini-Circuits是一家全球领先的射频、微波和毫米波元器件及子系统制造商。TC16-161T是Mini-Circuits出产的一款射频IC&#xff08;射频集成电路&#xff09;&#xff0c;具有平衡-不平衡转换器功用。制造商: Mini-Circuits 产品品种: 音频变压器/信号变压器 RoHS…

Git 如何合并多个连续的提交

我平常的编程喜欢是写一段代码就提交一次&#xff0c;本地一般不攒代码&#xff0c;生怕本地有什么闪失导致白干。但这样就又导致一个问题&#xff1a;查看历史日志时十分不方便&#xff0c;随便找一段提交可以看到&#xff1a; > git log --oneline 8f06be5 add 12/qemu-h…

OSPF基本原理和概念

文章目录 背景知识OSPF协议概述&#xff1a;OSPF区域的表示OSPF 骨干区域 –区域0OSPF 非骨干区域 -非0区域OSPF的五种区域类型OSPF工作原理OSPF 的报文类型OSPF邻居表中的七个状态 总结 背景知识 一台路由设备如何获取其他网段的路由&#xff0c;并加入到路由表中 直连路由 …

腾讯云2核2G服务器优惠价格,61元一年

腾讯云2核2G服务器多少钱一年&#xff1f;轻量服务器61元一年&#xff0c;CVM 2核2G S5服务器313.2元15个月&#xff0c;轻量2核2G3M带宽、40系统盘&#xff0c;云服务器CVM S5实例是2核2G、50G系统盘。腾讯云2核2G服务器优惠活动 txybk.com/go/txy 链接打开如下图&#xff1a;…

Etag:HTTP缓存控制机制解析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

无论PC还是Mac,都能畅快地使用移动硬盘 Mac使用NTFS移动硬盘不能读写

如果你拥有一台Mac设备&#xff0c;总会遇到尴尬的那一刻——你在Mac上用得好好的移动硬盘怎么都不能被PC识别到。又或者你朋友在PC上用得好好的移动硬盘&#xff0c;连上你的Mac后&#xff0c;Mac里的文件死活就是拷贝不进移动硬盘里。这种坑&#xff0c;相信大多数使用Mac的小…

Redis基础命令集详解及实例

Redis基础命令集详解及实例 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的&#xff0c;基于内存的高性能键值对存储系统&#xff0c;它支持多种数据结构&#xff0c;如字符串&#xff08;String&#xff09;、列表&#xff08;List&#xff09;、集合&a…

.NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数

前言&#xff1a; 本次要讲的是使用.Net HttpClient拼接multipark/form-data形式post上传文件和相关参数&#xff0c;并接收到上传文件成功后返回过来的结果&#xff08;图片地址&#xff0c;和是否成功&#xff09;。可能有很多人会说用ajax不是就可以轻松的实现吗&#xff1f…

生信数据分析——GO+KEGG富集分析

生信数据分析——GOKEGG富集分析 目录 生信数据分析——GOKEGG富集分析1. 富集分析基础知识2. GO富集分析&#xff08;Rstudio&#xff09;3. KEGG富集分析&#xff08;Rstudio&#xff09; 1. 富集分析基础知识 1.1 为什么要做功能富集分析&#xff1f; 转录组学数据得到的基…

java多线程——概述,创建方式及常用方法

前言&#xff1a; 学习到多线程了&#xff0c;整理下笔记&#xff0c;daydayup!!! 多线程 什么是线程 线程&#xff08;Thread&#xff09;是一个程序内部的一条执行流程。若程序只有一条执行流程&#xff0c;那这个程序就是单线程的程序。 什么是多线程 多线程是指从软硬件上…

数据库安全(redis、couchdb、h2database)CVE复现

redis服务默认端口&#xff1a;6379&#xff1b;我们可以通过端口扫描来判断是否存在该服务。 Redis 是一套开源的使用ANSI C 编写、支持网络、可基于内存亦可持久化的日志型、键值存储数据库&#xff0c;并提供多种语言的API。 Redis 如果在没有开启认证的情况下&#xff0c;…

Javase百问白答系列一

1 、简述 FileInputStream 类和 FileOutputStream 类的创建语法。 FileInputStream 类和 FileOutputStream 类是用来处理文件输入 /输出的类&#xff0c;创建 FileInputStream 对象的语法如下 &#xff08;其中&#xff1a;fileName表示文件的路径名称&#xff0c;可以是绝对路…

学生综合测评系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 1. 系统功能…

GitHub文件克隆到本地(GitHub desktop快速上手版)

使用 GitHub Desktop 轻松地克隆 GitHub 上的项目。 打开 GitHub Desktop 应用程序。 在菜单栏中&#xff0c;单击“文件”&#xff0c;然后选择“克隆存储库”。 在弹出的窗口中&#xff0c;选择要克隆的存储库&#xff1a; 单击与要克隆的仓库位置对应的选项卡。或者&#…

简明Pytorch分布式训练 — DistributedDataParallel 实践

上一次的Pytorch单机多卡训练主要介绍了Pytorch里分布式训练的基本原理&#xff0c;DP和DDP的大致过程&#xff0c;以及二者的区别&#xff0c;并分别写了一个小样作为参考。小样毕竟还是忽略了很多细节和工程实践时的一些处理方式的。实践出真知&#xff0c;今天&#xff08;简…

记 SpringBoot 使用@RequestBody 接收不到参数

POST请求&#xff0c;前端传的参数名字跟后端规定的参数一样。但是通过RequestBody注解接收的参数始终为NULL&#xff01; //实体类中属性没有用驼峰命名 private String SubscribeID; /*** 标题*/ private String Title;解决方案&#xff1a; 1、字段上使用JsonProperty(valu…

深入理解数据结构(1):复杂度详解

文章主题&#xff1a;复杂度详解&#x1f331;所属专栏&#xff1a;深入理解数据结构&#x1f4d8;作者简介&#xff1a;更新有关深入理解数据结构知识的博主一枚&#xff0c;记录分享自己对数据结构的深入解读。&#x1f604;个人主页&#xff1a;[₽]的个人主页&#x1f525;…

面试宝典:深入剖析golang 反射在orm模型中的应用

在 Go 语言中,反射(Reflection)是一种强大的机制,它允许程序在运行时检查和修改自身的结构和行为。在 ORM(Object-Relational Mapping,对象关系映射)模型中,反射被广泛应用于将数据库中的表记录映射到 Go 语言的结构体实例,以及将结构体实例的数据持久化到数据库中。以…