Java三种代理模式-静态代理,动态代理和cglib代理

【README】

本文阐述了3种代理模式的定义,并编写了代码测试案例;

代理其实是一种设计模式,可以在访问目标对象的方法上下文添加处理逻辑(扩展目标对象的功能),是 切面编程的基石;

【举个例子】 抖音直播带货就是一种代理模式;主播代理了工厂对象,提供了购买商品的方法;

主播可以下调商品价格(因为走量),类似于在购买商品方法上文添加了逻辑;

主播可以赠送其他商品,类似于在购买商品方法下文添加了逻辑;

【代理模式URL】

1.定义一个带有代理方法的接口;

2.定义目标对象实现这个接口;

3.定义代理对象实现这个接口,且代理对象调用目标对象的对应方法;

4.客户端调用代理对象的方法,代理对象接着调用目标对象的方法,且代理对象可以在上下文添加逻辑(代理模式的目的所在)


 【1】静态代理(编译时生成代理类class文件)

1,代码结构: 需要代理对象和目标对象实现相同接口;

2,优点:可以在不修改目标对象的前提下,扩展其功能;

3,缺点:

  • 冗余:每次代理都要定义一个代理类,会产生过多代理类;
  • 不移维护: 一旦接口方法有增删改,则代理对象和目标对象都需要修改;

【1.1】静态代理代码示例

// 接口
public interface IUserDao {public void sayHello();
}// 目标类
public class UserDaoImpl implements IUserDao {@Overridepublic void sayHello() {System.out.println("i am method named sayHello.");}
}// 代理类 
public class UserDaoStaticProxy implements IUserDao {private IUserDao target;public UserDaoStaticProxy(IUserDao target) {this.target = target;}@Overridepublic void sayHello() {System.out.println("before");target.sayHello();System.out.println("after");}
}

main方法

public class StaticProxyTest {public static void main(String[] args) {// 目标对象IUserDao target = new UserDaoImpl();// 代理对象UserDaoStaticProxy proxy = new UserDaoStaticProxy(target);// 调用代理方法proxy.sayHello();}
}

执行结果 

before
i am method named sayHello.
after

【2】动态代理(运行时生成代理对象)

1,为了解决静态代理会有多个代理类,不易维护的缺点,我们引入了动态代理;

2,动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
3,静态代理与动态代理的区别主要在:

  • 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件;
  • 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中;

4,优点: 不需要代理对象实现接口,但目标对象必须实现接口;

5,底层实现: 底层调用了

// 生成代理对象 
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  • loader: 目标对象的类加载器;
  • interfaces: 目标对象实现的接口class数组;
  • InvocationHandler:将目标方法调用分配到的调用处理器对象;

6,调用处理类实现接口 InvocationHandler ;

类描述:InvocationHandler 是代理对象关联的调用处理程序所实现的接口。
每个代理对象都有一个关联的调用处理程序。
当在代理对象上调用方法时,方法调用被编码并分派到其调用处理程序的调用方法。

public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

 方法描述:处理代理对象上的方法调用并返回结果。

当在与其关联的代理对象上调用方法时,将在调用处理程序上调用本方法。

【2.1】动态代理代码示例

// 目标对象的接口
public interface IUserDao2 {public String sayHello();
}// 目标对象所属类
public class UserDaoImpl2  implements IUserDao2 {@Overridepublic String sayHello() {String msg = "i am method named sayHello. by dynamic proxy"; System.out.println(msg);return msg;}
}// 生成代理对象工厂
public class DynamicProxyFactory {private DynamicProxyFactory(){}public static Object getProxyInstance (Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before");// 执行目标方法Object result = method.invoke(target, args);System.out.println("after");return result;}});}
} 

main方法;

public class DynamicProxyTest {public static void main(String[] args) {// 目标对象UserDaoImpl2 target = new UserDaoImpl2();// 代理对象IUserDao2 proxy = (IUserDao2) DynamicProxyFactory.getProxyInstance(target);// 调用代理方法String result = proxy.sayHello();System.out.println(result);}
}

运行结果:

before
i am method named sayHello. by dynamic proxy
after
i am method named sayHello. by dynamic proxy


【3】cglib代理

1,cglib定义, cglib 是一个强大、高性能和高质量的代码生成库。 它可以扩展 JAVA 类并在运行时实现接口;

2,特点

  1. JDK的动态代理有一个限制,就是使用动态代理的目标对象必须实现一个或多个接口。如果想目标对象不实现接口,就可以使用CGLIB实现
  2. CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口;它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的 interception(拦截);
  3. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉;

3,cglib与动态代理最大的区别:

  • 使用动态代理的对象必须实现一个或多个接口;使用cglib代理的对象则无需实现接口,达到代理类无侵入;

【3.1】cglib代理代码示例

1,maven 引入 cglib 依赖;

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.5</version></dependency>

2,没有实现接口的目标类

public class UserDaoImpl3 {public String sayHello() {String msg = "i am method named sayHello. test cglib  proxy.";System.out.println(msg);return msg;}
}

3,代理工厂

public class CglibProxyFactory implements MethodInterceptor {private Object target;public CglibProxyFactory(Object target) {this.target = target;}public Object getProxyInstance () {this.target = target;// 工具类Enhancer enhancer = new Enhancer();// 设置父类enhancer.setSuperclass(target.getClass());// 设置回调函数enhancer.setCallback(this);// 创建子类对象代理return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("before");Object result = method.invoke(target, args);System.out.println("after");return result;}
}

4,main方法

public class CglibProxyTest {public static void main(String[] args) {// 目标对象UserDaoImpl3 target = new UserDaoImpl3();// 代理对象UserDaoImpl3 proxy = (UserDaoImpl3) new CglibProxyFactory(target).getProxyInstance();// 调用代理方法String result = proxy.sayHello();System.out.println(result);}
}

执行结果:

before
i am method named sayHello. test cglib  proxy.
after
i am method named sayHello. test cglib  proxy.


【4】java代理实现小结

1)静态代理

  • 1.1)实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
  • 1.2)静态代理在编译时产生class字节码文件,可以直接使用,效率高。

2)动态代理

  • 2.1)需要目标对象实现业务接口,代理对象关联的调用处理类需要实现InvocationHandler接口
  • 2.2)动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

3)cglib代理

  • 无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

【reference】

https://segmentfault.com/a/1190000011291179icon-default.png?t=M0H8https://segmentfault.com/a/1190000011291179

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

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

相关文章

python遗传算法工具箱的使用_遗传算法的python实现,非常值得看的一篇文章

遗传算法是一种智能优化算法&#xff0c;通常用于求解复杂的数学问题。相比于传统方法&#xff0c;遗传算法摒弃了盲目的穷举或完全随机的求解策略&#xff0c;借鉴了自然界优胜劣汰、自然进化的思想&#xff0c;快速逼近最优解。上文对遗传算法的基本内容进行了介绍&#xff0…

JAVA面试常考系列十一

转载自 JAVA面试常考系列十一 题目一 什么是JSP&#xff1f; JSP(Java Server Page)是一个文本文档&#xff0c;是一种将静态内容和动态生成内容混合在一起的技术。 JSP包含两种类型的文本&#xff1a;静态数据和JSP元素。静态数据可以用任何基于文本的格式表示&#xff0c;如H…

.NET跨平台实践:用C#开发Linux守护进程

Linux守护进程&#xff08;Daemon&#xff09;是Linux的后台服务进程&#xff0c;它脱离了与控制终端的关联&#xff0c;直接由Linux init进程管理其生命周期&#xff0c;即使你关闭了控制台&#xff0c;daemon也能在后台正常工作。 一句话&#xff0c;为Linux开发与控制台无关…

ThreadLocalRandom与Random区别

转自&#xff1a; 一文秒懂 Java ThreadLocalRandom - Java 一文秒懂 - 简单教程&#xff0c;简单编程随机数生成是一个非常常见的操作&#xff0c;而且 Java 也提供了 java.util.Random 类用于生成随机数&#xff0c;而且呢&#xff0c;这个类也是线程安全的&#xff0c;就是…

python自动配置文件_【python接口自动化】- ConfigParser配置文件的使用

前言&#xff1a;目前我们使用的绝大多数计算机程序&#xff0c;无论是办公软件&#xff0c;浏览器&#xff0c;甚至游戏、视频都是通过菜单界面系统配置的&#xff0c;它几乎成了我们使用机器的默认方式。而在python中&#xff0c;也有这样的一个配置模块可以把代码可配置化。…

JAVA面试常考系列十

转载自 JAVA面试常考系列十 题目一 Servlet是什么&#xff1f; Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xff0c;称为小服务程序或服务连接器&#xff0c;是用Java编写的服务器端程序&#xff0c;主要的作用是处理客户端请求并生成动态Web内容。…

DotNet 资源大全

Awesome DotNet&#xff0c;这又是一个 Awesome XXX 系列的资源整理&#xff0c;由 quozd 发起和维护。内容包括&#xff1a;编译器、压缩、应用框架、应用模板、加密、数据库、反编译、IDE、日志、风格指南等。 伯乐在线已在 GitHub 上发起「DotNet 资源大全中文版」的整理。欢…

javabean与json转换(fastjson与jackson两个版本)

【README】 本文演示了 javabean与json转换的开发方式&#xff1b; 要想 javabean的属性名 与 json的字段名不一致&#xff0c;也是可以转换的&#xff1b; 之前需要引入 ali.fastjson <dependency><groupId>com.alibaba</groupId><artifactId>fas…

mysql数据库新建一个递增的_分享一个mysql实验—基于数据库事务隔离级别RR及RC的测试...

概述今天主要分享一个最近做的实验&#xff0c;主要是测试mysql数据库在RR和RC不同事务隔离级别下的表现。MySQL使用可重复读来作为默认隔离级别的主要原因是语句级的Binlog。RR能提供SQL语句的写可串行化&#xff0c;保证了绝大部分情况(不安全语句除外)的DB/DR一致。下面以my…

直面Java第45期

转载自 直面Java第45期

ABP框架搭建项目系列教程基础版

我现在要着手一个新的项目&#xff0c;也打算用这个框架&#xff0c;所以想要将我一步一步用这个框架的做项目的步骤和想法跟大家分享出来。 经过前面十二篇的基础教程&#xff0c;现在终于该做个总结了。 第一篇&#xff0c;我们建议新手朋友们先通过ABP官网的启动模板生成解决…

字节数组转jsonobject(如读取HttpServletRequest.inputstream到jsonobject)

【README】 本文po出了 如何读取 字节数组到jsonobject&#xff1b; 字节数组如何获取&#xff0c;本文不再赘述&#xff1b; 【1】代码 /*** Description 字节数组转json演示* author xiao tang* version 1.0.0* createTime 2022年02月11日*/ public class ByteArr2JsonDem…

sql server 2008安装_性能不够?基于时序数据库的Zabbix 5.2安装指南

我们往往在谈论zabbix的优缺点的时候&#xff0c;提到最多的依然还是数据库(默认采用关系型数据库)&#xff0c;由于关系型数据库所有的读写都是采用sql语句解析&#xff0c;一但并发过大或者数据量过大&#xff0c;处理能力就显得捉襟见肘&#xff0c;这也是Zabbix让人诟病的地…

对象并不一定都是在堆上分配内存的

转载自 对象并不一定都是在堆上分配内存的 JVM内存分配策略 关于JVM的内存结构及内存分配方式&#xff0c;不是本文的重点&#xff0c;这里只做简单回顾。以下是我们知道的一些常识&#xff1a; 1、根据Java虚拟机规范&#xff0c;Java虚拟机所管理的内存包括方法区、虚拟机栈、…

Bash on Windows 抢鲜测试 -- 介绍及安装

前言 微软在上周的Windows BUILD大会上宣布&#xff0c;WIN10将引入原生Bash&#xff0c;并将很快在技术预览版中推出。 如此一来&#xff0c;windows的命令行工具就不再只有cmd和powershell了&#xff0c;我们可以还可以使用bash。 今天&#xff08;2016/4/8&#xff09;收到了…

jvm的client与server工作模式

【README】 JVM Server模式与client模式启动&#xff0c;最主要的差别在于&#xff1a; -Server模式启动时&#xff0c;速度较慢&#xff0c;但是一旦运行起来后&#xff0c;性能将会有很大的提升. 原因是:当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译…

python重定向到socket_python套接字流重定向实例汇总

#!/usr/bin/env python3"""测试socket-stream 重定向模式"""import sys,os,timefrom multiprocessing import Processfrom socket import *def initListenerSocket(port50008,host):"""初始化在服务器模式下调用者用于监听连接的…

Spring Boot的自动化配置原理

转载自 Spring Boot的自动化配置原理 随着Ruby、Groovy等动态语言的流行&#xff0c;相比较之下Java的开发显得格外笨重。繁多的配置、低下的开发效率、复杂的部署流程以及第三方技术集成难度大等问题一直被人们所诟病。随着Spring家族中的新星Spring Boot的诞生&#xff0c;这…

复盘Build 2016:不要错过微软给.NET开发者的这些福利

本文作者陈计节&#xff0c;ThoughtWorks 高级咨询师。多年的跨平台 .NET 开发者&#xff0c;全栈工程师&#xff0c;技术布道师。擅长互联网应用程序的设计、开发和运维等工作。 近年来&#xff0c;微软坚持为社区提供更多灵活性&#xff0c;并以更开放的思路重构其已有平台&a…

java异步线程内存可见性实验

【README】 本文演示了内存可见性的场景&#xff0c;以及解决方法&#xff1b; 相关定义如下&#xff08;转自java并发编程实战&#xff0c;一本好书&#xff0c;强烈推荐&#xff09;&#xff1a; 内存可见性&#xff1a;一个线程修改了对象状态后&#xff0c; 其他线程可以…