当前位置: 首页 > news >正文

Spring之手写IoC

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
想系统/深入学习某技术知识点…
一个人摸索学习很难坚持,想组团高效学习…
想写博客但无从下手,急需写作干货注入能量…
热爱写作,愿意让自己成为更好的人…

文章目录

  • 前言
  • 一、回顾Java反射
  • 二、实现Spring的IoC
    • ①搭建子模块
    • ②准备测试需要的bean
    • ③定义注解
    • ④定义bean容器接口
    • ⑤编写注解bean容器接口实现
    • ⑥编写扫描bean逻辑
    • ⑦java类标识Bean注解
    • ⑧测试Bean加载
    • ⑨依赖注入
    • ⑩依赖注入实现
  • 总结


前言

我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。


一、回顾Java反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API**(1)java.lang.Class(2)java.lang.reflect**,所以,Class对象是反射的根源

自定义类

package com.atguigu.reflect;public class Car {//属性private String name;private int age;private String color;//无参数构造public Car() {}//有参数构造public Car(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}//普通方法private void run() {System.out.println("私有方法-run.....");}//get和set方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +'}';}
}

编写测试类

package com.atguigu.reflect;import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class TestCar {//1、获取Class对象多种方式@Testpublic void test01() throws Exception {//1 类名.classClass clazz1 = Car.class;//2 对象.getClass()Class clazz2 = new Car().getClass();//3 Class.forName("全路径")Class clazz3 = Class.forName("com.atguigu.reflect.Car");//实例化Car car = (Car)clazz3.getConstructor().newInstance();System.out.println(car);}//2、获取构造方法@Testpublic void test02() throws Exception {Class clazz = Car.class;//获取所有构造// getConstructors()获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();// getDeclaredConstructors()获取所有的构造方法public  privateConstructor[] constructors = clazz.getDeclaredConstructors();for (Constructor c:constructors) {System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());}//指定有参数构造创建对象//1 构造public
//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
//        Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
//        System.out.println(car1);//2 构造privateConstructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);c2.setAccessible(true);Car car2 = (Car)c2.newInstance("捷达", 15, "白色");System.out.println(car2);}//3、获取属性@Testpublic void test03() throws Exception {Class clazz = Car.class;Car car = (Car)clazz.getDeclaredConstructor().newInstance();//获取所有public属性//Field[] fields = clazz.getFields();//获取所有属性(包含私有属性)Field[] fields = clazz.getDeclaredFields();for (Field field:fields) {if(field.getName().equals("name")) {//设置允许访问field.setAccessible(true);field.set(car,"五菱宏光");System.out.println(car);}System.out.println(field.getName());}}//4、获取方法@Testpublic void test04() throws Exception {Car car = new Car("奔驰",10,"黑色");Class clazz = car.getClass();//1 public方法Method[] methods = clazz.getMethods();for (Method m1:methods) {//System.out.println(m1.getName());//执行方法 toStringif(m1.getName().equals("toString")) {String invoke = (String)m1.invoke(car);//System.out.println("toString执行了:"+invoke);}}//2 private方法Method[] methodsAll = clazz.getDeclaredMethods();for (Method m:methodsAll) {//执行方法 runif(m.getName().equals("run")) {m.setAccessible(true);m.invoke(car);}}}
}

二、实现Spring的IoC

我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。

①搭建子模块

搭建模块:guigu-spring,搭建方式如其他spring子模块

②准备测试需要的bean

添加依赖

<dependencies><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency>
</dependencies>

创建UserDao接口

package com.atguigu.spring6.test.dao;public interface UserDao {public void print();
}

创建UserDaoImpl实现

package com.atguigu.spring6.test.dao.impl;import com.atguigu.spring.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void print() {System.out.println("Dao层执行结束");}
}

创建UserService接口

package com.atguigu.spring6.test.service;public interface UserService {public void out();
}

创建UserServiceImpl实现类

package com.atguigu.spring.test.service.impl;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.service.UserService;@Bean
public class UserServiceImpl implements UserService {//    private UserDao userDao;@Overridepublic void out() {//userDao.print();System.out.println("Service层执行结束");}
}

③定义注解

我们通过注解的形式加载bean与实现依赖注入

bean注解

package com.atguigu.spring.core.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

依赖注入注解

package com.atguigu.spring.core.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

说明:上面两个注解可以随意取名

④定义bean容器接口

package com.atguigu.spring.core;public interface ApplicationContext {Object getBean(Class clazz);
}

⑤编写注解bean容器接口实现

AnnotationApplicationContext基于注解扫描bean

package com.atguigu.spring.core;import java.util.HashMap;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {}
}

⑥编写扫描bean逻辑

我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:

package com.atguigu.spring.core;import com.atguigu.spring.core.annotation.Bean;import java.io.File;
import java.util.HashMap;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}}private  void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);} else {//通过文件路径转变成全类名,第一步把绝对路径部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//选中class文件if (pathWithClass.contains(".class")) {//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的类实例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判断一下有没有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}}

⑦java类标识Bean注解

@Bean
public class UserServiceImpl implements UserService
@Bean
public class UserDaoImpl implements UserDao 

⑧测试Bean加载

package com.atguigu.spring;import com.atguigu.spring.core.AnnotationApplicationContext;
import com.atguigu.spring.core.ApplicationContext;
import com.atguigu.spring.test.service.UserService;
import org.junit.jupiter.api.Test;public class SpringIocTest {@Testpublic void testIoc() {ApplicationContext applicationContext = new AnnotationApplicationContext("com.atguigu.spring.test");UserService userService = (UserService)applicationContext.getBean(UserService.class);userService.out();System.out.println("run success");}
}

控制台打印测试

⑨依赖注入

只要userDao.print();调用成功,说明就注入成功

package com.atguigu.spring.test.service.impl;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;
import com.atguigu.spring.dao.UserDao;
import com.atguigu.spring.service.UserService;@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void out() {userDao.print();System.out.println("Service层执行结束");}
}

执行第八步:报错了,说明当前userDao是个空对象

⑩依赖注入实现

package com.atguigu.spring.core;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//依赖注入loadDi();}private  void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);} else {//通过文件路径转变成全类名,第一步把绝对路径部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//选中class文件if (pathWithClass.contains(".class")) {//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的类实例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判断一下有没有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}private void loadDi() {for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){//就是咱们放在容器的对象Object obj = entry.getValue();Class<?> aClass = obj.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields){Di annotation = field.getAnnotation(Di.class);if( annotation != null ){field.setAccessible(true);try {System.out.println("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}}}

执行第八步:执行成功,依赖注入成功


总结

以上就是Spring之容器:IOC(3)的相关知识点,希望对你有所帮助。
积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!

相关文章:

Spring之手写IoC

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…...

IDEA运行JSP启动后页面中文乱码

源代码截图&#xff1a; 运行结果截图&#xff1a; 在<head>标签内加入代码 <% page contentType"text/html; charsetgb2312"%> 重启服务器&#xff0c;问题已改善 ————————————————— 该文仅供学习以及参考&#xff0c;可做笔记收藏…...

Python 自动化之收发邮件(二)

发邮件之Windows进程监控 文章目录 发邮件之Windows进程监控前言一、基本内容二、基本结构三、库模块四、函数模块1.进程监控2.邮件发送 五、程序运行模块1.获取时间2.用户输入3.进程监控3.1进程启动发邮件3.2进程停止发邮件 总结 前言 上一篇简单写了一下如何进行邮件的收发操…...

RHEL8_Linux_Ansible常用模块的使用

本章主要介绍Ansible中最常见模块的使用 shell模块文件管理模块软件包管理模块服务管理模块磁盘管理模块用户管理模块防火墙管理模块 ansible的基本用法如下。 ansible 机器名 -m 模块x -a "模块的参数" 对被管理机器执行不同的操作&#xff0c;只需要调用不同的模块…...

2023 英特尔On技术创新大会直播 | AI 融合发展之旅

前言 2023 年的英特尔 On 技术创新大会中国站&#xff0c;主要聚焦最新一代增强 AI 能力的计算平台&#xff0c;深度讲解如何支持开放、多架构的软件方案&#xff0c;以赋能人工智能并推动其持续发展。 大会的目标之一是优化系统并赋能开发者&#xff0c;特别注重芯片增强技术…...

【JavaWeb】往浏览器打印一个hello world

上集:建一个web项目 第一步&#xff1a;建好Servlet类的文件 右键src&#xff0c;建一个class 就行 第二步&#xff1a;编代码 可以直接复制粘贴 用来测试的类 import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; //↓是注解&#xff0…...

技术探秘:在RISC Zero中验证FHE——由隐藏到证明:FHE验证的ZK路径(1)

1. 引言 开源代码实现见&#xff1a; https://github.com/hashcloak/fhe_risc0_zkvm&#xff08;Rust&#xff09;https://github.com/weikengchen/vfhe-profiled&#xff08;Rust&#xff09;https://github.com/l2iterative/vfhe0&#xff08;Rust&#xff09; L2IV Resea…...

Spring容器中scope为prototype类型Bean的回收机制

文章目录 一、背景二、AutowireCapableBeanFactory 方法 autowireBean 分析三、Spring 容器中 scope 为 prototype 类型 Bean 的回收机制四、总结 一、背景 最近做 DDD 实践时&#xff0c;遇到业务对象需要交给 Spring 管理才能做一些职责内事情。假设账号注册邮箱应用层代码流…...

Python生成器(python系列25)

前言&#xff1a;什么是生成器&#xff0c;他和迭代器的区别是什么&#xff1f;什么时生成器表达式&#xff0c;为什么和列表推导式那么像呢&#xff1f; 生成器&#xff1a; 定义&#xff1a;能够动态&#xff08;循环一次&#xff0c;计算一次&#xff0c;返回一次&#xf…...

Vue项目搭建过程

Vue项目搭建过程 1、安装NodeJs 1.1 下载安装包 在 http://nodejs.cn/download/ 上下载64位安装包&#xff0c;然后进行安装&#xff0c;和普通软件的安装一样。 C:\Users\Administrator>node -v v16.13.1C:\Users\Administrator>npm -v 8.5.51.2 安装cnpm # 安装cn…...

系统分析师(软考)知识点整理(一)

第一章 信息 信息是不确定性的减少 xi: n个状态中的第i个状态p(xi):出现第i个状态的概率b: b一般取值为2 特征 #mermaid-svg-pvPkY9RE5GZIIIxl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-pvPkY9RE5GZIIIxl…...

2021年数维杯国际大学生数学建模D题2021年电影市场票房波动模型分析求解全过程文档及程序

2021年数维杯国际大学生数学建模 D题 2021年电影市场票房波动模型分析 原题再现&#xff1a; 1、电影票房预测建模背景   随着人们文化消费需求的增加&#xff0c;电影院和银幕的数量不断增加&#xff0c;我国的电影产业不断呈现出繁荣景象。2019年&#xff0c;全国电影票房…...

Kubernetes 的用法和解析 -- 5

一.企业级镜像仓库Harbo 准备&#xff1a;另起一台新服务器&#xff0c;并配置docker yum源&#xff0c;安装docker 和 docker-compose 1.1 上传harbor安装包并安装 [rootharbor ~]# tar xf harbor-offline-installer-v2.5.3.tgz [rootharbor ~]# cp harbor.yml.tmpl harbor…...

HTML选择题试题——附答案

单选题 HTML的缩写是什么&#xff1f; A) Hyper Tool Markup LanguageB) Hyperlinks and Text Markup LanguageC) Hyper Text Markup LanguageD) Home Tool Markup Language 下列哪个标签用于定义文档的主体内容&#xff1f; A) <head>B) <body>C) <title>D)…...

html之CSS的高级选择器应用

文章目录 一、CSS高级选择器有哪些呢&#xff1f;二、高级选择器的应用1、层次选择器后代选择器子选择器相邻兄弟选择器通用兄弟选择器 2、结构伪类选择器&#xff08;不常用&#xff09;3、属性选择器E[attr]E[attrval]E[attr^val]E[attr$val]E[attr*val] 一、CSS高级选择器有…...

elementui+ <el-date-picker type=“datetime“/>时间组件的当前时间的180天之内的禁止选择处理

需求1如下&#xff1a;当前时间180天不可选择&#xff0c;180天之后可以选择&#xff0c;之前的时间都禁止选择 页面代码如下&#xff1a; <el-date-picker v-model"temp.expire_time" :picker-options"pickerOption" type"datetime" placeh…...

全网好听的BGM都在这里下载,赶紧收藏好了

无论是自媒体创作者还是从事视频剪辑工作的朋友&#xff0c;对于BGM的选择都很重要&#xff0c;一首适配的BGM能大大提升你作品的质量&#xff0c;还能让作品更优秀。哪里才能找到好听又免费的BGM&#xff1f;下面推荐几个我多年收藏的6个音效、音频素材网站&#xff0c;赶紧收…...

Spark编程实验一:Spark和Hadoop的安装使用

目录 一、目的与要求 二、实验内容 三、实验步骤 1、安装Hadoop和Spark 2、HDFS常用操作 3、Spark读取文件系统的数据 四、结果分析与实验体会 一、目的与要求 1、掌握在Linux虚拟机中安装Hadoop和Spark的方法&#xff1b; 2、熟悉HDFS的基本使用方法&#xff1b; 3、掌…...

代理和AOP

一:java代理 整体分为两种&#xff1a;静态代理和动态代理 静态代理&#xff1a;23种设计模式里面有个代理模式&#xff0c;那个就是静态代理。 动态代理&#xff1a;分为编译时增强(AspectJ)和运行时增强(JDK动态代理和CGLIB动态代理) 1:静态代理 这种代理在我们日常生活中其…...

Solidity-3-类型

Solidity 是一种静态类型语言&#xff0c;这意味着每个变量&#xff08;状态变量和局部变量&#xff09;都需要在编译时指定变量的类型。 “undefined”或“null”值的概念在Solidity中不存在&#xff0c;但是新声明的变量总是有一个 默认值 &#xff0c;具体的默认值跟类型相…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...