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

spring IOC(实现原理)

文章目录

  • 依赖注入
  • 控制反转
    • 相关
    • Spring 框架的 Bean管理的配置文件方式
    • 实例化Bean的三种方式
      • 无参构造器实例化
      • 静态工厂方法实例化
      • 实例工厂方法实例化
      • 静态和动态对比
    • 注解
      • 常用注解
      • 纯注解
  • 其它问题
    • 为什么p 命名空间方式需要无参构造

依赖注入

**依赖注入(DI)**是Spring框架核心功能之一,它允许对象间的依赖关系通过配置或代码在运行时动态注入,而不是通过对象内部硬编码。这种方法减少了代码间的耦合,增加了灵活性和可重用性。

依赖注入强调的是对象的依赖关系由外部来管理和提供,而不是由对象自身负责创建依赖对象。通过这种方式,可以降低对象之间的耦合度。即下面的方式也属于依赖注入。

常见的依赖注入方式

  • 构造函数注入:通过类的构造函数将依赖对象传递给该类。在对象实例化时,容器会调用相应的构造函数,并传入依赖对象的实例。
public class ProductService {private final ProductRepository productRepository;public ProductService(ProductRepository productRepository) {this.productRepository = productRepository;}// 业务方法
}
  • Setter 方法注入:利用类的 setter 方法将依赖对象设置到该类中。容器先创建对象实例,然后调用 setter 方法来注入依赖对象。
public class UserService {private UserRepository userRepository;public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}// 业务方法
}

xml依赖注入

  • <property>
  • <list><value></value></list>
  • <map><entry key=' ' value=' '></map>
  • <array><value></value></array>
  • <set><value></value></set>
  • <props><prop key=' '>值</prop></props>
---------------Student类------------------
public class Student {private String name;private Adress address;private String[] books;private List<String> hobbies;private Map<String,String> card;private Set<String> games;private String wife;private Properties info;//getter,setter,无参and全参构造}
---------------Teacher类------------------
public class Teacher {private String name;private int age;//getter,setter,无参and全参构造}
---------------applicationContext.xml------------------<bean id="adress" class="com.luxiya.Adress"><property name="address" value="空中花园"/></bean><!-- 需要相应的构造函数--><bean id="teacher" class="com.luxiya.Teacher" c:name="luxiya" c:age="18"/><!-- 需要相应的构造函数--><bean id="teacher1" class="com.luxiya.Teacher"><constructor-arg name="name" value="luxiya-constructor"/><constructor-arg name="age" value="188"/></bean><!-- 需要空参构造--><bean id="teacher2" class="com.luxiya.Teacher" p:name="zhihuiguan" p:age="19"></bean><bean id="student" class="com.luxiya.Student"><property name="name" value="luxiya"/><property name="address" ref="adress"/><property name="books"><array><value>java</value><value>python</value></array></property><property name="hobbies"><list><value>打篮球</value><value>打游戏</value></list></property><property name="card"><map><entry key="身份证" value="123456789"/><entry key="银行卡" value="123456789"/></map></property><property name="games"><set><value>LOL</value><value>DOTA</value></set></property><property name="wife"><null/></property><property name="info"><props><prop key="name">luxiya</prop><prop key="age">18</prop></props></property></bean>
---------------测试类------------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
Teacher teacher = (Teacher) context.getBean("teacher");
System.out.println(teacher.toString());
Teacher teacher1 = (Teacher) context.getBean("teacher1");
System.out.println(teacher1.toString());
Teacher teacher2 = (Teacher) context.getBean("teacher2");
System.out.println(teacher2.toString());

控制反转

​ 在传统的程序设计中,当一个对象(A)需要使用另一个对象(B)的功能时,通常会在对象 A 内部主动创建对象 B 的实例,即对象 A 对对象 B 的创建和生命周期管理具有控制权。而在控制反转的思想下,对象 A 所依赖的对象 B 的创建、实例化以及依赖关系的建立,不再由对象 A 自身负责,而是由一个外部的容器(比如 Spring 容器)来负责管理。这样一来,对象 A 获得依赖对象 B 的过程被反转了,控制权从对象 A 转移到了外部容器,这就是 “控制反转” 名称的由来。

​ IoC 容器负责创建对象、管理对象的生命周期,并在适当的时候将对象注入到需要它们的其他对象中。应用程序中的各个组件(对象)只需要关注自身的业务逻辑,而不需要关心依赖对象的创建和获取过程。容器会根据配置信息(如 XML 配置文件或注解)来确定对象之间的依赖关系,并自动完成对象的实例化和注入。

  • XML 配置:在 Spring 早期,XML 是配置 Bean 的主要方式。通过编写 XML 文件,**将 Bean 的定义信息(如 Bean 的类名、依赖关系、属性值等)与 Bean 的实现类分离开来。**这样做的好处是可以在不修改实现类代码的情况下,修改 Bean 的配置信息,便于对系统进行维护和扩展。但缺点是 XML 文件可能会变得冗长复杂,增加了配置的难度和维护成本。
  • 注解配置:随着 Spring 框架的发展,注解配置逐渐流行起来。使用注解(如 @Component@Service@Repository@Autowired 等)可以直接在实现类中定义 Bean 的信息和依赖关系,将 Bean 的定义和实现合二为一。这种方式减少了大量的 XML 配置,使得代码更加简洁,开发效率更高,实现了所谓的 “零配置”(或者说减少了显式配置)。

相关

ApplicationContext 接口,工厂的接口,使用该接口可以获取到具体的 Bean 对象。该接口下有两个具体的实现类。

ClassPathXmlApplicationContext,加载类路径下的 Spring 配置文件。(注:ClassPathXmlApplicationContext在读取application.xml文件时,就会自动创建bean实例)

FileSystemXmlApplicationContext,加载本地磁盘下的 Spring 配置文件。

Spring 框架的 Bean管理的配置文件方式

  1. id 属性

id 属性为每个 Bean 提供了一个唯一的标识符。

  1. class 属性

class 属性指定了 Bean 对象的全限定类名。在这个例子中,com.example.service.UserService 就是 UserService 类的完整路径,Spring 容器会根据这个路径来创建相应的 Bean 实例。

  1. scope 属性
  • singleton(单例):这是 scope 属性的默认值。当 scope 设置为 singleton 时,Spring 容器只会创建该 Bean 的一个实例,并且在整个应用程序的生命周期中都使用这个实例。
  • prototype(多例):当 scope 设置为 prototype 时,每次从 Spring 容器中获取该 Bean 时,容器都会创建一个新的实例。
  1. init - methoddestroy - method 属性
  • init - method:当 Bean 被加载到 Spring 容器时,会调用 init - method 属性指定的方法。
  • destroy - method:当 Bean 从 Spring 容器中移除时,会调用 destroy - method 属性指定的方法。
    <!-- 定义一个单例的 UserService Bean --><bean id="userServiceSingleton" class="com.example.service.UserService"scope="singleton"init-method="init"destroy-method="destroy"></bean><!-- 定义一个多例的 UserService Bean --><bean id="userServicePrototype" class="com.example.service.UserService"scope="prototype"></bean>
-----------------测试------------------
public class UserService {// 初始化方法public void init() {System.out.println("UserService 初始化");}// 销毁方法public void destroy() {System.out.println("UserService 销毁");}
}

实例化Bean的三种方式

无参构造器实例化

这是 Spring 中最基本的实例化 Bean 的方式,Spring 容器会调用 Bean 类的无参构造器(如果没有指定其他构造器)或者指定的有参构造器来创建 Bean 对象。

public class UserService {public UserService() {System.out.println("UserService实例化");}
}
-------------application.xml--------------
<bean id="userService" class="com.luxiya.demo.UserService"/>
-------------测试类--------------ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");

静态工厂方法实例化

使用一个静态工厂方法来创建 Bean 对象。静态工厂方法是类中的一个静态方法,该方法会返回一个对象实例。Spring 容器会调用这个静态工厂方法来获取 Bean 对象。

public class StaticFactory {public static UserService getUserService(){System.out.println("静态工厂创建");return new UserService();}
}
-------------application.xml--------------<!-- 静态工厂--><bean id="uSstatic" class="com.luxiya.demo.StaticFactory" factory-method="getUserService"/>
-------------测试类--------------ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("uSstatic");

实例工厂方法实例化

使用一个实例工厂的方法来创建 Bean 对象。首先需要创建一个工厂类的实例,然后调用该实例的方法来创建 Bean 对象。

-------------application.xml--------------
<!--动态工厂-->
<bean id="dFactory" class="com.luxiya.demo.DFactory"/>
<bean id="useDF" factory-bean="dFactory" factory-method="getUserService"/>
-------------测试类--------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("useDF");

静态和动态对比

  • 静态工厂:静态工厂方法相对固定,因为它是类的静态方法,不能根据不同的实例状态进行不同的处理。通常适用于创建一些通用的、不依赖于实例状态的对象。不依赖于工厂类的实例。
  • 动态工厂:动态工厂方法可以根据工厂类实例的不同状态来创建不同的 Bean 对象,具有更高的灵活性。例如,工厂类可以有不同的属性值,根据这些属性值在实例工厂方法中创建不同类型或配置的 Bean 对象。依赖于工厂类的实例。

注解

常用注解

@Component 普通的类

@Controller 表现层@Service 业务层

@Repository 持久层

依赖注入常用的注解

@Value 用于注入普通类型(String,int,double 等类型)

@Autowired 默认按类型进行自动装配(引用类型)

@Qualifier 和@Autowired 一起使用,强制使用名称注入

@Resource Java 提供的注解,也被支持。使用 name 属性,按名称注入

对象生命周期(作用范围)注解

@Scope 生命周期注解,取值 singleton(默认值,单实例)和 prototype(多

例)

初始化方法和销毁方法注解(了解)

@PostConstruct 相当于 init-method

@PreDestroy 相当于 destroy-method

@Service
public class TeacherService {public TeacherService() {System.out.println("TeacherService初始化");}
}-----------application.xml----------
<!--开启注解扫描-->
<context:component-scan base-package="com.luxiya.demo"/>
-----------------测试类--------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TeacherService teacherService = (TeacherService) context.getBean("teacherService");

纯注解

纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目的是替换掉所有的配置文件。但是需要编写配置类。

常用的注解总结

@Configuration 声明是配置类

@ComponentScan 扫描具体包结构的。也可以扫描多个包@ComponentScan(value ={“com.qcbyjy.demo4”,“com.qcbyjy.demo3”})

@Import 注解 Spring 的配置文件可以分成多个配置的,编写多个配置类。用于导入其他配置类

@Bean 注解 只能写在方法上,表明使用此方法创建一个对象,对象创建完成保存到 IOC 容器中

------------demo包下的配置类--------------
@Configuration
@ComponentScan(basePackages = "com.luxiya.demo")
@Import(SpringConfig2.class)
public class SpringCofig {@Bean(name="dataSource")public DataSource createDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring_db");dataSource.setUsername("root");dataSource.setPassword("root");return dataSource;}
}
-------------------基础测试--------------ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);Student student = (Student) context.getBean("student");
-------------demo2包下的配置类----------------
@Configuration
@ComponentScan(basePackages = "com.luxiya.demo2")
public class SpringConfig2 {
}
//demo2包下
@Data
@Component
public class Course {@Value("java")private String name;public Course(){System.out.println("Course初始化");}
}
//demo包下
@Component
@Data
public class Student {@Value("luxiya")private String name;public Student() {System.out.println("Student实例化");}
}
------------测试@Import注解-------------ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);Course course = (Course) context.getBean("course");------------测试@Bean注解-------------ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
//        Student student = (Student) context.getBean("student");
//        Course course = (Course) context.getBean("course");// 从应用上下文中获取 DataSource 对象DataSource dataSource = context.getBean("dataSource", DataSource.class);// 验证 DataSource 对象是否为 DruidDataSource 类型if (dataSource instanceof DruidDataSource) {DruidDataSource druidDataSource = (DruidDataSource) dataSource;System.out.println("Druid DataSource 配置信息:");System.out.println("Driver Class Name: " + druidDataSource.getDriverClassName());System.out.println("URL: " + druidDataSource.getUrl());System.out.println("Username: " + druidDataSource.getUsername());System.out.println("Password: " + druidDataSource.getPassword());}

其它问题

为什么p 命名空间方式需要无参构造

原因:在 Spring 中,当你使用属性注入(如你给出的 p 命名空间方式)时,Spring 容器在创建 Bean 实例的过程中,默认情况下会尝试使用无参构造函数来实例化对象,之后再通过 setter 方法进行属性注入。

Spring 容器在创建 Bean 实例时,主要有以下几个步骤:

  1. 实例化对象:Spring 需要先创建 Bean 对应的 Java 对象实例,一般情况下,它会优先尝试使用无参构造函数来创建对象。这是因为无参构造函数可以简单直接地创建对象,不需要额外的参数信息。
  2. 属性注入:在对象实例创建完成后,Spring 会根据配置(如 p 命名空间指定的属性值)调用对象的 setter 方法将属性值注入到对象中。

所以,当你使用 p 命名空间进行属性注入时,Spring 首先会尝试调用无参构造函数来创建 Teacher 对象,若类中没有无参构造函数,就会抛出 No default constructor found 这样的异常。

相关文章:

spring IOC(实现原理)

文章目录 依赖注入控制反转相关Spring 框架的 Bean管理的配置文件方式实例化Bean的三种方式无参构造器实例化静态工厂方法实例化实例工厂方法实例化静态和动态对比 注解常用注解纯注解 其它问题为什么p 命名空间方式需要无参构造 依赖注入 **依赖注入&#xff08;DI&#xff0…...

linux环保监测4G边缘网关:环境数据的可靠传输者

环保监测工控机&#xff0c;常被称为“环境数据采集器”或“环保数据终端”&#xff0c;是一种专门用于环境监测领域的工业计算机。它具备强大的数据处理能力、稳定的运行性能和多种接口&#xff0c;能够实时采集、处理和传输环境监测数据。这些数据包括空气质量、水质、噪声、…...

【哇! C++】类和对象(五) - 赋值运算符重载

目录 ​编辑 一、运算符重载 1.1 运算符重载概念 1.2 全局运算符重载 1.3 运算符重载为成员函数 二、赋值运算符重载的特性 2.1 赋值运算符重载需要注意的点 2.2 赋值运算符重载格式 2.2.1 传值返回 2.2.2 传引用返回 2.2.3 检查自己给自己赋值 三、赋值运算符重载的…...

基于单片机的风速报警装置设计

标题:基于单片机的风速报警装置设计 内容:1.摘要 本设计聚焦于基于单片机的风速报警装置&#xff0c;旨在解决传统风速监测缺乏实时报警功能的问题。采用单片机作为核心控制单元&#xff0c;结合风速传感器采集风速数据。经实验测试&#xff0c;该装置能准确测量 0 - 60m/s 范…...

Linux一键安装zsh终端美化插件

zsh应该是很多人第一个用的Linux终端美化软件 但是其安装略微复杂&#xff0c;让人有些困扰 所以我花了两天写了一键安装脚本&#xff0c;实测运行后直接安装好 适用于Ubuntu、Debian、Red Hat、macOS等系统 直接安装好zsh 以及常用插件 autojump 跳转插件 zsh-syntax-highlig…...

Docker部署Ragflow(完美解决502 bad gateway)

Docker快速启动Ragflow:Dev 系统准备 ubuntu server 24.04 CPU ≥ 4 cores (x86);RAM ≥ 16 GB;Disk ≥ 100 GB; 更新系统 sudo apt update 下载源码 git clone https://github.com/infiniflow/ragflow.git cd ragflow/docker # 切换稳定版本分支 git checkout -f v0.17.…...

C++ 算法竞赛STL以及常见模板

目录 STL /*═══════════════ Vector ═══════════════*/ /*════════════════ Pair ════════════════*/ /*══════════════ String ════════════════*/ /*══════════…...

前端数据模拟 Mock.js 学习笔记(附带详细)

前端数据模拟 Mock.js 学习笔记 在前端开发过程中&#xff0c;数据模拟是一项至关重要的环节。当后端接口尚未完成或者需要独立进行前端开发与测试时&#xff0c;Mock.js 能发挥巨大作用&#xff0c;它可以模拟各种数据场景&#xff0c;助力前端开发高效进行。 一、Mock.js 的…...

Web基础:HTML快速入门

HTML基础语法 HTML&#xff08;超文本标记语言&#xff09; 是用于创建网页内容的 标记语言&#xff0c;通过定义页面的 结构和内容 来告诉浏览器如何呈现网页。 超文本&#xff08;Hypertext&#xff09; 是一种通过 链接&#xff08;Hyperlinks&#xff09; 将不同文本、图像…...

学习一下Qt中的分裂器以及为什么要使用分裂器?

关于分裂器的使用和内容这篇文章写的很详细了[QT_043]Qt学习之分裂器&#xff08;QSplitter&#xff09;-CSDN博客 我就说一下为什么已经有布局器要还使用分裂器 在 Qt 中&#xff0c;布局时使用分裂器&#xff08;QSplitter&#xff09;主要有以下几个方面的好处&#xff…...

Xshell链接Linux机器更换jdk版本

都25年了&#xff0c;不会还有人在用 jdk1.8 吧? 那么问题来了&#xff0c;如果公司让你更新机器上的jdk版本&#xff0c;你又刚好是一个小白没操作过&#xff0c;怎么办&#xff1f;别急&#xff0c;步骤这就奉上~ 编辑 .bashrc 或 .zshrc 配置文件&#xff1a; 如果你使用的…...

2025.3.3总结

周一这天&#xff0c;我约了绩效教练&#xff0c;主要想了解专业类绩效的考核方式以及想知道如何拿到一个更好的绩效。其他的岗位并不是很清楚&#xff0c;但是专业类的岗位&#xff0c;目前采取绝对考核&#xff0c;管理层和专家岗采取相对考核&#xff0c;有末尾淘汰。 通过…...

如何应用大模型 — 大模型使用范式

从OpenAI发布ChatGPT开始&#xff0c;大模型就开始受到大家关注&#xff0c;到DeepSeek-R1出现&#xff0c;大家的关注达到了顶峰&#xff0c;越来越多的企业&#xff0c;机构&#xff0c;学校&#xff0c;政府部门希望接入大模型&#xff0c;希望通过大模型来提升效率&#xf…...

DeepSeek本机部署(基于Ollama和Docker管理)

目录 一、ollama 与 docker 简介 &#xff08;一&#xff09;ollama(Ollama) &#xff08;二&#xff09;docker 二、利用 ollama 和 docker 配置 deepseek-r1 的准备工作 &#xff08;一&#xff09;硬件需求 &#xff08;二&#xff09;软件安装 三、配置 deepseek-r1…...

C++复试笔记(一)

Setw 是C中用于设置输出字段宽度的函数。当使用 setw(3) 时&#xff0c;它会设置紧接着的输出字段的最小宽度为3个字符。如果字段内容长度小于3&#xff0c;则会在左侧填充空格以达到指定宽度&#xff1b;如果内容长度大于或等于3&#xff0c;则全部内容将被输出&#xff0c;…...

如何让一个类作为可调用对象被thread调用?

如何让一个类作为可调用对象&#xff0c;被 std::thread 调用 在 C 中&#xff0c;可以让一个类对象作为可调用对象&#xff08;Callable Object&#xff09;&#xff0c;然后用 std::thread 进行调用。要实现这一点&#xff0c;主要有三种方法&#xff1a; 重载 operator()&…...

学习小程序开发--Day1

项目学习开篇 项目架构 项目进程 创建uni-app项目 通过HBuilderX创建 小结 page.json 和 tabBar 目录文件 pages.json的配置...

“量子心灵AI“的监控仪表盘 - javascript网页设计案例

【前端实战】基于Three.js和Chart.js打造未来科技风AI监控仪表盘 本文通过AI辅助开发&#xff0c;详细记录了一个高级前端项目的完整实现过程。文章包含核心代码片段、技术要点及遇到的问题与解决方案。适合有一定前端基础的开发者学习参考。 1. 项目概述 本文详细介绍了一个名…...

Redis 中 string 和 list 的原理说明

Redis 中 string 和 list 的底层实现 Redis有5种基础数据结构&#xff0c;对应的value分别为&#xff1a;string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合) Redis 对象头结构体&#xff1a; struct RedisObject {int4 type; // 4bits 对象的基本类型…...

JVM常用概念之String.intern()

问题 String.intern()的工作原理&#xff1f;我们应该如何使用它? 基础知识 字符串池&#xff08;String Pool&#xff09; String类在我们日常编程工作中是使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销&#xff0c;避免字符串的重复创建&#xff0c;其维…...

DeepLabv3+改进6:在主干网络中添加SegNext_Attention|助力涨点

🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 目录 论文简介 步骤一 步骤二…...

亚信安全发布2024威胁年报和2025威胁预测

在当今数字化时代&#xff0c;网络空间已成为全球经济、社会和国家安全的核心基础设施。随着信息技术的飞速发展&#xff0c;网络连接了全球数十亿用户&#xff0c;推动了数字经济的蓬勃发展&#xff0c;同时也带来了前所未有的安全挑战。2024年&#xff0c;网络安全形势愈发复…...

深入理解 DOM 元素

深入理解 DOM 元素&#xff1a;构建动态网页的基石 在网页开发的世界里&#xff0c;DOM&#xff08;Document Object Model&#xff0c;文档对象模型&#xff09;元素宛如一座桥梁&#xff0c;连接着静态的 HTML 结构与动态的 JavaScript 交互逻辑。它让原本呆板的网页变得鲜活…...

[数据分享第七弹]全球洪水相关数据集

洪水是一种常见的自然灾害&#xff0c;在全球范围内造成了极为严重的威胁。近年来&#xff0c;针对洪水事件的检测分析&#xff0c;以及对于洪水灾害和灾后恢复能力的研究日渐增多&#xff0c;也产生了众多洪水数据集。今天&#xff0c;我们一起来收集整理一下相关数据集。&…...

SpringBoot POST和GET请求

1. 什么是 HTTP 请求&#xff1f; HTTP 协议&#xff1a;超文本传输协议&#xff0c;用于客户端和服务器之间的通信。 常见 HTTP 方法&#xff1a; GET&#xff1a;获取资源POST&#xff1a;提交数据PUT&#xff1a;更新资源DELETE&#xff1a;删除资源 2. GET 请求详解 作…...

MySQL 面试篇

MySQL相关面试题 定位慢查询 **面试官&#xff1a;**MySQL中&#xff0c;如何定位慢查询? 我们当时做压测的时候有的接口非常的慢&#xff0c;接口的响应时间超过了2秒以上&#xff0c;因为我们当时的系统部署了运维的监控系统Skywalking &#xff0c;在展示的报表中可以看到…...

【Andrej Karpathy 神经网络从Zero到Hero】--2.语言模型的两种实现方式 (Bigram 和 神经网络)

目录 统计 Bigram 语言模型质量评价方法 神经网络语言模型 【系列笔记】 【Andrej Karpathy 神经网络从Zero到Hero】–1. 自动微分autograd实践要点 本文主要参考 大神Andrej Karpathy 大模型讲座 | 构建makemore 系列之一&#xff1a;讲解语言建模的明确入门&#xff0c;演示…...

Android MVC、MVP、MVVM三种架构的介绍和使用。

写在前面&#xff1a;现在随便出去面试Android APP相关的工作&#xff0c;面试官基本上都会提问APP架构相关的问题&#xff0c;用Java、kotlin写APP的话&#xff0c;其实就三种架构MVC、MVP、MVVM&#xff0c;MVC和MVP高度相似&#xff0c;区别不大&#xff0c;MVVM则不同&…...

python使用django搭建图书管理系统

大家好,你们喜欢的梦幻编织者回来了 随着计算机网络和信息技术的不断发展&#xff0c;人类信息交流的方式从根本上发生了改变&#xff0c;计算机技术、信息化技术在各个领域都得到了广泛的应用。图书馆的规模和数量都在迅速增长&#xff0c;馆内藏书也越来越多&#xff0c;管理…...

JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件

JavaScript 事件系统是构建交互式 Web 应用的核心。本文从原生 DOM 事件到 React 的合成事件&#xff0c;内容涵盖&#xff1a; JavaScript 事件基础&#xff1a;事件类型、事件注册、事件对象事件传播机制&#xff1a;捕获、目标和冒泡阶段高级事件技术&#xff1a;事件委托、…...