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

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

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

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


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

相关推荐

如何将数据仓库迁移到阿里云 AnalyticDB for PostgreSQL

阿里云AnalyticDBforPostgreSQL(以下简称ADBPG,即原HybridDBforPostgreSQL)为基于PostgreSQL内核的MPP架构的实时数据仓库服务,可以...

Python数据分析:探索性分析

写在前面如果你忘记了前面的文章,可以看看加深印象:Python数据处理...

CSP-J/S冲奖第21天:插入排序

...

C++基础语法梳理:算法丨十大排序算法(二)

本期是C++基础语法分享的第十六节,今天给大家来梳理一下十大排序算法后五个!归并排序...

C 语言的标准库有哪些

C语言的标准库并不是一个单一的实体,而是由一系列头文件(headerfiles)组成的集合。每个头文件声明了一组相关的函数、宏、类型和常量。程序员通过在代码中使用#include<...

[深度学习] ncnn安装和调用基础教程

1介绍ncnn是腾讯开发的一个为手机端极致优化的高性能神经网络前向计算框架,无第三方依赖,跨平台,但是通常都需要protobuf和opencv。ncnn目前已在腾讯多款应用中使用,如QQ,Qzon...

用rust实现经典的冒泡排序和快速排序

1.假设待排序数组如下letmutarr=[5,3,8,4,2,7,1];...

ncnn+PPYOLOv2首次结合!全网最详细代码解读来了

编辑:好困LRS【新智元导读】今天给大家安利一个宝藏仓库miemiedetection,该仓库集合了PPYOLO、PPYOLOv2、PPYOLOE三个算法pytorch实现三合一,其中的PPYOL...

C++特性使用建议

1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...

Qt4/5升级到Qt6吐血经验总结V202308

00:直观总结增加了很多轮子,同时原有模块拆分的也更细致,估计为了方便拓展个管理。把一些过度封装的东西移除了(比如同样的功能有多个函数),保证了只有一个函数执行该功能。把一些Qt5中兼容Qt4的方法废...

到底什么是C++11新特性,请看下文

C++11是一个比较大的更新,引入了很多新特性,以下是对这些特性的详细解释,帮助您快速理解C++11的内容1.自动类型推导(auto和decltype)...

掌握C++11这些特性,代码简洁性、安全性和性能轻松跃升!

C++11(又称C++0x)是C++编程语言的一次重大更新,引入了许多新特性,显著提升了代码简洁性、安全性和性能。以下是主要特性的分类介绍及示例:一、核心语言特性1.自动类型推导(auto)编译器自...

经典算法——凸包算法

凸包算法(ConvexHull)一、概念与问题描述凸包是指在平面上给定一组点,找到包含这些点的最小面积或最小周长的凸多边形。这个多边形没有任何内凹部分,即从一个多边形内的任意一点画一条线到多边形边界...

一起学习c++11——c++11中的新增的容器

c++11新增的容器1:array当时的初衷是希望提供一个在栈上分配的,定长数组,而且可以使用stl中的模板算法。array的用法如下:#include<string>#includ...

C++ 编程中的一些最佳实践

1.遵循代码简洁原则尽量避免冗余代码,通过模块化设计、清晰的命名和良好的结构,让代码更易于阅读和维护...

取消回复欢迎 发表评论: