Spring实战 | Spring IOC不能说的秘密?

news2024/11/21 2:21:49

国庆中秋特辑系列文章:

国庆中秋特辑(八)Spring Boot项目如何使用JPA

国庆中秋特辑(七)Java软件工程师常见20道编程面试题

国庆中秋特辑(六)大学生常见30道宝藏编程面试题

国庆中秋特辑(五)MySQL如何性能调优?下篇

国庆中秋特辑(四)MySQL如何性能调优?上篇

国庆中秋特辑(三)使用生成对抗网络(GAN)生成具有节日氛围的画作,深度学习框架 TensorFlow 和 Keras 来实现

国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作

国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词

目录

  • 一、工作原理
  • 二、具体分析
  • 三、核心功能分析

Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心特性之一,它通过解耦和依赖注入的方式简化了应用的组件开发和维护。在 Spring 框架中,有两个主要的 IOC 容器实现:一个是基于 XML 配置文件的 BeanFactory,另一个是基于 Java 类的 ApplicationContext。

在这里插入图片描述

一、工作原理

这里我们以一个简单的案例来分析 Spring IOC 的工作原理。假设我们有一个简单的 Java 程序,需要用到一个数据持久层(DataAccess)和一个业务层(Service)。
首先,我们需要创建一个 Spring 配置文件(如:applicationContext.xml),在这个文件中,我们将 DataAccess 和 Service 作为 Bean 定义:

<bean id="dataAccess" class="com.example.DataAccessImpl"/>  
<bean id="service" class="com.example.ServiceImpl" property="dataAccess">  
   <property name="dataAccess" ref="dataAccess"/>  
</bean>  

在这个配置文件中,我们定义了两个 Bean:一个是 DataAccess 类型的 Bean,另一个是 Service 类型的 Bean。Service Bean 中的 dataAccess 属性通过 ref 属性指定为 DataAccess Bean。
接下来,我们需要在 Java 程序中创建一个 Spring 的 IOC 容器,并从中获取 DataAccess 和 Service Bean:

import com.example.DataAccess;  
import com.example.Service;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {  
   public static void main(String[] args) {  
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
       DataAccess dataAccess = (DataAccess) context.getBean("dataAccess");  
       Service service = (Service) context.getBean("service");
       // 使用 Service 进行业务操作  
       service.doSomething();  
   }  
}

在这个 Java 程序中,我们首先创建了一个 Spring 的 IOC 容器(ApplicationContext),然后通过 getBean 方法获取了 DataAccess 和 Service Bean。注意,由于 Service Bean 的 dataAccess 属性是通过 ref 属性指定为 DataAccess Bean,所以在获取 Service Bean 时,Spring 会自动将 DataAccess Bean 注入到 Service Bean 中。
现在,我们可以通过 Service 类的 doSomething 方法来调用 DataAccess 类的相关方法进行数据持久操作。这个过程就是 Spring IOC 的工作原理。Spring IOC 容器负责管理 Bean 的创建和管理,以及 Bean 之间的依赖关系,我们只需要关注业务逻辑的实现即可。
总结一下,Spring IOC 的核心思想是:不再由对象自己创建和管理它所依赖的对象,而是由外部(如 Spring 容器)负责注入依赖对象。这样可以大大简化对象的创建和管理,提高代码的可维护性和可扩展性。

二、具体分析

  1. 资源加载:Spring IOC 容器中的资源加载主要采用了模板设计模式和抽象工厂设计模式。在创建实例的托管和创建过程中,Spring 根据 XML 配置文件创建 Resource 对象,该对象中包含了 BeanDefinition 的信息。
// Resource 加载  
public Resource getResourceByPath(String path) {  
   if (path.startsWith("/")) {  
       path = path.substring(1);  
   }  
   Resource[] resources = getResources();  
   for (Resource resource : resources) {  
       if (resource.getPath().equals(path)) {  
           return resource;  
       }  
   }  
   return null;  
}
  1. BeanDefinition 的管理:Spring IOC 容器中的 BeanDefinition 是一个重要的组成部分,它用于描述 Bean 的定义,包括 Bean 的名称、类名、属性等信息。BeanDefinition 的管理主要通过 BeanDefinitionRegistry 和 BeanDefinitionReader 两个类实现。
// BeanDefinitionReader 读取 BeanDefinition  
public void readBeanDefinitions(Resource resource) throws BeansException, IOException {  
   if (!resource.exists()) {  
       return;  
   }  
   try (InputStream reader = resource.getInputStream()) {  
       while (reader.markSupported()) {  
           int marker = reader.mark();  
           if (marker == XMLBeanDefinitionReader.ROOT_ELEMENT_START_MARKER) {  
               // 创建 BeanDefinitionReader 对象  
               BeanDefinitionReader beanDefinitionReader = new BeanDefinitionReader(this);  
               // 开始解析 XML 文件中的 BeanDefinition  
               beanDefinitionReader.parse(reader);  
           } else if (marker == XMLBeanDefinitionReader.ROOT_ELEMENT_END_MARKER) {  
               // 解析结束  
               break;  
           }  
       }  
   }  
}
  1. Bean 的创建和管理:Spring IOC 容器中的 Bean 创建和管理主要通过 BeanFactory 类实现。BeanFactory 类包含了创建 Bean、获取 Bean、删除 Bean 等方法,它是 Spring IOC 容器的核心部分。
// BeanFactory 创建 Bean  
public Object getBean(String name) throws BeansException {  
   // 根据 Bean 名称获取 BeanDefinition  
   BeanDefinition beanDefinition = getBeanDefinition(name);  
   // 如果 BeanDefinition 不存在,抛出异常  
   if (beanDefinition == null) {  
       throw new NoSuchBeanDefinitionException(name);  
   }  
   // 创建 Bean  
   Object bean = createBean(name, beanDefinition);  
   // 返回 Bean  
   return bean;  
}
  1. 依赖注入:Spring IOC 容器中的依赖注入主要通过 DependencyAutowire 和 PropertyAutowire 两个类实现。DependencyAutowire 类用于自动注入依赖,而 PropertyAutowire 类用于自动注入属性。
// DependencyAutowire 注入依赖  
public void setDependency(String propertyName, @Qualifier @Nullable String[] qualifiedClassNames) {  
   // 获取 BeanDefinition  
   BeanDefinition beanDefinition = getBeanDefinition(propertyName);  
   // 如果 BeanDefinition 不存在,抛出异常  
   if (beanDefinition == null) {  
       throw new NoSuchBeanDefinitionException(propertyName);  
   }  
   // 获取依赖的 Bean  
   @Qualifier("'" + beanDefinition.getBeanClassName() + "'")  
   @Autowired  
   Object dependency = getBeanFactory().getBean(propertyName, Object.class);  
   // 设置依赖  
   setter.setValue(this, dependency);  
}
  1. 生命周期管理:Spring IOC 容器中的生命周期管理主要通过 Lifecycle 和 LifecycleCallback 两个接口实现。Lifecycle 接口定义了 Bean 的生命周期方法,如 start、stop 等;LifecycleCallback 接口则是一个回调接口,用于在 Bean 的生命周期方法执行前后执行特定的逻辑。
// Lifecycle 接口  
public interface Lifecycle {  
   void start() throws BeansException;  
   void stop() throws BeansException;  
   boolean isStarted();  
   boolean isStopped();  
   void destroy() throws BeansException;  
}

三、核心功能分析

由于 Spring IOC 核心代码较长,无法在这里全部展示。但我可以简要介绍一下 Spring IOC 的核心部分,并提供部分关键代码示例。

  1. BeanFactory 的创建:
    BeanFactory 是 Spring IOC 容器的基本实现,它负责管理 Bean 的创建和管理。创建过程主要包括以下几个步骤:
  • 读取 Spring 配置文件(如 applicationContext.xml),解析其中的 Bean 定义;
  • 创建一个 BeanDefinitionRegistry,用于存储解析后的 BeanDefinition;
  • 创建一个 BeanFactoryInstance,用于封装 BeanDefinitionRegistry 和其他 BeanFactory 的实例;
  • 调用 BeanFactoryInstance 的 getObjectForBeanName 方法,根据 BeanName 获取对应的 Bean 定义;
  • 通过 BeanDefinition 的 getBean 方法创建 Bean 实例;
  • 将 Bean 实例添加到 BeanFactory 的 singletonObjects 中,以 BeanName 为键。
    以下是创建 BeanFactory 的示例代码:
public class BeanFactoryImpl implements BeanFactory {  
   //... 省略其他代码...
   @Override  
   protected void createBeanFactory(String name, BeanDefinitionRegistry registry) throws BeansException {  
       // 创建并初始化一个 DefaultListableBeanFactory 实例  
       DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();  
       beanFactory.setBeanDefinitionRegistry(registry);  
       beanFactory.setResourceAccess(new ClassPathResource(name, getClassLoader()));  
       beanFactory.setEnvironment(this.environment);  
       beanFactory.setApplicationContextName(name);  
       beanFactory.setParentBeanFactory(this);  
       beanFactory.setSingletonObjects(new ConcurrentHashMap<String, Object>());  
       beanFactory.setDefaultSingletonBeanName(name);  
       beanFactory.registerBeanDefinition(new RootBeanDefinition(beanFactory, name));  
   }  
}
  1. Bean 的生命周期:
    Bean 的生命周期主要包括以下几个阶段:
  • 实例化:根据 BeanDefinition 创建 Bean 实例;
  • 属性填充:将 BeanDefinition 中的属性值设置到 Bean 实例上;
  • 初始化:调用 Bean 的初始化方法,进行相关设置;
  • 依赖注入:将 Bean 的依赖关系注入到 Bean 实例上;
  • 销毁:当 Bean 没有引用时,调用 Bean 的 destroy 方法进行销毁。
    以下是 Bean 生命周期的示例代码:
public class BeanFactoryImpl implements BeanFactory {  
   //... 省略其他代码...
   @Override  
   protected Object createBean(String name, BeanDefinition beanDefinition, Object[] args) throws BeansException {  
       // 根据 BeanDefinition 创建 Bean 实例  
       Object bean = beanDefinition.getBean();
       // 属性填充  
       for (PropertyValue propertyValue : beanDefinition.getPropertyValues()) {  
           bean = propertyValue.resolve(bean);  
       }
       // 初始化  
       bean = beanDefinition.initialize(bean);
       // 依赖注入  
       if (bean instanceof ConfigurableBeanFactory) {  
           ((ConfigurableBeanFactory) bean).mergeBeanDefinitions(beanDefinition.getResourceDescription());  
       }
       // 将 Bean 添加到 singletonObjects 中,以 BeanName 为键  
       synchronized (this.singletonObjects) {  
           this.singletonObjects.put(name, bean);  
       }
       return bean;  
   }  
}
  1. 依赖注入:
    Spring IOC 容器通过依赖注入(Dependency Injection,DI)的方式将 Bean 的依赖关系注入到 Bean 实例上。依赖注入主要有两种方式:构造器注入和 setter 方法注入。
    以下是依赖注入的示例代码:
public class BeanFactoryImpl implements BeanFactory {  
   //... 省略其他代码...
   @Override  
   protected Object createBean(String name, BeanDefinition beanDefinition, Object[] args) throws BeansException {  
       //... 省略其他代码...
       // 依赖注入  
       if (bean instanceof ConfigurableBeanFactory) {  
           ((ConfigurableBeanFactory) bean).mergeBeanDefinitions(beanDefinition.getResourceDescription());  
       }
       //... 省略其他代码...  
   }  
}

以上是 Spring IOC 核心代码的简要分析和示例。要了解更多关于 Spring IOC 的详细信息,建议参考 Spring 官方文档和相关教程。

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

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

相关文章

巧用h2-database.jar连接数据库

文章目录 一 、概述二、实践三、解决办法 一 、概述 H2 Database是一个开源的嵌入式数据库引擎&#xff0c;采用java语言编写&#xff0c;不受平台的限制&#xff0c;同时H2 Database提供了一个十分方便的web控制台用于操作和管理数据库内容。H2 Database还提供兼容模式&#…

hive数据表创建

目录 分隔符 分区表 二级分区 分桶表 外部表 分隔符 CREATE TABLE emp( userid bigint, emp_name array<string>, emp_date map<string,date>, other_info struct<deptname:string, gender:string>) ROW FORMAT DELIMITED FIELDS TERMINATED BY \t COL…

BN体系理解——类封装复现

from pathlib import Path from typing import Optionalimport torch import torch.nn as nn from torch import Tensorclass BN(nn.Module):def __init__(self,num_features,momentum0.1,eps1e-8):##num_features是通道数"""初始化方法:param num_features:特征…

工业读写器如何选型?

随着工业自动化的迅速发展&#xff0c;库存管理、生产流程、质量管理等传统工作人工工作也逐渐由各种智能设备来替代管理。RFID技术作为非接触式数据传输的通信方式&#xff0c;也常常应用在工业场合之中。具体工业RFID读写器如何选型&#xff0c;有哪些选择要点呢?ANDEAWELL国…

复杂环境下人形机器人自动导航项目复盘

百度飞浆企业赛&#xff1a; 乐聚机器人提供动作工程文件&#xff0c;aelos软件中可修改动作的每个舵机的数值。 Aelos Smart零点设置 MobXterm上通过ssh远程连接树莓派。 1、上下开横杆 横杆一开始是关闭的&#xff0c;横杆打开的那个瞬间&#xff0c;机器人以最快速度通过。…

spring:详解控制反转IOC

文章目录 IOC工厂模式实例基于xml管理Bean基于注解管理Bean常用注解&#xff1a;用于创建对象的常用注解&#xff1a;用于注入数据的常用注解&#xff1a;和生命周期相关的(了解)常用注解&#xff1a;新注解 IOC IOC (Inversion of Control)是Spring的核心概念之一。它是指控制…

智能电表消磁复位怎么回事

随着科技的发展的不断进步&#xff0c;智能电表已经成了家家户户用电管理的左膀右臂。相对于传统电表&#xff0c;智能电表具备远程控制读值、实时监控系统、自动控制系统等服务&#xff0c;不但能有效提高电气安全&#xff0c;还有助于客户节约能源。但是&#xff0c;智能电表…

从零开始探索C语言(十一)----共用体和位域

文章目录 1. 共用体1.1 定义共用体1.2 访问共用体成员 2. 位域2.1 位域声明2.2 位域的定义和位域变量的说明2.3 位域的使用2.4 位域小结 1. 共用体 共用体是一种特殊的数据类型&#xff0c;允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体&#…

Vuex基础使用存取值+异步请求后台

目录 一、Vuex简介 1.1 定义 1.2 Vuex关键概念 1.3 使用Vuex的优势 1.4 Vuex中各个js文件的用途 1.5 Vuex各组件 1.5.1 图解 1.5.2 详解 1.6 变量传值的演变形式 二、Vuex获取值 2.1 安装 2.2 菜单栏 2.3 模块 2.4 引用 三、Vuex改变值 四、Vuex异步&请求后台…

Jmeter性能测试之生成测试报告详解

结构 测试计划 测试计划是顶级的层级⽬录的结构&#xff0c; 那么在这样的⽬录结构中&#xff0c;⾥⾯可以包含很多线程组 线程组 线程组我们可以简单的理解为postman测试⼯具⾥⾯的collection&#xff0c;那么在整体线程组⾥⾯&#xff0c;可以添加很多的测试 ⽤例 简单控…

北京股票开户的佣金手续费是多少?北京股票开户选择哪家券商?

北京股票开户的佣金手续费是多少?北京股票开户选择哪家券商? 股票注册开户是非常简单的&#xff0c;在2015年前也就是互联网还不发达的时候&#xff0c;投资者只能去券商的营业部柜台办理&#xff0c;而自从各大券商都可以网上开户后&#xff0c;更多的投资者会选择网上开户…

红队专题-从零开始VC++远程控制软件RAT-C/S-[1]远控介绍及界面编写

红队专题 招募六边形战士队员[1]---远控介绍及界面编写1.远程控制软件演示及教程简要说明主程序可执行程序 服务端生成器主机上线服务端程序 和 服务文件管理CMD进程服务自启动主程序主对话框操作菜单列表框配置信息 多线程操作非模式对话框 2.环境&#xff1a;3.界面编程新建项…

2023年中国滑雪设备行业分析:随着滑雪运动人数增加,产品需求不断提升[图]

滑雪设备行业是指生产各种滑雪设备的行业。这些设备包括滑雪板、雪杖、雪鞋、滑雪杆、滑雪头盔、滑雪镜等。这些设备广泛应用于滑雪运动和相关活动&#xff0c;为滑雪者提供安全、舒适和高效的体验。 滑雪设备行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&am…

ITSS云能力评估是什么?

一、ITSS云服务能力评估是什么为了提升云计算产品&#xff08;系统&#xff09;的服务能力&#xff0c;保证云计算服务质量&#xff0c;以GB/T 36326-2018《信息技术云计算云服务运营通用要求》等系列国家标准为依托&#xff0c;由中国电子工业标准化技术协会信息技术分会&…

HashMap(2)正文源码分析

序、慢慢来才是最快的方法。 1.简介 HashMap的底层结构是基于分离链表发解决散列冲突的动态散列表。 在Java7中使用数组链表&#xff0c;发生散列冲突的键值对会使用头插法添加到单链表中&#xff1b;在Java8中使用数组链表红黑树&#xff0c;发生散列冲突的键值对会用尾插发…

面试算法23:两个链表的第1个重合节点

题目 输入两个单向链表&#xff0c;请问如何找出它们的第1个重合节点。例如&#xff0c;图4.5中的两个链表的第1个重合节点的值是4。 分析 首先遍历两个链表得到它们的长度&#xff0c;这样就能知道哪个链表比较长&#xff0c;以及长的链表比短的链表多几个节点。在第2次遍…

Java基础面试-JDK JRE JVM

详细解释 JDK&#xff08;Java Devalpment Kit&#xff09;java 开发工具 JDK是Java开发工具包&#xff0c;它是Java开发者用于编写、编译、调试和运行Java程序的核心组件。JDK包含了Java编程语言的开发工具和工具集&#xff0c;以及Java标准库和其他一些必要的文件。JDK中的…

QTableWidget 表格部件

QTableWidget是QT中的表格组件类。一般用来展示多行多列的数据&#xff0c;是QT中使用较多的控件之一。1、QTableWidgetItem对象 QTableWidget中的每一个单元格都是一个QTableWidgetItem对象&#xff0c;因此先介绍下QTableWidgetItem的常用方法。 1.1、设置文本内容 void QT…

陪诊系统|陪诊系统开发|陪诊小程序开发指南

随着移动互联网的快速发展&#xff0c;陪诊小程序的出现为医疗服务行业带来了全新的便捷体验。无需排队、无需等待&#xff0c;只需轻轻一点&#xff0c;陪诊小程序即可为患者提供全方位的陪诊服务。本文将为您介绍陪诊小程序的开发流程和其功能特点&#xff0c;帮助您了解并投…

java模拟GPT流式问答

流式请求gpt并且流式推送相关前端页面 1&#xff09;java流式获取gpt答案 1、读取文件流的方式 使用post请求数据&#xff0c;由于gpt是eventsource的方式返回数据&#xff0c;所以格式是data&#xff1a;&#xff0c;需要手动替换一下值 /** org.apache.http.client.metho…