- Published on
什么是反射机制?RocketMQ中是如何使用的?
- Authors
- Name
- Tan Xiang
什么是反射机制?
反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有属性和方法。Java的反射可以:
在运行时判断任意一个对象所属的类。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时任意调用一个对象的方法
在运行时构造任意一个类的对象
Object obj=//...任意对象;
Class<?> clazz = obj.getclass();
//获取成员变量
Field[] fields =clazz.getDeclaredFields();
for(Field field : fields){
System.out.println("成员变量: "+ field.getName());
}
//获取方法
Method[] methods =clazz.getDeclaredMethods();
for(Method method:methods){
System.out.println("方法:" + method.getName));
}
Method method = clazz.getDeclaredMethod("methodName",// 方法参数类型...)
method.setAccessible(true);//如果方法是私有的
method.invoke(obj,//方法参数...);
// 默认构造函数
Object obj=clazz.newInstance();
//或者使用特定的构造函数
Constructor<?> constructor = clazz.getconstructor(// 参数类型...);
Object obj= constructor.newInstance(// 构造函数参数...);
反射的好处是可以提升程序的灵活性和扩展性,比较容易在运行期间干很多事情。但是也存在很多问题:
- 代码可读性低。
- 反射代码执行的性能低
- 反射破环了封装性
所以,我们应该在业务代码中尽量避免使用反射。但在部分场景中,反射的用处是很大的。
反射为什么慢
- 动态解析 :反射涉及动态解析的类型,导致无法进行某些Java虚拟机优化,如JIT优化。
- 参数包装 :反射调用时,参数需要被包装成
Object[]
类型,执行时又需要拆包,这个过程消耗时间。 - 方法查找 :反射调用方法时需要遍历方法数组并检查可见性,这些都是耗时的操作。
反射常见的应用场景
动态代理 :可以在运行时创建代理类。
JDBC:使用
Class.forName
动态加载数据库驱动。BeanUtils:用于属性值的拷贝。
RPC框架 :实现远程调用的动态处理。
ORM框架 :如Hibernate,使用反射操作数据库表。
Spring的IOC/DI:通过反射实现依赖注入。
反射和Class的关系
Java的Class类是java反射机制的基础,通过Class类我们可以获得关于一个类的相关信息。Java.lang.Class是一个比较特殊的类,它用于封装被装入到IM中的类(包括类和接口)的信息。当一个类或接口被装入到IVM时便会产生一个与之关联的java.lang.Class对象,可以通过这个Class对象对被装入类的详细信息进行访问。虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
如何通过反射破坏单例模式?
https://www.txlink.me/blog/202409/如何破坏单例模式?
RocketMQ中哪里使用到了反射?
1 MixAll中打印参数
如图所示,printObjectProperties函数用于打印object的所有非静态字段值到指定的logger中,可选择仅打印标注为@ImportantField的字段。
在broker启动时,BrokerStartup中便调用了该方法将brokerConfig、nettyServerConfig等配置类的属性打印到日志中。
2 分层存储中动态选择文件存储
分层存储是什么?
参考https://github.com/apache/rocketmq/tree/develop/tieredstore
消息队列 RocketMQ 分层存储允许用户将消息数据从本地磁盘卸载到其他更便宜、更大的存储介质上。这样用户就可以以更低的成本延长消息保留时间。并且不同的 topic 可以根据需要灵活指定不同的 TTL。
这里的FileSegment就像是插件,通过配置tieredBackendServiceProvider属性,默认为“"org.apache.rocketmq.tieredstore.provider.MemoryFileSegment"”,通过反射动态创建实例。
public FileSegmentFactory(MetadataStore metadataStore, MessageStoreConfig storeConfig) {
try {
this.storeConfig = storeConfig;
this.metadataStore = metadataStore;
Class<? extends FileSegment> clazz =
Class.forName(storeConfig.getTieredBackendServiceProvider()).asSubclass(FileSegment.class);
fileSegmentConstructor = clazz.getConstructor(
MessageStoreConfig.class, FileSegmentType.class, String.class, Long.TYPE);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
结论
反射机制在Java编程中是一个强大的工具,它提供了极大的灵活性和动态性。然而,使用反射时需要谨慎,因为它可能导致性能下降和代码可读性降低。在实际开发中,建议在必要时使用反射,避免在业务代码中频繁使用。
FAQs
- 反射机制的主要用途是什么?
- 反射机制主要用于在运行时获取类的信息,动态调用方法,和构造对象。
- 反射会影响程序性能吗?
- 是的,反射会导致性能下降,因为它涉及动态解析和多次检查。
- Java中的反射如何实现?
- 反射通过
java.lang.Class
类和相关的方法,如getDeclaredMethods()
和getDeclaredFields()
来实现。
- 反射通过
- 反射的缺点有哪些?
- 反射的缺点包括代码可读性低、性能较差以及可能破坏封装性。
- 在什么情况下应该使用反射?
- 当需要动态加载类、调用方法或构造对象时,可以使用反射,但应尽量避免在业务逻辑中频繁使用。