手动实现 SpringBoot 中的 IoC(控制反转)

您所在的位置:网站首页 springboot控制反转 手动实现 SpringBoot 中的 IoC(控制反转)

手动实现 SpringBoot 中的 IoC(控制反转)

2024-07-12 13:58:03| 来源: 网络整理| 查看: 265

1. 什么是Spring Boot IOC

IOC(Inversion of Control)是指将控制反转,以前我们的程序里需要自己去 new 对象,通过依赖注入的方式,我们可以告诉Spring Boot框架需要哪些类,框架会根据您的要求自动将对象装配到相应位置,我们不需要关心对象的创建和依赖关系,让 Spring Boot 的控制流程来管理,这就是IOC的精髓。

2. 手动创建对象不行吗?

在一个程序中,不同的对象之间经常需要相互协作,多个对象之间相互依赖。如果我们在代码中手动创建这些对象,即使用 new 关键字进行实例化,那么在对象之间产生依赖关系时就需要我们手动进行对象的传递和管理,这样代码会变得非常繁琐,可读性和可维护性都会大大降低。

而IoC(Inverse of Control)控制反转是一种解决这类问题的设计模式,就是将对象实例的创建、依赖关系的管理和生命周期托管给 IoC 容器,由容器自动进行管理和注入。Spring Boot 的 IoC 容器会自动创建对象实例,并在需要的时候自动注入依赖关系。这样我们就可以避免手动创建对象,也不需要考虑对象之间的依赖关系,代码量可以大大减少,程序的可读性和可维护性也会更高。

简单来说,使用 Spring Boot 的 IoC 容器可以让我们专注于业务逻辑的实现,而无需关注对象的创建和依赖关系的管理,使得开发变得更加高效、简单和可维护。

3. 基于 Java 基本库实现 IoC

首先,我们定义一个 @Component 注解和 @Autowired 注解:

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { }

接着定义 IoC 容器类 SimpleIoc,用于维护对象映射关系和创建实例:

package com.example.demo; import java.lang.reflect.Field; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class SimpleIoc { private final Map beanMap = new ConcurrentHashMap(); public void scan(String packageName) throws Exception { Set classes = ClassUtils.getClasses(packageName); for (Class cls : classes) { if (cls.isAnnotationPresent(Component.class)) { register(cls); } } } public void register(Class clazz) throws Exception { for (Class intf : clazz.getInterfaces()) { beanMap.put(intf, createBean(clazz)); } } private Object createBean(Class clazz) throws Exception { // 如果已经注册了该类,直接返回 Object bean = beanMap.get(clazz); if (bean != null) { return bean; } bean = clazz.newInstance(); beanMap.put(clazz, bean); // 查找该类的带有 @Autowired 注解的属性,并注入依赖 for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { Class fieldType = field.getType(); Object dependency = beanMap.get(fieldType); if (dependency == null) { dependency = createBean(fieldType); } field.setAccessible(true); field.set(bean, dependency); } } return bean; } public T getBean(Class clazz) { return (T) beanMap.get(clazz); } }

对于 scan 方法,我们使用 getClasses 方法从指定包中获取所有的 Class 对象,并对每一个带有 @Component 注解的类进行注册。当注册某个 Class 对象时,我们使用反射创建它的实例对象并将其放入 beanMap 中,并使用循环遍历其所有字段,如果有带有 @Autowired 注解的字段,就通过 getBean 方法获取它所要依赖的对象,并将其注入到当前对象中。getBean 方法根据传入的 Class 对象从 beanMap 中获取相应的实例,如果不存在就抛出异常。

接下来定义 UserRepository 接口和它的实现类 UserRepositoryImpl:

interface UserRepository { String getInfo(); } @Component class UserRepositoryImpl implements UserRepository { @Override public String getInfo() { return "Hello, World!"; } }

然后定义 UserService 接口和它的实现类 UserServiceImpl:

public interface UserService { String getInfo(); } @Component public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Override public String getInfo() { return userRepository.getInfo(); } }

在上面的代码中,UserServiceImpl 类中使用了带有 @Autowired 注解的 userRepository 字段用于注入依赖,并实现了 getInfo 方法。

以下是简单实现的 ClassUtils 工具类代码,用于从指定包名中获取所有的 Class 对象

import java.io.File; import java.net.URL; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; public class ClassUtils { public static Set getClasses(String packageName) throws Exception { Set classes = new HashSet(); String path = packageName.replace(".", "/"); Enumeration resources = Thread.currentThread().getContextClassLoader().getResources(path); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); if (resource != null) { String protocol = resource.getProtocol(); if ("file".equals(protocol)) { classes.addAll(findClassesByFile(new File(resource.toURI()), packageName)); } } } return classes; } private static Set findClassesByFile(File parent, String packageName) throws Exception { Set classes = new HashSet(); if (!parent.isDirectory()) { return classes; } File[] files = parent.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class")); for (File file : files) { String fileName = file.getName(); if (file.isDirectory()) { classes.addAll(findClassesByFile(file, packageName + "." + fileName)); } else { String className = packageName + "." + fileName.substring(0, fileName.length() - 6); classes.add(Class.forName(className)); } } return classes; } }

最后,我们定义一个简单的测试类:

public class Main { public static void main(String[] args) throws Exception { SimpleIoc ioc = new SimpleIoc(); ioc.scan("com.example.demo"); UserService userService = ioc.getBean(UserService.class); System.out.println(userService.getInfo()); } } 4. 总结

@Component 注解:该注解用于标记需要被 IoC 容器所管理的类,被标记的类会自动被扫描并加入到 IoC 容器中进行管理。

@Autowired 注解:该注解用于标记需要进行依赖注入的属性,被标记的属性会自动被 IoC 容器所管理的对象所注入。

SimpleIoc 类:该类是一个简单的 IoC 容器实现,主要实现了对象的创建和注入,并且能够自动扫描标记了 @Component 注解的类,并将其加入到容器中进行管理。

UserRepository 接口及其实现类:模拟了一个 DAO 层的接口,实现了简单的业务逻辑。

UserService 接口及其实现类:模拟了一个 Service 层的接口,依赖于 UserRepository 接口,并实现了相应的业务逻辑。

Main 类:程序的入口类,使用 SimpleIoc 容器创建 UserService 对象并调用其方法。

此示例程序通过简单的实现了 IoC 的基本原理和使用方法,它可以帮助我们更好地理解 IoC 的概念和实现方式。在实际开发中,Spring Boot 的 IoC 容器使用更加自动化和方便,可扩展性也非常好。

PS:你以为IoC容器多么复杂,其实就是个Map。^_^

关注微信公众号:小虎哥的技术博客



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭