【Java动态代理如何实现】

在这里插入图片描述

✅Java动态代理如何实现

    • ✅JDK动态代理和Cglib动态代理的区别
  • ✅拓展知识仓
    • ✅静态代理和动态代理的区别
    • ✅动态代理的用途
    • ✅Spring AOP的实现方式
    • 📑JDK 动态代理的代码段
    • 📑Cglib动态代理的代码块
  • ✅注意事项:


在Java中,实现动态代理有两种方式:


1 . JDK动态代理 : Java.lang.reflect 包中的Proxy类和 InvocationHandler 接口提供了生成动态代理类的能力。


2 . Cglib动态代理 : Cglib (Code Generation Library) 是一个第三方代码生成类库,运行时在内存中动态生成一个了类对象从而实现对目标对象功能的扩展。


用一张图片看一下什么是动态代理(概念):

在这里插入图片描述


✅JDK动态代理和Cglib动态代理的区别


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


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


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


所以,使用JDK动态代理的对象必须实现一个或多个接口:而使用cgib代理的对象则无需实现接口,达到代理类无侵入。


✅拓展知识仓

✅静态代理和动态代理的区别


最大的区别就是静态代理是编译期确定的,但是动态代理却是运行期确定的。




同时,使用静态代理模式需要程序员手写很多代码,这个过程是比较浪费时间和精力的。一旦需要代理的类中方法比较多,或者需要同时代理多个对象的时候,这无疑会增加很大的复杂度。


反射是动态代理的实现方式之一。

✅动态代理的用途


Java的动态代理,在日常开发中可能并不经常使用,但是并不代表他不重要。Java的动态代理的最主要的用途就是应用在各种框架中。因为使用动态代理可以很方便的运行期生成代理类,通过代理类可以做很多事情,比如AOP,比如过滤器、拦截器等。


在我们平时使用的框架中,像 servlet 的 filter 、包括 spring 提供的 aop 以及 struts2 的拦截器都使用了动态代理功能。我们日常看到的mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部由动态代理的身影。


✅Spring AOP的实现方式


Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。


JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接 和 Proxy 类。


如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。


CGLIB (Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。


📑JDK 动态代理的代码段


public class UserServiceImpl implements UserService {
	@Override
	public void add() {
		// TODO Auto-generated method stub
		System,out,println("--------------------add----------------------");
	}
}

public class MyInvocationHandler implements InvocationHandler {
	private Object target;
	public MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		PerformanceMonior.begin(target.getClass().getlame( )+"+method.getlame());
		//System.out .println("-----------------begin “+method.getName()+"---------);
		Object result = method.invoke(target, args);
		//System.out.println("--------------end "+method.getName( )+"-----);
		PerformanceMonior.end();
		return result;
	}
	public Object getProxy() {
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this);
		
	}
}


public static void main(string[] args) {
	UserService seryice = new UserServiceImpl();
	MyInvocationHandler handler = new MyInvocationHandler(service);
	UserService proxy = (UserService) handler.getProxy();
	proxy .add();
}

📑Cglib动态代理的代码块


public class UserServiceImpl implements UserService {
	@Override
	public void add() {
		//TODO Auto-generated method stub
		System.out,println("--------------------add----------------------");
	}
	
}

public class CglibProxy implements MethodInterceptor {
	private Enhancer enhancer = new Enhancer();
	public Object getProxy(Class clazz) {
		//设置需要创建子类的类
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		//通过字节码技术动态创建子类实例
		return enhancer.create();
	}


	//实现MethodInterceptor接口方法
	public Object intercept(Object obj,Method method, Object[] args,MethodProxy proxy) throws Throwable {
		System.out.println("前置代理");
		//通过代理类调用父类中的方法
		Object result = proxy.invokeSuper(obj, args);
		System.out.printIn("后置代理");
		return result;
	}
}



public class DoCGLib {

	public static void main(String[] args) {
		CglibProxy proxy = new CglibProxy();
		//通过生成子类的方式创建代理类
		UsenServiceImpl proxyImp = (UsenServiceImpl)proxy.getProxy(UserServiceImpl.class);
		proxyImp.add():
	}
}

✅注意事项:

JDK动态代理只能用于接口,不能用于类。

  • 动态代理只对方法调用有效,对字段访问和赋值无效
  • 如果目标对象抛出了异常,那么这个异常会被代理对象抛出,而不是在调用invoke方法时抛出

与CGLIB等其他代理技术的比较:


  • CGLIB:它是一个强大的高性能的代码生成库,可以扩展JAVA类和实现JAVA接口。它主要应用于高级OOP设计和应用,如AOP实现、缓存框架、事务管理等。
  • 两者的选择:如果你的目标是基于现有类的行为进行拦截或修改,可以使用CGLIB;如果目标是基于接口进行拦截或修改,那么应该使用JDK动态代理。

💡思考:


除了JDK动态代理和CGLIB,还有其他一些常用的代理技术,如Spring AOP、AspectJ等。这些技术提供了更高级的特性,如支持方法级别的拦截、支持运行时和编译时切面等。


// 导入java.lang.reflect包中的InvocationHandler和Proxy类  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  
  
// 定义一个接口Hello  
interface Hello {  
    // 定义接口方法sayHello,没有实现  
    void sayHello();  
}  
  
// 定义一个实现Hello接口的类HelloImpl  
class HelloImpl implements Hello {  
    // 实现接口方法sayHello  
    public void sayHello() {  
        System.out.println("Hello, world!");  
    }  
}  
  
// 定义一个实现InvocationHandler接口的类DynamicProxyHandler  
class DynamicProxyHandler implements InvocationHandler {  
    // 私有成员变量obj,用来保存目标对象的引用  
    private Object obj;  
    // 构造方法,传入目标对象作为参数,赋值给obj成员变量  
    public DynamicProxyHandler(Object obj) { this.obj = obj; }  
    // 实现InvocationHandler接口的方法invoke,传入代理对象、方法、参数数组,返回值类型为Object  
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {  
        // 在目标方法执行前输出"Before method call"  
        System.out.println("Before method call");  
        // 调用目标方法,传入参数args,返回值赋值给result变量  
        Object result = m.invoke(obj, args);   
        // 在目标方法执行后输出"After method call"  
        System.out.println("After method call");  
        // 返回目标方法的返回值或者异常(如果目标方法抛出了异常)  
        return result;    
    }  
}  
  
public class DynamicProxyExample {  
    public static void main(String[] args) {  
        // 创建一个HelloImpl类的实例作为目标对象,并实现Hello接口的sayHello方法  
        Hello hello = new HelloImpl();   
        // 创建一个DynamicProxyHandler类的实例作为InvocationHandler,传入目标对象实例作为参数传入构造器中  
        InvocationHandler handler = new DynamicProxyHandler(hello);   
        /** 使用Proxy类的静态方法newProxyInstance创建代理对象实例,传入目标对象的类加载器、目标对象的接口数组以及
         * InvocationHandler实例作为参数传入构造器中。返回的是代理对象实例,类型为目标对象的接口类型。
         * 创建的代理对象实例可以直接像目标对象实例一样使用,只不过它实现了所有接口中的方法。
         * 所有这些方法的调用最终会调用到我们提供的InvocationHandler实例中对应的invoke方法中去处理。
         * 这样我们就能够在不修改原有代码的基础上为某个对象提供额外行为操作,
         * 也就是实现了在运行时动态扩展了某个类的行为功能操作,即实现了AOP的功能。
         * 这样就达到了在不修改原有代码的基础上扩展了某个类的行为功能操作的目的。
         * 比如可以在目标对象方法执行前后添加额外的逻辑操作处理。此处因为动态代理的目标是Hello接口,所以没有错误。
        */ 
         Hello proxy = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), new Class[] { Hello.class }, handler); 
   }   
}

最后总结一下JDK动态代理的思想:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错的问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序,查看库与库的依赖关系,找出出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总(专栏文章列表&…

浅谈springboot整合ganymed-ssh2远程访问linux

环境介绍 技术栈 springbootmybatis-plusmysqlganymed-ssh2 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 SSH(远程连接工具)连接原理:ssh服务是一个守护进程(demon),系统后台监听客户…

2006年AMC8数学竞赛中英文真题典型考题、考点分析和答案解析

今天距离2024年的AMC8美国数学竞赛举办还有二十多天,面临着期末考试的压力和紧张复习,更需要高效地准备AMC8比赛!“在战争中学习战争”是最有效的方式,反复做历年的AMC8真题也是备考最有效的方式之一。 通过反复研究历年真题&…

线程管理方式

线程管理方式 下图描述了线程的相关操作,包含:创建 / 初始化线程、启动线程、运行线程、删除 / 脱离线程。可以使用 rt_thread_create() 创建一个动态线程,使用 rt_thread_init() 初始化一个静态线程。 动态线程与静态线程的区别是&#xff1…

TCP:IP原理

TCP/IP 原理 TCP/IP 协议不是 TCP 和 IP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。从协议分层模型方面来讲,TCP/IP 由四个层次组成:网络接口层、网络层、传输层、应用层。 网络访问层(Network Access Layer) 网络访问层(Network …

成员函数指针作为参数是,静态函数和非静态函数的区别

成员函数指针作为参数时,静态函数和非静态函数的区别 举个 QT 的例子(没学过QT的也不要紧,这适用于学习C的同学),当我有两个类,Teacher 类和 Student 类。现在有一个场景就是,Teacher 类会发出…

【洛谷算法题】P4414-[COCI2006-2007#2] ABC【入门2分支结构】Java题解

👨‍💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P4414-[COCI2006-2007#2] ABC【入门2分支结构】Java题解🌏题目描述&a…

股市中的Santa Claus Rally (圣诞节行情)

圣诞节行情 Santa Claus Rally Santa Claus Rally 是指 12 月 25 日圣诞节前后股市的持续上涨这样一个现象。《股票交易员年鉴》的创始人 Yale Hirsch 于 1972 年创造了这个定义,他将当年最后五个交易日和次年前两个交易日的时间范围定义为反弹日期。 根据 CFRA Re…

双向可控硅触发电路图大全

双向可控硅触发电路图一: 为了提高效率,使触发脉冲与交流电压同步,要求每隔半个交流电的周期输出一个触发脉冲,且触发脉冲电压应大于4V,脉冲宽度应大于20us.图中BT为变压器,TPL521-2为光电耦合器&#xff…

靠谱免费的MAC苹果电脑杀毒软件CleanMyMac X2024

您是否曾经为Mac电脑的性能下降、存储空间不足而烦恼?是否希望有一个简单而高效的解决方案来优化您的Mac系统?那么,我向您介绍一款非常出色的工具:CleanMyMac X。它能够轻松处理这些问题,并让您的Mac恢复到最佳状态。 …

多款双向晶闸管调光电路

双向晶闸管调光控制电路原理图 交流调压是把不变的交流电压变换成有效值可调的交流电压,用一只双向晶闸管代替两只反并联晶闸管,可使电路大大简化。被广泛应用于工业加热、灯光控制、感应电动机的调速以及电解电镀的交流侧调压等场合。 用双向晶闸管组…

shell 函数的详细用法及应用

简介 本篇文章从函数的特点开始介绍 ,教会小白如何定义函数,学习函数中的各种方法,最后整理了一些实际的应用场景来帮助大家学会如何灵活应用。 文章目录如下: 1. 了解什么是shell函数 1.1. 函数的历史 1.2. 函数有哪些特点 2…

常见的Ubuntu命令30条(二)

Ubuntu命令是指在Ubuntu操作系统中用于执行各种任务和操作的命令行指令。这些命令可以用于管理系统、配置网络、安装软件、浏览文件等。Ubuntu命令通常在终端(Terminal)应用程序中输入并执行。 history:显示命令行历史记录。grep&#xff1a…

Python - 深夜数据结构与算法之 Graph

目录 一.引言 二.图的简介 1.Graph 图 2.Undirected graph 无向图 3.Directed Graph 有向图 4.DFS / BFS 遍历 三.经典算法实战 1.Num-Islands [200] 2.Land-Perimeter [463] 3.Largest-Island [827] 四.总结 一.引言 Graph 无论是应用还是算法题目在日常生活中比较…

YOLO算法改进7【中阶改进篇】:主干网络C3替换为轻量化网络MobileNetV3

解决问题:YOLOv5主干特征提取网络采用C3结构,带来较大的参数量,检测速度较慢,应用受限,在某些真实的应用场景如移动或者嵌入式设备,如此大而复杂的模型时难以被应用的。首先是模型过于庞大,面临…

reactor的原理与实现

网络模型 前情回顾服务器模型 Reactor和 ProactorReactor模型Proactor模型同步I/O模拟Poractor模型Libevent,libev,libuv优先级事件循环线程安全 前情回顾 网络IO,会涉及到两个系统对象:   一个是用户空间调用的进程或线程   …

企业计算机服务器中了babyk勒索病毒怎么办,babyk勒索病毒解密数据恢复

在数字化的今天,网络安全威胁不断增加,给企业的生产生活带来了严重影响,使得企业不得不重视数据安全问题。近日,云天数据恢复中心接到企业求助,企业的计算机服务器中了babyk勒索病毒,导致企业所有计算机系统…

【开源】基于Vue+SpringBoot的贫困地区人口信息管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 人口信息管理模块2.2 精准扶贫管理模块2.3 特殊群体管理模块2.4 案件信息管理模块2.5 物资补助模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 人口表3.2.2 扶贫表3.2.3 特殊群体表3.2.4 案件表3.2.5 物资补助表 四…

CleanMyMac X2024解锁完整官方版本

作为一款知名的系统清理软件,深受广大用户们的喜爱。它操作简洁,能够实现一键清理,包括但不仅限于清理语言文件、缓存文件、日志文件以及ISO图片缓存等。同时对于用户们比较头疼的iTunes垃圾,也能做到真正地清洁,不留文…

Hadoop(2):常见的MapReduce[在Ubuntu中运行!]

1 以词频统计为例子介绍 mapreduce怎么写出来的 弄清楚MapReduce的各个过程&#xff1a; 将文件输入后&#xff0c;返回的<k1,v1>代表的含义是&#xff1a;k1表示偏移量&#xff0c;即v1的第一个字母在文件中的索引&#xff08;从0开始数的&#xff09;&#xff1b;v1表…