final类是否可以被代理_浅谈Java【代理设计模式】——看这篇文章就懂了

什么是代理模式

为其他对象提供一种代理以控制对这个对象的访问。

为什么使用代理模式

中介隔离:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息过滤消息把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

代理模式实现原理

代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy)

65fdf03257cf2dda854154c232e0186f.png

抽象主题角色(Subject):可以是接口,也可以是抽象类委托类角色(proxied):真实主题角色,业务逻辑的具体执行者代理类角色(Proxy):内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理

代理模式应用场景

SpringAop、日志收集、权限控制、过滤器、RPC远程调用

代理模式创建的方式

静态代理 和 动态代理静态代理
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
一句话,自己手写代理类就是静态代理。

基于接口实现方式:

//主题(Subject)
public interface OrderService {void order();
}
//实现接口
public class OrderServiceImpl implements OrderService {public void order() {System.out.println("用户下单操作..");}
}
//代理类
public class OrderServiceProxy implements OrderService {/*** 代理对象*/private OrderService proxiedOrderService;public OrderServiceProxy( OrderService orderService) {this.proxiedOrderService=orderService;}public void order() {System.out.println("日志收集开始..");proxiedOrderService.order();System.out.println("日志收集结束..");}
}
//测试
public class ClientTest {public static void main(String[] args) {OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());orderService.order();}
}

接口继承方式实现

//继承接口实现类
public class OrderServiceProxy extends OrderServiceImpl {public void order() {System.out.println("日志收集开始..");super.order();System.out.println("日志收集结束..");}
}
//测试
public class ClientTest {public static void main(String[] args) {OrderService orderService = new OrderServiceProxy();orderService.order();}
}

动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

JDK动态代理

JDK动态代理的一般步骤如下:
1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

//主题()Subject
public interface OrderService {void order();
}
//实现接口
public class OrderServiceImpl implements OrderService {public void order() {System.out.println("修改数据库订单操作..");}
}
//代理类
public class JdkInvocationHandler implements InvocationHandler {/*** 目标代理对象*/public Object target;public JdkInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(">>>日志收集开始>>>>");// 执行代理对象方法Object reuslt = method.invoke(target, args);System.out.println(">>>日志收集结束>>>>");return reuslt;}/*** 获取代理对象接口** @param <T>* @return*/public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}}
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = jdkInvocationHandler.getProxy();
proxy.order();

原理分析

1.获取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

62ed31319c0b499037bf2cf1f7023067.png


2.使用反编译工具该Proxy0.class

04ecec08e5274c80a24263a402234752.png


注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

CGLIB动态代理

Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。

CGLIB原理

运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。

Cglib优缺点

优点:JDK动态代理要求被代理的类必须实现接口,当需要代理的类没有实现接口时Cglib代理是一个很好的选择。另一个优点是Cglib动态代理比使用java反射的JDK动态代理要快
缺点:对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数

CGLIB代理实现

实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。

intercept方法的具体参数有

obj 目标类的实例

1.method 目标方法实例(通过反射获取的目标方法实例)

2.args 目标方法的参数

3.proxy 代理类的实例

该方法的返回值就是目标方法的返回值。

public class OrderServiceImpl {public void order() {System.out.println("用户下单操作..");}
}
public class CglibMethodInterceptor implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("<<<<<日志收集开始...>>>>>>>");Object reuslt = proxy.invokeSuper(obj, args);System.out.println("<<<<<日志收集结束...>>>>>>>");return reuslt;}
}System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:code");
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
// 设置代理类的付类
enhancer.setSuperclass(OrderServiceImpl.class);
// 设置回调对象
enhancer.setCallback(cglibMethodInterceptor);
// 创建代理对象
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
orderServiceImpl.order();

Maven依赖

<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.12</version></dependency>
</dependencies>

结果

>>>>cglib日志收集开始....
执行订单业务逻辑代码
>>>>cglib日志收集结束....

静态代理与动态代理区别

静态代理需要自己写代理类,而动态代理不需要写代理类。

JDK动态代理与CGLIB实现区别

JDK动态代理底层实现:
JDK的动态代理使用Java的反射技术生成动态代理类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。CGLIB动态代理底层实现:
运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高。

案例:使用AOP拦截Controller所有请求日志

@Aspect
@Component
@Slf4j
public class AopLogAspect {// 申明一个切点 里面是 execution表达式@Pointcut("execution(* com.xuyu.controller.*.*(..))")private void serviceAspect() {}// 请求method前打印内容@Before(value = "serviceAspect()")public void methodBefore(JoinPoint joinPoint) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();// 打印请求内容log.info("===============请求内容===============");log.info("请求地址:" + request.getRequestURL().toString());log.info("请求方式:" + request.getMethod());log.info("请求类方法:" + joinPoint.getSignature());log.info("请求类方法参数:" + Arrays.toString(joinPoint.getArgs()));log.info("===============请求内容===============");}// 在方法执行完结后打印返回内容@AfterReturning(returning = "o", pointcut = "serviceAspect()")public void methodAfterReturing(Object o) {log.info("--------------返回内容----------------");log.info("Response内容:" + o.toString());log.info("--------------返回内容----------------");}
}

Maven依赖信息

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.1.RELEASE</version>
</parent>
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.12</version></dependency><!-- sprinboot web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
</dependencies>

结果

===============请求内容===============
: 请求地址:http://127.0.0.1:8080/getUser
: 请求方式:GET
: 请求类方法:String com.xuyu.service.controller.IndexController.getUser(String,Integer)
: 请求类方法参数:[xuyu, 2]
: ===============请求内容===============
: >>>userName:{},xuyu
: --------------返回内容----------------
: Response内容:success_getUser
: --------------返回内容----------------

作者:须臾之余

来源:开源中国

原文:https://my.oschina.net/u/3995125/blog/3051269

福利专区:

动态代理及应用深度解析|Java SE视频课程 - 蛙课视频​www.wkcto.com
856d964fb0ff36926b6ce9b5b29a57a2.png

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

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

相关文章

《The C++ Standard Library》第50页 关于传递auto_ptr的问题

第50页最后一段话: 我怎么没记得在第43页里说不能传递auto_ptr. 可是我在第43页没有找到”不要用任何方式来传递auto_ptr”类似的语句啊。 而且里面讲的是source和sink,说的就是该如何传递auto_ptr和转移auto_ptr的ownership啊。 倒是在第44页中找到了这样的话&#xff1a; 第4…

在.NET Core 中使用Quartz.NET

Quartz.NET是功能齐全的开源作业调度系统&#xff0c;可用于最小的应用程序到大型企业系统。Quartz.NET具有三个主要概念&#xff1a;•job&#xff1a;运行的后台任务•trigger&#xff1a;控制后台任务运行的触发器。•scheduler&#xff1a;协调job和triggerASP.NET Core通过…

EntityFramework Core如何映射动态模型?

【导读】本文我们来探讨下映射动态模型的几种方式&#xff0c;相信一部分童鞋项目有这样的需求&#xff0c;比如每天/每小时等生成一张表&#xff0c;此种动态模型映射非常常见&#xff0c;经我摸索&#xff0c;这里给出每一步详细思路&#xff0c;希望能帮助到没有任何头绪的童…

构建深度学习模型:原理与实践

构建深度学习模型&#xff1a;原理与实践 引言 随着人工智能技术的飞速发展&#xff0c;深度学习已经成为当今最为炙手可热的研究领域之一。深度学习通过模拟人脑神经网络的工作原理&#xff0c;使得计算机能够具备更强大的学习和识别能力。本文将深入探讨深度学习的基本原理…

petshop4.0 详解之五(PetShop之业务逻辑层设计)[转]

业务逻辑层&#xff08;Business Logic Layer&#xff09;无疑是系统架构中体现核心价值的部分。它的关注点主要集中在业务规则的制定、业务流程的实现等与业务需求有关的系统设计&#xff0c;也即是说它是与系统所应对的领域&#xff08;Domain&#xff09;逻辑有关&#xff0…

自建比赛服务器,《三国杀》怎么自己创建比赛?自建赛事规则介绍

你知道在三国杀怎么自己创建比赛吗&#xff1f;游戏真的有这个功能吗&#xff1f;下面就让我们一起来看看三国杀自建赛事规则介绍吧。三国杀在2.113版本更新中新增了赛事大厅功能&#xff0c;该玩法支持玩家自建比赛&#xff0c;可以建立"私密赛"和“公开赛”两种比赛…

地线与接地螺丝_电气接地规范与接地的各项参数

点击上方↑「老王和你聊电气」关注我吧电气接地规范1、适用范围&#xff1a;本规范规定了生产经营单位用电系统、新建扩建、检维修、改造、办公区域、员工宿舍等电气线路接地规定。2、术语和定义&#xff1a;电气系统配置保护方法有&#xff1a;保护接地、保护接零、重复接地、…

如何在 Blazor WebAssembly中 使用 功能开关

微软Azure 团队开发的 功能管理 (Feature Management) 包 Microsoft.FeatureManagement可用于实现 功能开关&#xff0c;可以通过功能开关特性动态的改变应用程序的行为而不需要改变任何的业务逻辑代码。关于功能开关的更多功能请看Edi Wang的B站视频&#xff0c;长按小程序码进…

jbl css-h15,JBL CSS8006BM 天花音箱

原标题&#xff1a;JBL CSS8006BM 天花音箱JBL CSS8006BM 天花音箱JBL Commercial CSS-8006BM吸顶扬声器为寻呼和背景音乐应用提供了出色的性能。即使在低敲击设置下&#xff0c;高灵敏度也能提供最大音量。产品特性/ PRODUCT FEATURES价格实惠&#xff0c;高品质的双锥天花扬声…

窗宽窗位改变图像_CT、MRI图像的影像诊断4大原则、5个步骤、3大阅片方法

来源:互联网第一节 影像诊断原则与步骤医学影像诊断包括&#xff38;线、CT、MRI、超声等&#xff0c;是重要的临床诊断方法之一。为了达到正确诊断&#xff0c;必须遵循一定的诊断原则和步骤&#xff0c;才能全面、客观地作出结论。一、影像诊断原则一般应掌握16字原则&#x…

EFCore 5 新特性 Savepoints

EFCore 5 中的 SavepointsIntroEFCore 5中引入了一个新特性&#xff0c;叫做 Savepoints&#xff0c;主要是事务中使用&#xff0c;个人感觉有点类似于 Windows 上的系统还原点&#xff0c;如果事务发生了异常&#xff0c;可以回滚到某一个还原点。Savepoints当我们在一个事务里…

KBuilderTools小灰熊歌词制作

KBuilderTools小灰熊歌词制作<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />——实战KbuilderTools利用KbuilderTools您可以自己制作卡拉OK字幕描述脚本&#xff0c;这样&#xff0c;您可以利用现成的音乐资源&#xff0c;随心…

AWS 宣布创建“真正”开源的 Elasticsearch 分支

喜欢就关注我们吧&#xff01;Elasticsearch 和 Kibana 宣布变更开源许可证后引发了各方激烈讨论&#xff0c;但整起事件的另一个关键角色——被 Elastic 公司 CEO 发文怒斥的 AWS 却一直没有发声。然而就在今日&#xff0c;AWS 宣布将基于目前仍为开源状态的 Elasticsearch 和…

在服务器系统怎么设置地址怎么办,路由器怎么设置地址

路由器怎么设置地址路由器怎么设置地址呢?你是否也很想了解呢?今天我们就一起来看看相关内容吧!路由器怎么设置地址一、自动设置IP地址&#xff0c;使用DHCP服务器功能DHCP是路由器的一个特殊功能&#xff0c;使用DHCP功能可以避免因手工设置IP地址及子网掩码所产生的错误。(…

祝贺 在线文件管理系统 访问量 超过500

到目前为止,<<在线文件管理系统>>一文已有超过500位网友访问过了&#xff0c;深感欣慰。祝贺一下&#xff0c;转载于:https://www.cnblogs.com/GISDEV/archive/2007/10/21/932384.html

dotNet Core 3.1 使用 Elasticsearch

Elasticsearch 是基于 Lucene 的搜索引擎。可以非常方便地实现分布式的全文搜索&#xff0c;本文介绍在 dotNet Core 3.1 中怎样使用 Elasticsearch 。版本dotnet Core &#xff1a;3.1Elasticsearch&#xff1a;7.6.1Kibana&#xff1a;7.6.1NEST&#xff1a;7.10.1Docker&am…

在.NET Core 中实现健康检查

.NET Core中提供了开箱即用的运行状况检查&#xff0c;首先&#xff0c;我将在.NET Core API应用程序中执行运行状况检查&#xff0c;接下来&#xff0c;我们将使用DbContext集成SQL Server或数据库的运行状况检查&#xff0c;最后是如何实现自定义服务的运行状况检查。在ASP.N…

python3.7安装keras教程_Python3.7安装keras和TensorFlow的教程图解

win10 Python3.7安装keras深度学习集成包 TensorFlow 和Ubuntu下安装keras 在win10下安装 安装时必须检查你的python是否为64位&#xff0c;32位不支持&#xff01;&#xff01;&#xff01; 32 位卸载下载其中的64位在python官网 然后在 pip install --upgrade tensorflow 如果…

python离群点检测_如何从熊猫DataFrame中检测峰点(离群值)

我有一个带有多个速度值的熊猫数据帧&#xff0c;这些速度值是连续移动的值&#xff0c;但它是一个传感器数据&#xff0c;因此我们经常在中间出现误差的情况下&#xff0c;移动平均值似乎也无济于事&#xff0c;所以我可以采用什么方法用于从数据中删除这些离群值或峰点&#…

普通故障处理流程

一般OA或者BOSS的用户报故障后流程如下。、从中心机房扫描微波如果基站端能扫描到&#xff0c;但是客户端无法扫描。基本可以排除 基站端设备正常&#xff08;除天线外&#xff09;。问题一般出在客户那里&#xff0c;比如客户端微波数据丢失&#xff0c;停电。天线问题&#x…