ssm框架之spring:浅聊IOC
IOC
前面体验了spring,不过其运用了IOC,至于IOC( Inverse Of Controll—控制反转 )
看一下百度百科解释:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
从这个里面可以看出三个重要的词汇:控制,反转,依赖注入。
-
控制:既然提到控制,那么就是谁控制谁,也就行前提需要有两个对象,不然哪里来的控制。
一般创建对象的时候,通过new来创建一个对象。但是现在又有个问题了,IOC既然是控制,那么其如何生成对象呢?既然是解耦减少new来创建对象。前面对于例子初体验的时候,看出控制对象的时候没有通过new来创建对象。
-
反转: 既然叫做反转,那自然就有转了,不然就没有反转一说了。当然没有正转这个词汇,简单说就是一般用法就是正转。
比如一个对象控制另一个对象的时候,会在这个类中创建这个被调用的对象。
class A{} class B{public static void main(String[] args) {// 所谓的正转调用 因为需要调用A的方法所以需要创建一个A的对象A a=new A();} }
而反转却没有看见通过new来创建对象。详情看初体验的例子
可以看出没有通过new来创建对象,那么如何创建对象呢?肯定是Spring帮我们创建了对象。其通过配置文件就创建了对象,而这个帮忙创建对象的好人就是被称为IOC容器。而IOC容器帮我们查找以及注入依赖对象,而作为操作者的对象只能被动的接受依赖对象。 所以可以看出不是手动去创建对象,而根据配置文件Spring通过IOC容器进行依赖注入,然后对对象进行创建,销毁。所以是说控制对象生存周期的不再是引用它的对象,而是 Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。
-
依赖注入(DI):依赖注入和IOC两者其实可以说IOC是一种编程思维,而依赖注入是具体实现这个编程思维的方式。
其实最常用的两种注入方法是:set注入,构造注入,当然这个在spring中听过xml或者注解进行体现。
通过依赖注入机制 只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
当然组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
当然依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
其实在Spring中的IOC容器使用了工厂模式,以及反射,如果没有反射也就没有必要进行配置信息。
现在有两个问题了,Spring中是通过那个类进行处理这个配置信息的,毕竟配置信息可以是xml或者注解,而这个类必然要进行判断之后才能处理的。
创建bean容器
Spring的IOC容器就是IOC思想的一种实现,而在IOC容器(IOC容器存放着bean 所以被叫做 Spring bean容器)的创建,需要看一个接口BeanFactory,这个是创建Spring bean容器的根接口,这个不是我说的而是源码:
但是BeanFactory这个是Spring内部使用的接口,面向Spring本身,不是给开发人员用的。一般使用其子接口ApplicationContext,而这个接口在前面例子中很多体现,现在可以看下其关系:
常用的ApplicationContext实现类或接口:
类/接口 | 描述 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的XML格式配置文件创建的IOC容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径下XML格式配置文件创建的IOC容器对象 |
ConfigurableApplicationContext | ApplicationContext的子接口,包含了一些扩展方法比如close(),refresh等。 |
AnnotationConfigApplicationContext | 完全注解的时候,用来加载带有配置注解的类。 |
WebApplicationContext | 为web应用准备,是基于web环境开发创建IOC容器对象,并将对象存入ServletContext域中。 |
得到bean信息
其实这个需要一个接口:BeanDefinition (Definition的英文意思是解释,释义。 不得说母语英语真是友好,看名知其意,还是需要学英语的。)
然后看一下其源码是如果解释的:
翻译如下:
BeanDefinition 描述了一个实例信息,其拥有的属性只,构造方法中带有的参数以及具体实现去其它更多信息。
当然这个类加载信息,需要通过配置文件或者注解才可以,而这个配置文件或者注解也有一定的标准,不然呈现也不能读取这些配置的信息。具体源码就不再此篇聊了。
还有在spring中也不可能只有一个bean的信息,所以在spring中用一个BeanDefinitionMap进行保存信息。
可以用一个图来看一下这个IOC创建容器的大概过程:
图中还有缓存这个概念,毕竟生存的bean有的时候会被重复使用,如果调用某个bean的时候先判断是否被保存,如果有就直接调用,如果没有就在返回查看BeanDefinitionMap中需要的bean的配置信息。图中既然写了一级缓存那就是spring有多级缓存了,这个后面有机会再聊吧。
自己写一个依注释实现IOC注入的代码
代码的结构如下:
代码直接能用,可以复制在自己环境内就可以运行。而且每步带有注解。
-
首先实现两个接口:Di和Bean
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Bean {}@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Di { // 无法使用Object作为注解参数String value(); }
-
自己定义一个容器接口:MyApplicationContext
//创建一个自己Spring容器的接口 这个将BeanFactory和ApplicationContext融为一个方便写不然需要写父接口和子接口
public interface MyApplicationContext {Object getBean(Class clazz);
}
-
实现容器接口的类:MyAnnotionAoolicationContext
public class MyAnnotionAoolicationContext implements MyApplicationContext {private static Annotation beanAnnotation;// 用一个map存储bean的信息 模仿 BeanDefinitionMapprivate static Map<Class, Object> MyBeanDefinitionMap = new HashMap<Class, Object>();// 作为一个加载扫描其下包或者类的根目录private static String rootFile;@Overridepublic Object getBean(Class clazz) {return MyBeanDefinitionMap.get(clazz);}// 创建构造方法,加载配置文件或者带有自定义注解的 // 这个直接使用的是通过注解进行创建容器,不是通过xml配置文件进行配置public MyAnnotionAoolicationContext(String packagename) {try {// 因为传递过来包名路径是. 转换为路径符合String packageFile = packagename.replace(".", File.separator);// 得到绝对路径 因为会部署在不同的电脑上,目的是遍历其下的文件中是否都有注解URL url = Thread.currentThread().getContextClassLoader().getResource(packageFile);String fileString = URLDecoder.decode(url.getFile(), "utf-8");rootFile = fileString.substring(1, fileString.length() - packageFile.length());// System.out.println(fileString);loadFile(fileString);} catch (Exception e) {System.out.println(e);throw new RuntimeException(e);}loadDi();}// 遍历根路径下的文件中带有注解的文件private static void loadFile(String fileString) {try {File file = new File(fileString); // 首先判断是否是文件夹if (file.isDirectory()) {File[] childFileArr = file.listFiles();// 判断文件夹是否为空,如果为空就直接跳出即可if (childFileArr.length == 0 || childFileArr == null) {return;} else {// 遍历所有的文件判断是文件还是文件夹for (File childFile : childFileArr) {if (childFile.isFile()) { // System.out.println(childFile);// 通过路径得到反射所需要的包路径+类名// 这样得到的文件不是以.java 结束,而是以.classString childFileString = childFile.toString();String forName = childFileString.substring(rootFile.length(), childFileString.length() - ".class".length()).replace("\\",".");Class clazz= Class.forName(forName);// 自己写的注解一般作用在类上而不是接口上,所以将接口,和注解类排除if(!clazz.isAnnotation() && !clazz.isInterface()){ // 判断类上是否有bean注解,如果有就实例化 getAnnotation针对的是类上的注解 // 不过一般如果类的实例化上都没有注解,那么方法上带注解实现ioc 也就没有多少意义了Annotation beanAnnotation= clazz.getAnnotation(Bean.class);// Class s=Class.forName("com.xzd.myannotion.Bean"); // System.out.println(s.getFields());if(beanAnnotation!=null){ // 为了方便暂时使用空构造方法Object bean= clazz.newInstance();System.out.println(forName);System.out.println(clazz); // 因为一般针对的是接口,所以保存MyBeanDefinitionMap中如果有接口就以接口作为主键if(clazz.getInterfaces().length>0) { // 默认使用第一个接口吧MyBeanDefinitionMap.put(clazz.getInterfaces()[0], bean);}else {MyBeanDefinitionMap.put(clazz, bean);}}}} else {loadFile(String.valueOf(childFile));}}}}} catch ( Exception e) {System.out.println(e);throw new RuntimeException(e);}}// 前面的实例对象,还可以为属性进行注入值private void loadDi(){try {// 一般类上带有注解的才会在属性上带有ioc注入,所以就不便利所有的类,直接从MyBeanDefinitionMap获取即可Set<Map.Entry<Class, Object>> set= MyBeanDefinitionMap.entrySet();Iterator<Map.Entry<Class, Object>> iterator= set.iterator();while(iterator.hasNext()){Map.Entry<Class, Object> entry =iterator.next();Class clazz= entry.getKey();Object bean= entry.getValue(); // System.out.println(bean+"111");// 得到属性,从属性判断是否有注入数据Field[] fields= clazz.getDeclaredFields();System.out.println(fields.length);for(Field field:fields){Annotation annotation= field.getAnnotation(Di.class);if(annotation!=null){Class fieldClass= field.getType();fieldClass.getName();System.out.println(fieldClass.getName()+"111");String fieldName= field.getName();Object value= ((Di) annotation).value();Constructor fieldconstructor=fieldClass.getConstructor(String.class);field.setAccessible(true);field.set(bean, fieldconstructor.newInstance(value));};}}} catch (Exception e) {throw new RuntimeException(e);}}}
-
为了方便直接在一个bean上进行注解
@Bean public class Student {@Di("12")Integer age;@Di("王五")String name;@Overridepublic String toString() {return "Student{" +"age=" + age +", name='" + name + '\'' +'}';} }
-
测试方便调用:test
public class testSpring {public static void main(String[] args) {// 直接从目录上开始加载 MyApplicationContext myApplicationContext= new MyAnnotionAoolicationContext("com.xzd");Student student= (Student) myApplicationContext.getBean(Student.class);System.out.println(student);} }
相关文章:

ssm框架之spring:浅聊IOC
IOC 前面体验了spring,不过其运用了IOC,至于IOC( Inverse Of Controll—控制反转 ) 看一下百度百科解释: 控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则&#x…...

pytest初识
一、单元测试框架 (1)什么是单元测试框架? 单元测试是指在软件开发中,针对软件的最小单元(函数、方法)进行正确性的检查测试 (2)单元测试框架 java:junit和testng pytho…...
设计模式~责任链模式(Chain of Responsibility)-12
目录 (1)优点 (2)缺点 (3)使用场景 (4)注意事项: (5)应用实例: (6)经典案例 代码 责任链, …...

【ElasticSearch】(一)—— 初识ES
文章目录1. 了解ES1.1 elasticsearch的作用1.2 ELK技术栈1.3 elasticsearch和lucene1.4 为什么不是其他搜索技术?1.5 总结2. 倒排索引2.1 正向索引2.2 倒排索引2.3 正向和倒排3. ES的一些概念3.1 文档和字段3.2 索引和映射3.3 mysql与elasticsearch1. 了解ES Elasti…...

MySQL 事务隔离
MySQL 事务隔离事务隔离实现事务的启动ACID : 原子(Atomicity)、一致(Consistency)、隔离(Isolation)、永久(Durability) 多个事务可能出现问题 : 脏读 (dirty read) , 不可重复读 (non-repeatable read) , 幻读 (phantom read) 事务隔离级别 : 读未提交 (read uncommitted)…...
基础06-JS中for-in和for-of有什么区别
for…in 和 for…of 的区别 题目 for…in 和 for…of 的区别 key 和 value for…in 遍历 key , for…of 遍历 value const arr [10, 20, 30] for (let n of arr) {console.log(n) }const str abc for (let s of str) {console.log(s) }function fn() {for (let argument…...

AI视频智能分析EasyCVR视频融合平台录像计划模块搜索框细节优化
EasyCVR支持海量视频汇聚管理,可提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、智能分析等视频服务。在录像功能上,平台可支持: 根据业务场景自定义录像计划,可支持7*24H不间断录像,支持…...

TCP和UDP对比
TCP和UDP对比 UDP(用户数据报协议) 无连接(指的是逻辑连接关系,不是物理上的连接) 支持单播、多播以及广播,也就是UDP支持一对一、一对多、一对全 面向应用报文的,对应用层交付的报文直接打包 无连接不可靠的传输服务(适用于IP电话、视频会议等实时应用),不使用流量控制和…...

CVS Health 西维斯健康EDI需求
CVS Health西维斯健康在特拉华州成立,通过旗下的 CVS Pharmacy 和 Longs Drugs 零售店以及 CVS.com 电商提供处方药、美容产品、化妆品、电影和照片加工服务、季节性商品、贺卡和方便食品。CVS Health通过使高质量的护理变得更经济、更易获得、更简单、更无缝&#…...

Anaconda配置Python科学计算库SciPy的方法
本文介绍在Anaconda环境中,安装Python语言SciPy模块的方法。 SciPy是基于Python的科学计算库,用于解决科学、工程和技术计算中的各种问题。它建立在NumPy库的基础之上,提供了大量高效、易于使用的功能,包括统计分析、信号处理、优…...

数据库基本功之复杂查询的子查询
子查询返回的值可以被外部查询使用,这样的复合查询等效与执行两个连续的查询. 1. 单行单列子查询 (>,<,,<>,>,<)内部SELECT子句只返回一行结果 2.多行单列子查询 (all, any, in,not in) all (>大于最大的,<小于最小的) SQL> select ename, sal from…...

脑机接口科普0019——大脑的分区及功能
本文禁止转载!!!! 在前文脑机接口科普0018——前额叶切除手术_sgmcy的博客-CSDN博客科普中,有个这样的一张图: 这个图呢,把大脑划分为不同的区域,然后不同的区域代表不同的功能。 …...

阿里云服务器使用教程:CentOS 7 安装JDK及Tomcat(以jdk1.8、tomcat9.0.37为例)
目录 1、下载JDK及Tomcat的安装包并上传至服务器 2、安装JDK 3、安装Tomcat 4、Tomcat启动后无法打开Tomcat首页的原因 1、下载JDK及Tomcat的安装包并上传至服务器 (1)下载JDK1.8版本压缩包 官网:Java Downloads | Oracle (…...

Ubuntu20.04下安装vm17+win10/11
一、安装vmware17 1、官网下载 vmware官网:https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html 2、安装依赖 sudo apt update sudo apt install build-essential linux-headers-generic gcc make3、权限和安装 到下载的目录下…...

Kalman Filter in SLAM (1) ——Data Fusion and Kalman Filter(数据融合和卡尔曼滤波)
文章目录0. 参考资料1. Intro Example 例子引入1.1. 测量硬币直径1.2. 思考2. Data Fusion 数据融合2.1. 数据融合在做什么?2.2. 数据融合的前提——不确定度2.3. 数据融合的结果——统计意义下的最优估计3. State Space Representation 状态空间表达式3.1. 状态方程…...
黑马程序最后
这里写自定义目录标题内建stl常用算法adjacent_findbinary_searchcountcount if常用排序算法常用拷贝和替换replace常用算术生成算法常用集合算法https://gitee.com/jiangjiandong/Cpp-0-1-Resource/blob/master/%E7%AC%AC5%E9%98%B6%E6%AE%B5-C%E6%8F%90%E9%AB%98%E7%BC%96%E7…...

u盘系统文件删除后的五种恢复方法
U盘是我们日常生活中使用较为普遍的移动存储设备,由于其便携性和易用性广受人们的欢迎。然而,在我们使用U盘的过程中,经常会出现误删文件的情况,例如本来要作为启动盘的u盘,误删里面的系统文件怎么办?当U盘…...

【玩转c++】List讲解和模拟底层实现
本期主题:list的讲解和模拟实现博客主页:小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限,出现错误希望大家不吝赐1.list的介绍和使用1.1.list的介绍1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&…...
【Python】特征编码
特征编码1. 独热编码(离散变量编码) sklearn.preprocessing.OneHotEncoder1.1 原理 & 过程1.2 封装函数2. 连续变量分箱(连续变量编码) sklearn.preprocessing.KBinsDiscretizer2.1 原理2.2 等宽分箱 KBinsDiscretizer(strategyuniform)2.3 等频分箱 KBinsDiscretizer(stra…...

前端开发者必备的Nginx知识
nginx在应用程序中的作用 解决跨域请求过滤配置gzip负载均衡静态资源服务器…nginx是一个高性能的HTTP和反向代理服务器,也是一个通用的TCP/UDP代理服务器,最初由俄罗斯人Igor Sysoev编写。 nginx现在几乎是众多大型网站的必用技术,大多数情…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...