spring(三):IOC介绍及代码实现

1. IoC容器概述

IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。

IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。

2. 控制反转(IoC)

控制反转是一种思想,可以降低程序耦合度,提高程序扩展力。

控制反转, 就是将对象的创建以及对象和对象之间关系的维护(也就是对象中属性的赋值)交给第三方容器负责。

也就是说,我们可以不需要手动创建Bean对象以及手动对Bean对象中的属性赋值,这些操作都可以由IoC容器完成。

控制反转这种思想是根据依赖注入(Dependency Injection,DI)实现的。

3. 依赖注入

指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

依赖注入常见的实现方式包括两种:

  • 第一种:set注入
  • 第二种:构造注入

IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

4. 代码实现

该代码只是粗略实现,目的是为了更好的展示IoC及依赖注入的实现放视。其中,Bean注解表示将该类交由IoC容器管理(spring中的@component),Di注解表示对该属性进行依赖注入(spring中的@Autowired)。

实现思路:

  • 根据用户输入的包名,首先获取到这个包的所在路径。
  • 之后,对这个路径下的所有文件及文件夹进行扫描,寻找添加了Bean注解的的类。
  • 找到满足条件的类后创建该类的实例对象,并将这个类的实例对象以及其类型放入到map当中。其中,key是类型,value是实例对象。这样。我们就可以根据类型获取到相对应的实例对象。
  • 将所有加了Bean注解的类都放入map之后,对map中的所有对象进行遍历。根据反射获取其所有属性,判断是否有属性添加了注解Di。
  • 如果存在添加了注解Di的属性,我们就要根据其属性的类型在map中得到相对应类型的实例对象,并调用set函数将该对象赋值给该属性,完成依赖注入操作。

实现代码如下:

public class AnnotationApplicationContext implements ApplicationContext{//存放bean对象private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;//当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化public AnnotationApplicationContext(String basePackage) throws Exception {// 把.替换成\\String packageDir = basePackage.replaceAll("\\.", "\\\\");//得到绝对路径Enumeration<URL> absolutePath = Thread.currentThread().getContextClassLoader().getResources(packageDir);while (absolutePath.hasMoreElements()) {URL url = absolutePath.nextElement();//斜杠'/'可能被编码,因此需要解码String filePath = URLDecoder.decode(url.getFile(),"utf-8");//获得包前面的路劲,以便后续操作rootPath = filePath.substring(0, filePath.length()-packageDir.length());//包扫描scanBean(new File(filePath));//属性注入loadDi();}}private void loadDi() throws Exception {Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();//遍历map集合中的每个对象for(Map.Entry<Class, Object> entry : entries) {Object obj = entry.getValue();//得到每个对象的classClass<?> clazz = obj.getClass();//获得每个对象的所有属性Field[] declaredFields = clazz.getDeclaredFields();for(Field field : declaredFields) {//如果存在属性含有注解Diif(field.getAnnotation(Di.class) != null) {//如果时私有属性,设置可访问field.setAccessible(true);//为属性赋值,按类型获取对应的对象field.set(obj, beanFactory.get(field.getType()));}}}}private void scanBean(File file) throws Exception {// 如果当前是文件夹if(file.isDirectory()) {//获得所有子文件(夹)File[] childFiles = file.listFiles();//如果当前文件夹空if(childFiles == null || childFiles.length == 0) {return;}for(File childFile : childFiles) {//如果当前子文件时文件夹,递归if(childFile.isDirectory()) {scanBean(childFile);}else {//得到包路径+类名称String classPath = childFile.getAbsolutePath().substring(rootPath.length() - 1);//如果当前文件类型时classif(classPath.endsWith(".class")) {//把路径\替换成.  把.class去掉String allName = classPath.replaceAll("\\\\", ".").replace(".class", "");//获取类的classClass clazz = Class.forName(allName);//如果不是接口而且上面有注解Beanif(!clazz.isInterface() && clazz.getAnnotation(Bean.class) != null) {// 实例化对象Object instance = clazz.getConstructor().newInstance();// 如果当前类实现了接口if(clazz.getInterfaces().length > 0) {//把接口的类型作为该实例的类别beanFactory.put(clazz.getInterfaces()[0], instance);}else {//没有接口,就把该类的类型作为keybeanFactory.put(clazz, instance);}}}}}}}@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}}

使用代码如下:

   @Testpublic void testIoc() throws Exception {ApplicationContext context = new AnnotationApplicationContext("org.kkk.spring6");UserService userService = (UserService) context.getBean(UserService.class);userService.print();}

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

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

相关文章

【第四天】蓝桥杯备战

题 1、求和2、天数3、最大缝隙 1、求和 https://www.lanqiao.cn/problems/1442/learning/ 解法&#xff1a;字符串方法的应用 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scann…

Linux命令-apk命令(Alpine Linux 下的包管理工具)

使用实例 apk install xxx apk search xxx # 支持正则 apk info xxx # 查看包的详细信息 apk show # list local package # 卸载并删除 包 apk del openssh openntp vim升级 upgrade命令升级系统已安装的所以软件包&#xff08;一般包括内核&#xff09;&#xff0c;当然也可…

c++ vector 赋值 只能用push_back()赋值 ?使用下标赋值出错,不能使用标赋值吗?

文章目录 1 push_back()赋值2 下标赋值2.1 下标赋值出错2.2 vector 真的 不能使用标赋值吗&#xff1f;2.2.1 只能使用push_back() 赋值情况2.2.2 push_back() 和 下标赋值 都可以2.2.3 先push_back()赋值&#xff0c;再下标赋值 2.3 push_back() 和 下标赋值 两种特点对比测试…

谷粒商城【成神路】-【1】——项目搭建

目录 &#x1f95e;1.整体架构图 &#x1f355;2.微服务划分图 &#x1f354;3.开发环境 &#x1f354;4.搭建git &#x1f32d;5.快速搭建服务 &#x1f37f;6.数据库搭建 &#x1f9c2;7.获取脚手架 &#x1f953;8.代码生成器 &#x1f373;9.创建公共模块 …

论文笔记(四十二)Diff-DOPE: Differentiable Deep Object Pose Estimation

Diff-DOPE: Differentiable Deep Object Pose Estimation 文章概括摘要I. 介绍II. 相关工作III. DIFF-DOPEIV. 实验结果A. 实施细节和性能B. 准确性C. 机器人-摄像机校准 V. 结论VI. 致谢 文章概括 作者&#xff1a;Jonathan Tremblay, Bowen Wen, Valts Blukis, Balakumar Su…

命令提示符

echo $PS1命令显示当前命令提示符的格式。 [rootlocalhost ~]# echo $PS1 [\u\h \W]\$参数含义 \u 代表是用户 user 分割符号\h 代表主机名 hostname\W 代表当前的工作目录&#xff08;当前的路径&#xff09; working\$ 如果你是root用户就是# &#xff0c;如果你是非root用户…

品牌突围|内容营销「共创公式」全面讲解

为什么品牌要扎根小红书&#xff1f;除了种草投放&#xff0c;品牌还能做些什么&#xff1f; 在小红书&#xff0c;迎接消费者共创的时代&#xff0c;激活品牌营销的无限潜能。 拥抱多元 在新机遇中预见未来 2023年&#xff0c;各大社交媒体平台涌现出了许多热点&#xff0c…

项目中日历管理学习使用

一些项目中会有日历或日期设置&#xff0c;最基本的会显示工作日&#xff0c;休息日&#xff0c;节假日等等&#xff0c;下面就是基于项目中的日历管理功能&#xff0c;要显示工作日&#xff0c;休息日&#xff0c;节假日 效果图 获取国家法定节假日工具类 public class Holi…

vue3中下载预览文件方法封装(已获取文件地址)

1.封装方法 在src/utils文件夹下新建文件previewDownFile.js import axios from "axios"; import { ElMessage} from "element-plus";export function previewBtn(url,fileName) { // url-下载预览地址 fileName-文件名if (!fileName) { // 图片预览wind…

P1591 阶乘数码题解

题目 求n!中某个数码出现的次数。 输入输出格式 输入格式 第一行为t(t≤10)&#xff0c;表示数据组数。接下来t行&#xff0c;每行一个正整数n(n≤1000) 和数码a。 输出格式 对于每组数据&#xff0c;输出一个整数&#xff0c;表示n!中a出现的次数。 输入输出样例 输入…

SpringMVC-域对象共享数据

文章目录 域对象共享数据一、三种域对象二、通过ServletAPI向Request域对象共享数据三、使用ModelAndView向Request域对象共享数据四、使用Model向Request域对象共享数据五、使用Map向Request域对象共享数据六、使用ModelMap向Request域对象共享数据七、向Session域对象共享数据…

go-zero配置DB的redis缓存

配置定义&#xff1a; # cat internal/config/config.go package configimport ("github.com/zeromicro/go-zero/rest""github.com/zeromicro/go-zero/core/stores/cache" )type Config struct {rest.RestConfCacheRedis cache.CacheConf }对应的配置文件…

项目中遇到通过域名访问服务提示 Service name unknow

目录 项目中遇到通过域名访问服务提示 Service name unknow 1.问题描述2.问题原因3.解决思路4.解决方案文章所属专区 项目问题解决 1.问题描述 在CentOS 系统环境下 项目中遇到通过域名访问服务提示 Service name unknow,但是 网络是连通的 通过ping 和telnet都能够验证。 …

再学webpack

1 优化 webpack 打包体积的思路 优化 webpack 打包体积的思路包括&#xff1a; 提取第三方库或通过引用外部文件的方式引入第三方库&#xff1a;将第三方库单独打包&#xff0c;并通过 CDN 引入&#xff0c;减少打包体积。使用代码压缩插件&#xff1a;例如 UglifyJsPlugin&a…

GBASE南大通用分享-linux centos下安装dokuwiki

GBASE南大通用分享 首先先大致介绍一下wiki&#xff1a; DokuWiki是一个开源wiki引擎程序&#xff0c;运行于PHP环境下。Doku Wiki 程序小巧而功能强大、灵活&#xff0c;适合中小团队和个人网站知识库的管理。 DokuWiki可以与多种CMS程序进行整合&#xff0c;例如WordPress…

【江科大】STM32:外部中断(Extern Interrupt)

文章目录 EXTI&#xff08;Extern Interrupt&#xff09;外部中断EXIT的基本结构EXIT框图 旋转编码器简介库函数&#xff1a;对射式红外传感器计次&#xff1a;代码展示&#xff1a;旋转编码器计次注意&#xff1a; EXTI&#xff08;Extern Interrupt&#xff09;外部中断 功能…

STM32标准库开发—W25Q64详细介绍

W25Q64简介 Flash编程原理都是只能将1写为0&#xff0c;而不能将0写成1.所以在Flash编程之前&#xff0c;必须将对应的块擦除&#xff0c;而擦除的过程就是将所有位都写为1的过程&#xff0c;块内的所有字节变为0xFF.因此可以说&#xff0c;编程是将相应位写0的过程&#xff0c…

Codewave学习体验分享:低代码开发世界的黑马

前言 Codewave平台介绍 CodeWave智能低代码开发平台基于网易自研拥有大规模参数和深度学习能力的智能模型底座产品架构,为企业级应用提供更加智能化研发的软件生产方式,IT人员可以轻易实现从“智能生成”到“可视化拖拽调整”的全栈低代码应用搭建,让复杂应用开发更加高效,加快…

2024年半导体存储领域的发展趋势

2024年半导体存储领域的发展趋势将呈现出多元且深刻的变化。以下是五大主要内存趋势&#xff1a; 内存价格上涨&#xff1a;随着市场供需调整&#xff0c;半导体内存价格在2023下半年开始预期上涨&#xff0c;并将在2024年持续攀升。Gartner预计内存市场总体增长66.3%&#xff…

Flink生产环境相关问题

1. FlinkKafka保证精确一次消费相关问题&#xff1f; Fink的检查点和恢复机制和可以重置读位置的source连接器结合使用&#xff0c;比如kafka&#xff0c;可以保证应用程序不会丢失数据。尽管如此&#xff0c;应用程序可能会发出两次计算结果&#xff0c;因为从上一次检查点恢…