反射机制的原理是什么

简介: 反射机制的原理是什么

第二种:Spring 框架的使用,最经典的就是xml的配置模式。

Spring 通过 XML 配置模式装载 Bean 的过程:

将程序内所有 XML 或 Properties 配置文件加载入内存中;
Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
使用反射机制,根据这个字符串获得某个类的Class实例;
动态配置实例的属性。
Spring这样做的好处是:

不用每一次都要在代码里面去new或者做其他的事情;
以后要改的话直接改配置文件,代码维护起来就很方便了;
有时为了适应某些需求,Java类里面不一定能直接调用另外的方法,可以通过反射机制来实现。
模拟 Spring 加载 XML 配置文件:

public class BeanFactory {

   private Map<String, Object> beanMap = new HashMap<String, Object>();
   /**
   * bean工厂的初始化.
   * @param xml xml配置文件
   */
   public void init(String xml) {
          try {
                 //读取指定的配置文件
                 SAXReader reader = new SAXReader();
                 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                 //从class目录下获取指定的xml文件
                 InputStream ins = classLoader.getResourceAsStream(xml);
                 Document doc = reader.read(ins);
                 Element root = doc.getRootElement();  
                 Element foo;

                 //遍历bean
                 for (Iterator i = root.elementIterator("bean"); i.hasNext();) {  
                        foo = (Element) i.next();
                        //获取bean的属性id和class
                        Attribute id = foo.attribute("id");  
                        Attribute cls = foo.attribute("class");

                        //利用Java反射机制,通过class的名称获取Class对象
                        Class bean = Class.forName(cls.getText());

                        //获取对应class的信息
                        java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
                        //获取其属性描述
                        java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
                        //设置值的方法
                        Method mSet = null;
                        //创建一个对象
                        Object obj = bean.newInstance();

                        //遍历该bean的property属性
                        for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {  
                               Element foo2 = (Element) ite.next();
                               //获取该property的name属性
                               Attribute name = foo2.attribute("name");
                               String value = null;

                               //获取该property的子元素value的值
                               for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
                                      Element node = (Element) ite1.next();
                                      value = node.getText();
                                      break;
                               }

                               for (int k = 0; k < pd.length; k++) {
                                      if (pd[k].getName().equalsIgnoreCase(name.getText())) {
                                             mSet = pd[k].getWriteMethod();
                                             //利用Java的反射极致调用对象的某个set方法,并将值设置进去
                                             mSet.invoke(obj, value);
                                      }
                               }
                        }

                        //将对象放入beanMap中,其中key为id值,value为对象
                        beanMap.put(id.getText(), obj);
                 }
          } catch (Exception e) {
                 System.out.println(e.toString());
          }
   }

   //other codes

}
反射机制的原理是什么?
Class actionClass=Class.forName(“MyClass”);
Object action=actionClass.newInstance();
Method method = actionClass.getMethod(“myMethod”,null);
method.invoke(action,null);
上面就是最常见的反射使用的例子,前两行实现了类的装载、链接和初始化(newInstance方法实际上也是使用反射调用了 方法),后两行实现了从class对象中获取到method对象然后执行反射调用。

因反射原理较复杂,下面简要描述下流程,想要详细了解的小伙伴,可以看这篇文章:https://www.cnblogs.com/yougewe/p/10125073.html

反射获取类实例 Class.forName(),并没有将实现留给了java,而是交给了jvm去加载!主要是先获取 ClassLoader, 然后调用 native 方法,获取信息,加载类则是回调 java.lang.ClassLoader。最后,jvm又会回调 ClassLoader 进类加载!

newInstance() 主要做了三件事:

权限检测,如果不通过直接抛出异常;
查找无参构造器,并将其缓存起来;
调用具体方法的无参构造方法,生成实例并返回。
获取Method对象,
上面的Class对象是在加载类时由JVM构造的,JVM为每个类管理一个独一无二的Class对象,这份Class对象里维护着该类的所有Method,Field,Constructor的cache,这份cache也可以被称作根对象。

每次getMethod获取到的Method对象都持有对根对象的引用,因为一些重量级的Method的成员变量(主要是MethodAccessor),我们不希望每次创建Method对象都要重新初始化,于是所有代表同一个方法的Method对象都共享着根对象的MethodAccessor,每一次创建都会调用根对象的copy方法复制一份:

Method copy() {

    Method res = new Method(clazz, name, parameterTypes, returnType,

                            exceptionTypes, modifiers, slot, signature,

                            annotations, parameterAnnotations, annotationDefault);

    res.root = this;

    res.methodAccessor = methodAccessor;

    return res;

}
相关文章
|
机器学习/深度学习 算法 搜索推荐
14 机器学习 - CF协同过滤推荐算法原理
14 机器学习 - CF协同过滤推荐算法原理
401 0
|
缓存 关系型数据库 MySQL
MYSQL超大分页怎么处理
MySQL超大分页是指在查询结果集非常庞大时,需要分页显示数据。由于数据库查询操作的性能开销较大,在处理超大分页时可能会导致性能问题。在这篇博客文中,我将详细探讨MySQL超大分页的问题,并提供一些解决方案来提升性能。
694 0
|
9月前
|
移动开发 jenkins 持续交付
jenkins配置git
通过上述步骤,您可以在 Jenkins 中成功配置 Git,从而实现自动拉取代码并进行构建和部署。这些配置不仅提高了开发效率,还保证了代码的连续集成和交付。确保每一步配置正确,以避免在实际使用中遇到问题。
627 1
|
缓存 NoSQL Java
面试官:Redis如何实现延迟任务?
延迟任务是计划任务,用于在未来特定时间执行。常见应用场景包括定时通知、异步处理、缓存管理、计划任务、订单处理、重试机制、提醒和数据采集。Redis虽无内置延迟任务功能,但可通过过期键通知、ZSet或Redisson实现。然而,这种方法精度有限,稳定性较差,适合轻量级需求。Redisson的RDelayedQueue提供更简单的延迟队列实现。
608 9
|
机器学习/深度学习
【保姆级教程】【YOLOv8替换主干网络】【1】使用efficientViT替换YOLOV8主干网络结构(3)
【保姆级教程】【YOLOv8替换主干网络】【1】使用efficientViT替换YOLOV8主干网络结构
|
安全 Linux Windows
深入理解操作系统内存管理:分页与分段的融合
【4月更文挑战第30天】 在现代操作系统中,内存管理是确保多任务环境稳定运行的关键。本文将深入探讨分页和分段两种内存管理技术,并分析它们如何相互融合以提供更为高效、安全的内存使用策略。通过对比这两种技术的优缺点,我们将探索现代操作系统中它们的综合应用,以及这种融合对操作系统设计和性能的影响。
|
存储 运维 Oracle
JDK1.8为什么使用元空间代替了永久代
JDK1.8为什么使用元空间代替了永久代
|
数据挖掘 Linux 数据安全/隐私保护
BUUCTF 二维码 1
BUUCTF 二维码 1
807 0
|
缓存 Java Spring
面试官猛的一问:Spring的Bean注入如何解决循环依赖的?
面试官猛的一问:Spring的Bean注入如何解决循环依赖的?
303 0
|
缓存 中间件 Go
Go 语言有哪些流行的框架和库
Go 语言有哪些流行的框架和库
669 0