手写Spring第三篇番外,反射的基本使用

news2024/11/24 12:45:34

上一篇发出去之后,我有一个朋友说 beanDefinition.getBeanClass().newInstance() 这句代码太突兀了,就像是在甜甜的睡梦中,突然脚踩悬崖惊醒。

image.png

像我这种插朋友两刀的人必须安排了,不止安排 newInstance 还把反射基本用法也给安排了。

先看看反射是什么?知己知彼才能百战百胜。

Reflection is commonly used by programs which require the ability to examine or modify 
the runtime behavior of applications running in the Java virtual machine.
This is a relatively advanced feature and should be used only by developers 
who have a strong grasp of the fundamentals of the language. 
With that caveat in mind, reflection is a powerful technique 
and can enable applications to perform operations which would otherwise be impossible.

这写的什么破玩意,每个字母我都能看懂,但不知道为什么合在一起看就想睡觉。让 GPT 翻译一下:

反射通常用于需要检查或修改在Java虚拟机中运行的应用程序的运行时行为的程序。
这是一个相对高级的特性,应该仅由对语言基础知识有深刻理解的开发人员使用。
警告,反射是一种强大的技术,可以使应用程序执行在其他情况下不可能的操作。

我只看到了应该由对语言基础知识由深刻理解的开发人员使用。看来想低调都不行了,连 Oracle 都为我量身定做了反射这个功能。那不用就说不过去了,盘它。

要真正理解反射,必须先理解 运行时。运行时是相对的,像万恶的空指针异常 NullPointerException 就是在运行时才出现的,编译时一点问题没有,运行的时候背刺。

image.png

我参考了好几篇文章,他们都用正射来理解反射,打不过就加入,那我也来。
我们需要 new 一个实例 TestService1,然后调用 test 方法。

正射的用法:

@Test
public void direct_new_test() {
    TestService1 testService1 = new TestService1();
    testService1.test();
}

反射的用法:

@Test
public void reflection_new_test() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
    Class<?> clazz = Class.forName("pri.hongweihao.smallspring.TestService1");

    // 获取构造方法,初始化实例
    Constructor<?>[] constructors = clazz.getConstructors();
    Object o = constructors[0].newInstance();

    // 获取方法对象并调用
    Method method = clazz.getMethod("test");
    method.invoke(o);
}

对比两段代码,正射的代码清晰易懂。就像我要从广州到北京,正射是直接打飞的去,反射是先坐公交车到香港,然后从澳门走路去上海。我猜有人肯定会问:反射那么复杂为什么还要用呢?这不是没苦硬吃吗?

官方也说了反射应该由我这种对语言基础知识由深刻理解的开发人员来使用。如果我把代码改成这样,阁下如何应对。把反射的部分独立出来一个方法,只需要传类名和方法名就能调用,你就说牛不牛皮?

@Test
public void reflection_new_test() throws Exception {
    reflection("pri.hongweihao.smallspring.TestService1", "test");
}

private void reflection(String className, String methodName) throws Exception {
    Class<?> clazz = Class.forName(className);

    // 获取构造方法,初始化实例
    Constructor<?>[] constructors = clazz.getConstructors();
    Object o = constructors[0].newInstance();

    // 获取方法对象并调用
    Method method = clazz.getMethod(methodName);
    method.invoke(o);
}

还有更牛皮的,如果我把类名和方法名写在配置文件里面,阁下还能应对吗?如果我的配置文件是这样的呢?

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="testDao" class="pri.hongweihao.smallspring.bean.TestDao"/>
    <bean id="testService" class="pri.hongweihao.smallspring.bean.TestService">
        <property name="name" value="karl"/>
        <property name="testDao" ref="testDao"/>
    </bean>
</beans>

这只是反射的一种用法,还有其他的骚操作等你来发掘,发掘了记得告诉我。

这就是运行时最基本的使用了,提前配置好一些参数,在程序运行的过程中加载进来,就能为所欲为了。

当年陈刀仔他能用20块赢到3700万,卢本伟用20万赢到500万不是问题!同理,当年陈刀仔他能用20块赢到3700万,我们用类名加方法名调用方法不是问题吧!埋伏他一手。

image.png

正片完,下面补充一点无关紧要的代码

获取Class对象:

/**
 * 获取Class对象:通过全限定名字符串获取Class对象
 */
@Test
public void get_class_test1() throws ClassNotFoundException {
    Class<?> clazz = Class.forName("pri.hongweihao.smallspring.TestService1");
    Assert.assertNotNull(clazz);
}

/**
 * 获取Class对象:通过类.class获取Class对象
 */
@Test
public void get_class_test2() {
    Class<TestService1> clazz = TestService1.class;
    Assert.assertNotNull(clazz);
}

/**
 * 获取Class对象:通过实例.getClass()获取Class对象
 */
@Test
public void get_class_test3() {
    TestService1 testService = new TestService1();
    Class<? extends TestService1> clazz = testService.getClass();
    Assert.assertNotNull(clazz);
}
/**
 * <p>
 * 获取类属性
 * </p>
 */
@Test
public void test() throws NoSuchFieldException {
    Class<TestService1> clazz = TestService1.class;

    System.out.println("公有属性:");
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }

    System.out.println("全部属性:");
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println(declaredField);
    }

    System.out.println("查询共有属性:");
    Field field = clazz.getField("field1");
    System.out.println(field);

    System.out.println("clazz.getField查询私有属性报错");
    Assert.assertThrows(NoSuchFieldException.class, () -> clazz.getField("field2"));

    System.out.println("查询全部属性:");
    Field declaredField1 = clazz.getDeclaredField("field1");
    System.out.println(declaredField1);

    Field declaredField2 = clazz.getDeclaredField("field2");
    System.out.println(declaredField2);


}
/**
 * 获取类方法
 */
@Test
public void test1() throws NoSuchMethodException {
    Class<TestService1> clazz = TestService1.class;

    System.out.println("当前类以及父类的所有公有方法: ");
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        System.out.println(method);
    }

    System.out.println("\n当前类的所有方法: ");
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println(method);
    }
}
/**
 * 初始化实例:通过 Class.newInstance() 调用无参构造方法初始化
 * TestService定义了一个无参构造方法
 */
@Test
public void create_instance1_test1() throws InstantiationException, IllegalAccessException {
    Class<TestService1> clazz = TestService1.class;
    TestService1 testService = clazz.newInstance();
    Assert.assertNotNull(testService);
    testService.test();
}

/**
 * 初始化实例:通过 Class.newInstance() 调用无参构造方法初始化
 * TestService1 没有定义构造方法
 */
@Test
public void create_instance1_test2() throws InstantiationException, IllegalAccessException {
    Class<TestService1> clazz = TestService1.class;
    TestService1 testService1 = clazz.newInstance();
    Assert.assertNotNull(testService1);
    testService1.test();
}

/**
 * 初始化实例:通过 Class.newInstance() 调用无参构造方法初始化
 * TestService2定义了有参构造方法
 */
@Test
public void create_instance1_test3() {
    Class<TestService2> clazz = TestService2.class;
    Assert.assertThrows(InstantiationException.class, clazz::newInstance);
}

/**
 * 初始化实例:通过 Class.newInstance() 调用无参构造方法初始化
 * TestService3定义了有参构造方法和无参构造方法
 */
@Test
public void create_instance1_test4() throws InstantiationException, IllegalAccessException {
    Class<TestService3> clazz = TestService3.class;
    TestService3 testService3 = clazz.newInstance();
    Assert.assertNotNull(testService3);
    testService3.test();
}

/**
 * 初始化实例:获取构造方法来初始化实例
 * TestService4定义了共有有参构造方法和私有无参构造方法
 */
@Test
public void create_instance2_test() throws Exception {
    Class<TestService4> clazz = TestService4.class;
    // 获取公有的构造方法
    Constructor<?>[] constructors = clazz.getConstructors();
    TestService4 testService4 = (TestService4) constructors[0].newInstance("fake");
    Assert.assertNotNull(testService4);
    testService4.test();

    // 获取所有的构造方法,调用私有构造方法会报错
    Constructor<TestService4> declaredConstructor = clazz.getDeclaredConstructor(Integer.class);
    Assert.assertThrows(IllegalAccessException.class, () -> declaredConstructor.newInstance(1));
}

image.png

Ref

Using Java Reflection (oracle.com)

大白话说Java反射:入门、使用、原理 - 陈树义 - 博客园 (cnblogs.com)

谈谈Java反射:从入门到实践,再到原理反射是Java底层框架的灵魂技术,学习反射非常有必要,本文将从入门概念,到实践, - 掘金 (juejin.cn)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

耳夹式耳机哪个品牌音质好?耳夹式蓝牙耳机排行榜!

当下&#xff0c;耳夹式蓝牙耳机愈发受到大众的青睐。不管是在上下班的途中、进行运动锻炼之时&#xff0c;还是休闲居家的日子里&#xff0c;这种耳机都能为使用者带来极为便捷的体验。不过&#xff0c;市面上的耳夹式蓝牙耳机品牌繁杂多样&#xff0c;产品品质也是良莠不齐&a…

【win10】VMware Workstation 16安装win10专业版及安装VMware Tools操作说明

参考链接 VMware虚拟机安装win10系统教程&#xff08;巨细&#xff09;_vmware安装win10-CSDN博客https://blog.csdn.net/gdidea/article/details/129523700 win10专业版安装说明 下载win10安装包 百度网盘 链接: https://pan.baidu.com/s/1kf4ORdXYgcqwAz2j86LSZw?pwdk4…

MySQL(八)——索引

文章目录 索引索引的数据结构索引的具体结构页的概念独立表空间文件页的结构页文件头和页文件尾页主体页目录数据页头 B树在索引中的应用索引分类按数据结构分按字段特性分按物理存储分按字段个数分 索引语法创建索引查看索引删除索引 索引优化SQL执行频率慢查询日志profile详情…

【机器学习导引】ch3-线性模型-2

优化理论&#xff1a;梯度下降&#xff08;Gradient Descent&#xff09; 梯度下降法的基本思路 梯度下降法是一种优化算法&#xff0c;目的是找到函数 f ( x ) f(x) f(x) 的最小值。图中提到“如果能找到一个序列 x 0 , x 1 , x 2 , … x_0, x_1, x_2, \dots x0​,x1​,x2​…

有手就会!低代码让你告别繁琐开发流程

近几年&#xff0c;随着低代码与无代码相关话题的火热&#xff0c;逻辑编排作为其重要构成部分也备受关注&#xff0c;集团内外不乏优秀的实践。之前在做技术调研时发现了不少业内逻辑编排相关的方案&#xff0c;陆续整理记录下来。今天先为大家带来低代码开发领域的 JNPF。 1.…

如何有效恢复受污染除砷树脂的功能?

在半导体制造领域&#xff0c;砷是一种常见的杂质元素。然而&#xff0c;砷的存在不仅会严重影响芯片的性能和可靠性&#xff0c;还会对生产环境和人体健康构成威胁。通过使用高效的除砷树脂&#xff0c;可以有效地去除水中的砷元素。 然而&#xff0c;一旦除砷树脂受到污染&am…

一个月学会Java 第10天 详讲static和面向对象——继承与Java世上最厉害的IDE——IDEA

Day10 详讲static和面向对象——继承与Java世上最厉害的IDE——IDEA 今天的篇幅会比较长&#xff0c;因为牵扯到不少的内容还有面向对象的内容 第一章 static 首先我们先来填一下坑&#xff0c;之前挖的&#xff0c;说后面要详细讲解static这个关键字的作用&#xff0c;那在这之…

二叉树的构建与遍历

在介绍二叉树的篇章中&#xff0c;我们用方法简单创建了一个二叉树&#xff0c;如下代码&#xff1a; public treeNode creattree(){treeNode Anew treeNode(A);treeNode Bnew treeNode(B);treeNode Cnew treeNode(C);treeNode Dnew treeNode(D);treeNode Enew treeNode(E);tr…

新大话西游图文架设教程

开始架设 1. 架设条件 新大话西游架设需要准备&#xff1a; linux 系统服务器&#xff0c;建议 CentOs 7.6或以上版本游戏源码&#xff0c;。 2. 安装宝塔面板 宝塔是一个服务器运维管理软件&#xff0c;安装命令&#xff1a; yum install -y wget && wget -O in…

zabbix7.0配置中文界面

Zabbix 是一个广泛使用的开源监控解决方案&#xff0c;支持多种语言界面。本文将详细介绍如何配置 Zabbix 以使用中文界面&#xff0c;从而提高用户体验和可读性。 1. 环境准备 在开始配置之前&#xff0c;请确保你已经安装并运行了 Zabbix 服务器、前端和数据库。如果你还没有…

成品婚恋交友相亲源码打包APP小程序H5公众号可打包小程序APP公众号H5源码交付支持二开

聊天交友系统开发专业语聊交友app开发搭建同城交友源码开发婚恋交友系统相亲app小程序H5婚恋交友小程序的定制开发与行业新机遇婚恋交友app红娘连线系统app小程序开发一站式开发系统源码&#xff1a;婚恋交友H5与小程序开发详解 电商运营技术服务源码系统php相亲交友平台APP婚恋…

前端优化之路:git commit 校验拦截

但是想要做到高效落地执行&#xff0c;就需要做些别的功课&#xff0c;先展示下成果图 需要了解git hooks&#xff0c;它是git的钩子&#xff0c;就像vue拥有自己的钩子一样。 官方文档&#xff1a;https://git-scm.com/docs/githooks 项目安装 husky&#xff0c;建议稳定版本…

Linux进程间通信(个人笔记)

Linux进程通信 1.进程通信介绍1.1进程间通信目的1.2进程间通信发展1.3进程间通信的具体分类 2.管道2.1匿名管道2.1.1代码实例2.1.2 fork共享管道原理2.1.3 管道的读写规则与特点2.1.4 进程池 2.2 命名管道2.2.1 命名管道的创建2.2.2匿名管道与命名管道的区别2.2.3代码实例 3.Sy…

Spring Boot洗衣店订单系统:数据驱动的决策

3系统分析 3.1可行性分析 通过对本洗衣店订单管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本洗衣店订单管理系统采用JAVA作为开发语言&#xff0c;S…

如何安装Llama3.1 —— 附一键安装包!

一键安装包文末领取&#xff01; 下载地址 软件&#xff1a;Llama版本&#xff1a;3.1语言&#xff1a;简体中文大小&#xff1a;645 MB安装环境&#xff1a;Win10及以上版本(64bit) 软件简介 ‌‌LLaMA模型是Meta研发的大语言模型‌&#xff0c;旨在帮助研究人员推进他们…

vue-jsonp的使用和腾讯地图当前经纬度和位置详情的获取

1.下载&#xff1a; npm install –save vue-jsonp2.main.js中引入&#xff1a; //腾讯逆地址解析会用到jsonp import {VueJsonp} from vue-jsonp; Vue.use(VueJsonp);3.腾讯地图中使用 uniapp中获取*经纬度*和通过经纬度获取当前**位置详情** //获取当前经纬度 getLocation…

【AI系统】AI在不同领域的应用与行业影响

本文将探讨AI在不同技术领域和行业中的广泛应用&#xff0c;以及这些应用如何影响和改变我们的世界。 I. 引言 AI技术正日益渗透到各个技术领域&#xff0c;从计算机视觉到自然语言处理&#xff0c;再到音频处理&#xff0c;AI的应用正变得越来越广泛。这些技术的发展不仅推动…

影刀RPA实战:制作Excel工资条

1.实战目标 使用Excel制作工资条是一种常见的做法&#xff0c;每个公司几乎都有这样的需求&#xff0c;我们先介绍下使用excel手动制作工资条的方法&#xff0c;看看不足之处&#xff0c;使用影刀RPA又会给我们带来怎样的便利&#xff0c;让我们更倾向于选择影刀来处理。 工资…

世昌股份与吉利亲密关系:资产负债率远高同行,应收账款周转率偏弱

《港湾商业观察》杨丹妮 9月23日&#xff0c;河北世昌汽车部件股份有限公司&#xff08;以下简称“世昌股份”&#xff09;回复了第一轮审核问询函&#xff0c;公司于今年六月递表北交所&#xff0c;保荐机构为东北证券。 近几年新能源汽车如日中天&#xff0c;世昌股份也因此…

系统端口号被占用问题处理(WindowsLinux系统)

Windows 直接kill占用端口的进程 WinR 输入cmd 打开命令行窗口 1.查询本地已被占用的端口号&#xff1a; 下面以8080端口为例&#xff1a; netstat -aon|findstr "8080" 查看本地8080端口进程的PID 2.杀死"xxxx"端口号的进程 (下面的22868是 你查到…