【JAVA】让 ChatGPT 来描述 IOC

前言

又迎来了一年一度的金三银四,虽然说今年的大环境不好,但是招聘还是在火热进行中。

面试过 Java 工程师的小伙伴都知道,Spring 中的 IOC 是面试高频题,面试官上来就问,知道什么是 IOC 吗,IOC 是如何初始化的,以及 Bean 的生命周期等相关内容。

那么接下来,让我们使用 ChatGPT 并结合自己的理解来讲述一下 IOC 的相关内容。

什么是 IOC?

什么是 IOC?ChatGPT 是这么说的:

Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心特性之一,它的基本思想是将对象的创建和对象间的关系由程序员手动管理转移到框架中自动管理。

在传统的开发模式中,程序员通过 new 关键字手动创建对象,并在程序中直接使用这些对象。而在 Spring IOC 中,程序员不需要手动创建对象,而是将对象的创建交给 Spring 容器进行管理。Spring 容器会根据配置文件中的规则自动创建对象并管理对象之间的关系,程序员只需要使用这些对象即可,从而将程序中的控制权反转到了框架中。

通过 Spring IOC,程序员可以更加专注于业务逻辑的实现,而不需要关注对象的创建和管理,从而提高了开发效率和程序的可维护性。

一个关于 IOC 的实际例子是一个家庭使用了自动化家居系统。这个系统可能会有不同的设备、传感器和控制器,例如智能灯泡、温度传感器、音乐播放器等。在传统的编程模式中,每个设备都需要直接连接到主控制器,并由主控制器直接管理所有设备的功能。但使用 IOC 模式后,每个设备可以被视为独立的组件,它们可以通过一个通用的接口来与其他组件交互。这样,当需要添加或删除设备时,只需对该设备进行必要的配置,而不需要修改整个系统的代码。

例如,当用户想要调整房间的温度时,他可以直接与智能温度传感器进行交互,传感器可以将数据发送到主控制器,然后主控制器可以向空调发送指令来改变室内温度。这里的关键在于,每个组件都被视为独立的实体,它们可以相互通信,而无需直接与其他组件或主控制器进行耦合。这种设计模式提高了系统的可维护性和可扩展性。

具体可以参考这篇 Spring IoC有什么好处呢?,博主提炼了一下:

控制反转(Inversion of Control)  就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection) ,这几种概念的关系大概如下:

什么是依赖倒置原则? 假设我们设计一辆汽车,结构如下所示:

这样的设计看起来没问题,但是可维护性却很低,在后面会进行解释。那现在换一种思路进行设计:

这就是依赖倒置原则 —— 把原本的高层建筑依赖底层建筑倒置过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的 “牵一发动全身” 的情况。

为了方便理解,先定义四个 Class,车,车身,底盘,轮胎。然后初始化这辆车,最后跑这辆车。代码结构如下:

这就是车子的第一个设计思路,上层建筑依赖下层建筑,即每一个类的构造函数都直接调用了底层代码的构造函数。这样就会出现很严重的问题,比如说我现在要把车的轮胎从30改成动态尺寸的,那么我们需要修改整个上层的所有构造函数!

这样的设计基本是不可维护的,因为在实际工程中,有些类会有几千个底层,如果要一一修改,所耗费的成本太大了。

那我们通过 IOC,对车子进行第二种思路的设计,重写车类的定义:

这次再把轮胎尺寸变成动态的,如果要让整个系统顺利运行,只需要做如下修改:

这样子就不需要修改上层的其他代码,因此 IOC 提高了系统的可维护性和可扩展性

IOC 的初始化过程

IOC 是如何进行初始化的?ChatGPT 是这样子描述的:

Spring IOC 容器的初始化过程主要分为以下六个步骤:

  1. 资源定位:Spring IOC 容器会根据配置文件的路径,找到配置文件并加载到内存中。一般是从 XML 文件中进行读取。
  2. 载入配置文件:Spring IOC 容器会读取配置文件中的 Bean 定义信息,并将其封装成对应的 BeanDefinition 对象。例如读取 property 元素并注入到 BeanDefinition 实例中。
  3. 实例化 Bean 对象:Spring IOC 容器根据 BeanDefinition 对象中的信息,使用 Java 反射机制实例化 Bean 对象,并将其放入 IOC 容器中。
  4. 依赖注入:Spring IOC 容器会根据 BeanDefinition 对象中的依赖关系,注入 Bean 之间的依赖关系。
  5. Bean 初始化:如果 Bean 实现了特定的接口,Spring IOC 容器会在 Bean 实例化之后调用这些接口中的方法进行初始化。
  6. 容器初始化完成:Spring IOC 容器初始化完成后,可以通过 getBean() 方法获取容器中的 Bean 对象。

TIPs:

单例 bean 的初始化以及依赖注入一般都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例 bean 是在应用第一次调用 getBean() 时进行初始化和依赖注入。

// AbstractApplicationContext 
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
复制代码

这里顺带提一下 Bean 的生命周期,如下图所示:

Bean 的生命周期一般包含以下阶段:

  1. 实例化:当程序调用某个类的构造方法时,将创建该类的实例。这个实例就是一个 Bean。
  2. 属性赋值:在实例化后,程序可以使用 set 方法或者直接为属性赋值,来设置 Bean 的属性值。
  3. 初始化:在属性赋值后,Spring 会通过回调函数(如 init-method)或者注解(如 @PostConstruct)来完成 Bean 的初始化操作,比如建立与其他 Bean 的关联关系等。
  4. 使用:在初始化完成后,Bean 就可以被应用程序使用了。
  5. 销毁:当应用程序不再需要 Bean 的时候,Spring 会调用回调函数(如 destroy-method)或者注解(如 @PreDestroy)来完成 Bean 的销毁操作,比如清理资源、断开数据库连接等。

TIPs:

需要注意的是,Spring 容器掌握着 Bean 的生命周期,所以所有的 Bean 操作都必须在 Spring 容器的管理下进行。同时要避免出现循环依赖,否则可能导致 Bean 的实例化失败。

手撕阉割版 IOC

先来一个简单的练练手,主打的就是一个凸显 IOC 特性:

public class IOCContainer {private Map<String, Object> beans; // 存储对象实例的 Mappublic IOCContainer() {beans = new HashMap<>();}// 注册 bean 到容器中public void registerBean(String name, Object bean) {beans.put(name, bean);}// 获取 bean 对象public Object getBean(String name) {return beans.get(name);}
}
复制代码

在上述代码中,将 Map 对象 beans 当成是 IOC 容器,用来存储注册进行来的 Bean,registerBean() 用来将 Bean 注册到容器当中,getBean() 方法则是从容器中获取已经注册的 Bean 对象。

那接下来写个测试用例,运行一下这个代码:

public class MyClass {public void sayHello() {System.out.println("Hello World! --sid10t.");}
}// 在 main 方法中使用 IOC 容器
public static void main(String[] args) {IOCContainer container = new IOCContainer();MyClass myClass = new MyClass();container.registerBean("myClass", myClass); // 将 MyClass 的实例注册到 IOC 容器中MyClass instance = (MyClass) container.getBean("myClass"); // 从 IOC 容器中获取 MyClass 的实例instance.sayHello(); // 调用 MyClass 实例的方法
}
复制代码

虽然这是一个非常简单的 IOC 容器实现的示例,但它清晰地表达了 IOC 的基本思想:通过将类的创建和依赖注入交给容器来管理,从而解耦应用程序中各个组件之间的依赖关系。在实际生产环境中,我们可能会需要更加完善的 IOC 容器实现,但这个示例可以作为一个基础来理解 IOC 容器的工作原理。


接下来,来点进阶挑战,通过 Java 反射机制,在原有的基础上,实现 Bean 的属性注入:

private Object createBean(Class<?> clazz, Map<String, Object> properties) throws Exception {try {// 构造函数实例化对象Constructor<?>[] constructors = clazz.getDeclaredConstructors();Constructor<?> constructor = null;for (Constructor<?> c : constructors) {if (c.getParameterCount() == 0) {constructor = c;break;}}if (constructor == null) {throw new RuntimeException("No default constructor found for bean: " + clazz);}Object bean = constructor.newInstance();// 注入依赖for (Field field : clazz.getDeclaredFields()) {if (properties.containsKey(field.getName())) {field.setAccessible(true);field.set(bean, properties.get(field.getName()));}}return bean;} catch (Exception e) {throw new RuntimeException("Failed to create bean: " + clazz, e);}
}
复制代码

通过 getDeclaredConstructors() 获取类的构造器,再通过 getParameterCount() == 0 来获取到默认构造器,即无参构造器,再使用 getDeclaredFields() 获取到相应的属性名,通过 set() 进行属性注入。

最后写一个测试用例运行一下代码:

public static void main(String[] args) throws Exception {MyIOCContainer container = new MyIOCContainer();// 注册 UserDTO 和 UserServiceMap<String, Object> userDTOProperties = new HashMap<>();userDTOProperties.put("url", "jdbc:mysql://localhost:3306/test");userDTOProperties.put("username", "root");userDTOProperties.put("password", "123456");container.registerBean("userDTO", UserDTO.class, userDTOProperties);Map<String, Object> userServiceProperties = new HashMap<>();userServiceProperties.put("userDTO", container.getBean("userDTO"));container.registerBean("userService", UserService.class, userServiceProperties);// 获取 UserService 并调用方法UserService userService = (UserService) container.getBean("userService");User user = new User("sid10t.", 18);userService.addUser(user);
}
复制代码

补充:

考虑到有些读者可能不知道怎么设计 User,UserDTO 和 UserService 类,就贴一下代码作为参考,其实是自己随意发挥就行,没有那么严谨;

User.java

public class User {private String name;private int age;public User() {}// Getter and Setter
}
复制代码

UserDTO.java

public class UserDTO {private String url;private String username;private String password;public UserDTO() {}// Getter and Setter
}
复制代码

UserService.java

public class UserService {private UserDTO userDTO;public UserService() {}public UserService(UserDTO userDTO) {this.userDTO = userDTO;}public void addUser(User user) {System.out.println("UserService.addUser: " + user.getName() + ", " + user.getAge());System.out.println("url: " + userDTO.getUrl() + ", username: " + userDTO.getUsername() + ", password: " + userDTO.getPassword());// 这里省略了其他方法调用}// Getter and Setter
}
复制代码

最后来点复杂的,在 Spring 框架中实现 Bean 的注入,作用域,初始化和销毁方法等功能,资源定位和载入配置文件这里就不赘述了:

public class IOCContainer {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();private Map<String, Object> singletonBeanMap = new HashMap<>();// 资源定位...// 载入配置文件...public void register(Class<?> beanClass, String beanName, Scope scope) {beanDefinitionMap.put(beanName, new BeanDefinition(beanClass, scope));}public <T> T getBean(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new RuntimeException("No such bean: " + beanName);}if (beanDefinition.getScope() == Scope.SINGLETON) {Object singletonBean = singletonBeanMap.get(beanName);if (singletonBean != null) {return (T) singletonBean;}}Object bean = createBean(beanDefinition);if (beanDefinition.getScope() == Scope.SINGLETON) {singletonBeanMap.put(beanName, bean);}return (T) bean;}private Object createBean(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();try {// 构造函数实例化对象Constructor<?>[] constructors = beanClass.getDeclaredConstructors();Constructor<?> constructor = null;for (Constructor<?> c : constructors) {if (c.getParameterCount() == 0) {constructor = c;break;}}if (constructor == null) {throw new RuntimeException("No default constructor found for bean: " + beanClass);}Object bean = constructor.newInstance();// 注入依赖Field[] fields = beanClass.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {String fieldName = field.getName();Object dependencyBean = getBean(fieldName);field.setAccessible(true);field.set(bean, dependencyBean);}}// 执行初始化方法Method[] methods = beanClass.getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(InitMethod.class)) {method.invoke(bean);}}return bean;} catch (Exception e) {throw new RuntimeException("Failed to create bean: " + beanClass, e);}}public void close() {for (Object singletonBean : singletonBeanMap.values()) {Class<?> beanClass = singletonBean.getClass();Method[] methods = beanClass.getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(DestroyMethod.class)) {try {method.invoke(singletonBean);} catch (Exception e) {throw new RuntimeException("Failed to invoke destroy method for bean: " + beanClass, e);}}}}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【工具】VScode|Linux 中怎么调试 Python 项目比较方便?又名 VScode 怎么调试 Python 项目(兼容环境Ubuntu18.04)

使用过 Anaconda、Jupyter、Pycharm、VScode、VS2022、pdb 这几个 IDE 去编写 python 项目或者维护 python 环境&#xff0c;各有各的优缺点&#xff0c;但 VScode yyds&#xff01; 可能会被网上说得天花乱坠的 Python 配置项吓退&#xff0c;会被 VScode 各种插件介绍吓退&a…

日本僧人问道弘法寺当家师

时间过得真快&#xff0c;六名日本禅僧在弘法寺为期10天的体验生活已进行到第三天&#xff0c;晚上10&#xff1a;00&#xff0c;方丈印顺大和尚依然等候在丈室&#xff0c;与前二日不同的是&#xff0c;弘法寺的当家师智空法师也来到了方丈室。 六位日本僧人行礼完…

佛教基础知识

佛教基础知识 1、《佛教常识》&#xff08;1&#xff09;佛陀和佛教的创立&#xff08;2&#xff09;佛法的基本内容、书籍&#xff08;3&#xff09;僧团和佛的弟子&#xff08;4&#xff09;佛教在印度的发展、衰亡及复兴&#xff08;5&#xff09;佛教在中国的发展、演变 2、…

【ChatGPT与网络安全攻击】AI密码破解器可在60秒内攻破50%以上普通密码

研究表明&#xff0c;ChatGPT等功能强大AI工具已经被用于网络攻击者实施犯罪活动&#xff0c;例如开发恶意软件和生成钓鱼邮件等。如果人们的密码从数据库泄露或被破坏&#xff0c;那么网络攻击者采用AI密码破解器猜出密码是概率几乎是100%&#xff0c;其中50%以上会在60秒内被…

chatgpt赋能Python-pythonmd5解密

Python MD5解密原理及应用 MD5是一种广泛使用的哈希算法&#xff0c;被用于加密敏感数据。MD5算法使用不可逆的方法将任何长度的数据转换为固定长度的哈希值&#xff0c;并且只能通过暴力破解的方式破解加密后的敏感数据。尽管MD5算法被广泛采用&#xff0c;但历史上已发现其存…

chatgpt赋能python:Python怎么破解Windows软件?

Python怎么破解Windows软件&#xff1f; 作为一名有10年Python编程经验的工程师&#xff0c;我想分享一些破解Windows软件的经验。Python是一种高级编程语言&#xff0c;可以用于许多不同的应用程序&#xff0c;包括软件破解。 什么是软件破解&#xff1f; 软件破解是指绕过…

讯飞星火认知大模型与ChatGPT的对比分析

引言&#xff1a; 人工智能是当今科技领域的热门话题&#xff0c;自然语言处理是人工智能的重要分支。自然语言处理的目标是让计算机能够理解和生成自然语言&#xff0c;实现人机交互和智能服务。近年来&#xff0c;随着深度学习的发展&#xff0c;自然语言处理领域出现了许多创…

【Unity开发小技巧】UnityWebGL移动端和电脑端调起输入法,中文输入处理

目录 一.TextMesh Pro中文显示问题 1.PC端和移动端中文显示异常乱码&#xff08;解决方案&#xff09; 1.制作TextMesh Pro字体 方式一 2.制作TextMesh Pro字体 方式二 3.通用字体资源 2.web端中文不能输入窗口模式&#xff08;解决方案&#xff09; 二.移动端Inputfile调…

分享一个利用ChatGPT为世界上任何城市建立旅行路线(带链接)的工具 GPTravel Advisor

GPTravel Advisor - 在几秒钟内创建世界上任何城市的旅行路线 网址链接&#xff1a;https://gpt-travel-advisor.vercel.app/ GIthub&#xff1a;https://github.com/dabit3/gpt-travel-advisor ChatGPT中文论坛&#xff1a;https://gptocean.com/

ChatGPT 新版 API 推出 語音轉換文字模型 Whisper

OpenAI 宣布釋出新 ChatGPT API&#xff0c;允許第三方開發人員通過 API 將 ChatGPT 整合到他們的網站、應用程式及產品中。同時發表開源的&#xff0c;讓用户用以轉錄或翻譯音訊。 OpenAI 表示&#xff0c;新版的 ChatGPT API 不僅可用於創建人工智能聊天界面&#xff0c;更可…

怎么玩chatgpt?如何利用ChatGPT来编写PRD?

很多人对于chatgpt不知道怎么玩&#xff1f;其实对于一个产品经理来说&#xff0c;他可以这样玩&#xff01;在产品开发过程中&#xff0c;产品需求文档&#xff08;PRD&#xff09;是一个非常重要的文档&#xff0c;它描述了产品的功能、特性和目标用户等信息。编写PRD需要耗费…

我群 300+人已熟练使用的 ChatGPT Prompt 技巧

这是吴恩达联合 OpenAI 官方录制的 ChatGPT Prompt 免费视频课&#xff0c;最后一个总结&#xff0c;视频&#xff1a; https://learn.deeplearning.ai/chatgpt-prompt-eng/lesson/2/guidelines 在这一节中&#xff0c;我会分享两个技巧&#xff1a;Transforming 和 Expanding …

利用python进行数据分析~基金分析

利用python进行基金数据分析 背景说明分析过程1.获取所有种类基金数据1.1导入相关包1.2通过天天基金网接口获取基金数据1.2.1获取网页信息1.2.2将数据转化成二维表并写入本地磁盘&#xff08;dataframe&#xff09; 1.3数据概览1.3.1查看前几行数据1.3.2查看各类型基金分布及可…

Tushare+Talib基金指标分析

本文介绍python语言下的两个第三方库&#xff0c;Tushare&#xff08;获取股票和基金数据&#xff09;和Ta-Lib&#xff08;用于数据指标分析&#xff09;&#xff0c;及其相关使用案例。 一、安装 Tushare安装 # 方式1&#xff1a;pip install tushare# 如果安装网络超时可尝…

chatgpt赋能python:Python实现人机对话

Python实现人机对话 人机对话是指人类和机器之间的交互过程&#xff0c;其中人类作为用户&#xff0c;通过语音或文字与机器进行交互。Python作为一门流行的编程语言&#xff0c;可以用来实现人机对话系统。本文介绍如何使用Python实现人机对话。 Python语言的优势 Python是…

爬取链家网房价数据

感觉最近做的东西好菜~~随便了。 import requests from lxml import etree import csvheaders {Referer: https://zs.fang.lianjia.com/loupan/nht1pg1/,User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.1…

python爬取全国房价并分析

使用方法程序原理 数据来源数据爬取 mac下mysql安装&使用&创建库和表网站爬取分析网站数据抓取代码基础 数据分析 我相信国内房价一直都是苦逼程序员比较关注的事情&#xff0c;我也非常关注全国部分地区的房价&#xff0c;因此我最原始的需求就是想看看全国都哪些地方…

爬虫 58同城房价信息 高德的接口进行房价地图绘制

爬虫部分 // An highlighted block def main():base_url1 https://hz.58.com/xinfang/loupan/all/pbase_url2 /?PGTID0d0091a8-0004-f055-d9b7-c447c3e1c187&ClickID1date_list []for i in range(1,34):html ask_url(base_url1str(i)base_url2)bs BeautifulSoup(htm…

安居客西安房源爬取 + pyecharts 数据展示

第一步是数据爬取&#xff1a; import xlwt from bs4 import BeautifulSoup from selenium import webdriver import time,random from selenium.webdriver import ChromeOptionsdef main():baseurl "https://xa.fang.anjuke.com/loupan/all/p"datalist getData(b…