百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

啥?你还不懂Java反射机制与动态代理

ztj100 2025-02-15 18:23 23 浏览 0 评论

Java开发中反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多。

一、反射

反射机制是Java的核心特性之一,它允许在程序运行时检查类的结构并对其进行操作。这种动态特性为程序提供了极高的灵活性,可以在运行时加载类、调用方法、修改字段值,甚至实例化对象。反射机制被广泛应用于各种框架和库中,如Spring、Hibernate、MyBatis等,它们通过反射实现了高度的动态性和灵活性。

通过反射,开发者可以:

  • 动态加载类及其依赖
  • 动态调用方法,而无需在编译时知道具体方法
  • 动态修改对象的字段,甚至是私有字段

尽管反射为开发者提供了强大的功能,但也伴随着一定的性能开销和安全隐患,因此在实际使用中需要谨慎。

1. 实现方式

想象你是一名厨师,而食谱就是一个类。食谱中记录了所有菜品的制作方法,而你作为厨师通过食谱(类)指导自己的烹饪过程。这就类似于反射机制,你可以在程序运行时查看类的结构并通过类中的方法制作“菜品”。

// 示例:获取类信息和方法
Class recipeClass = Class.forName("com.example.Recipe");
String recipeName = recipeClass.getName();
System.out.println("菜品名称: " + recipeName);


// 动态调用方法
Method cookMethod = recipeClass.getDeclaredMethod("cook");
cookMethod.invoke(recipeObject);  


2. 获取类(Class)对象

在使用反射机制时,第一步是获取Class对象。Java为此提供了三种方式:

1.通过forName() -> 示例:Class.forName(“People”)
2.通过getClass() -> 示例:new People().getClass()
3.class直接获取 -> 示例:PeopleImpl.class

2.1静态方法调用

使用 getMethod(xx) 获取到对应的方法,直接使用 invoke(xx)就可以了。

public static void main(String[] args) {
    Class myClass = Class.forName("example.People");
    // 调用静态(static)方法
    Method getSex = myClass.getMethod("getName");
    getSex.invoke(myClass);
}

2.2 普通方法调用

使用getMethod() 获取方法,可以声明需要传递的参数的类型。

Object object = myClass.newInstance();
Method method = myClass.getMethod("hello",String.class);
method.invoke(object,"word!");

2.3 调用私有方法

调用私有方法的关键是设置 setAccessible(true) 属性,修改访问限制,这样设置之后就可以进行调用

Method greetMethod = clazz.getDeclaredMethod("greet", String.class);
greetMethod.setAccessible(true); // 允许访问私有方法
greetMethod.invoke(personObj, "John Doe");  // 调用方法

3. 应用

反射机制在实际开发中有广泛的应用,尤其是在框架和工具开发中。以下是一些常见的应用场景:

  • 动态加载类与方法例如Spring框架通过反射来动态加载Bean对象并调用其初始化方法。
  • 序列化与反序列化例如JSON库(如Jackson和Gson)通过反射将JSON字符串转换为Java对象
  • 注解处理在Spring等框架中,通过反射扫描类上的注解并执行相应的逻辑。
  • 动态代理例如Java AOP(面向切面编程)利用动态代理实现方法的拦截和增强。


二、动态代理

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。

实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)等。

Java的动态代理主要有两种方式:

1. JDK动态代理

JDK动态代理是通过实现接口的方式生成代理类。Proxy类和InvocationHandler接口是JDK动态代理的核心。

public class CalculatorProxy implements InvocationHandler {
    private final Object target;


    public CalculatorProxy(Object target) {
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法 " + method.getName() + " 开始执行");
        Object result = method.invoke(target, args);
        System.out.println("方法 " + method.getName() + " 执行完毕");
        return result;
    }


    public static Object newProxyInstance(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new CalculatorProxy(target));
    }
}

注意:JDK Proxy 只能代理实现接口的类(即使是extends继承类也是不可以代理的)。

2. CGlib代理

JDK 动态代理机制只能代理实现了接口的类,CGlib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。

CGlib 的调用通过实现 MethodInterceptor 接口的 intercept 方法,调用 invokeSuper 进行动态代理的,可以直接对普通类进行动态代理。

class CglibProxy implements MethodInterceptor {
    private Object target; // 代理对象
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        // 设置父类为实例类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用前");
        Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
        System.out.println("调用后");
        return result;
    }
}


public static void main(String[] args) {
    // CGLIB 动态代理调用
    CglibProxy proxy = new CglibProxy();
    Panda panda = (Panda)proxy.getInstance(new Panda());
    panda.eat();
}


3. 应用

动态代理主要用于 AOP 场景,例如日志、权限控制、事务管理等。动态代理还常用于 RPC 框架中实现远程服务调用的透明化。例如,Spring AOP 使用动态代理来实现切面编程,dubbo使用动态代理来实现远程调用。

三. 总结

Java的反射机制和动态代理为开发者提供了强大的动态功能,使得程序可以在运行时灵活地处理类、对象及其行为。虽然这些特性极大地增强了灵活性和可扩展性,但也带来了性能开销和安全隐患。因此,反射和动态代理的使用应该谨慎,尤其是在对性能有较高要求的场景下。

通过对反射机制和动态代理的深入理解,两者在实际开发中常常各自发挥作用,或者结合使用,以实现复杂的动态行为和灵活的系统设计。


如果这篇文章对您有所帮助,或者有所启发的话,帮忙给个赞 谢谢!!!

相关推荐

sharding-jdbc实现`分库分表`与`读写分离`

一、前言本文将基于以下环境整合...

三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么

在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...

MySQL8行级锁_mysql如何加行级锁

MySQL8行级锁版本:8.0.34基本概念...

mysql使用小技巧_mysql使用入门

1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...

MySQL/MariaDB中如何支持全部的Unicode?

永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...

聊聊 MySQL Server 可执行注释,你懂了吗?

前言MySQLServer当前支持如下3种注释风格:...

MySQL系列-源码编译安装(v5.7.34)

一、系统环境要求...

MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了

对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...

MySQL字符问题_mysql中字符串的位置

中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...

深圳尚学堂:mysql基本sql语句大全(三)

数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...

MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?

大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...

一文讲清怎么利用Python Django实现Excel数据表的导入导出功能

摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...

用DataX实现两个MySQL实例间的数据同步

DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...

MySQL数据库知识_mysql数据库基础知识

MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...

如何为MySQL中的JSON字段设置索引

背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...

取消回复欢迎 发表评论: