手把手教你写一个简单的ioc容器

Ioc

IOC(控制反转) 就是 依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式。
在这里插入图片描述

一、主要实现的功能

  • 创建自定义注解@WqxBean,该注解的功能是:被该注解标记的类,会被注册到ioc容器中
  • 创建自定义注解@Di,该注解的功能是:被该注解标记的属性,将会从ioc容器中取出对应的实例化对象,使用该对象将被标记的属性初始化。

二、实现的步骤

  • 1.创建模块 wqx-spring
  • 2.创建两个测试需要用到的类service,dao
  • 3.创建两个注解 @WqxBean @Di
  • 4.创建ioc容器接口
  • 5.实现ioc容器接口

2.1 创建模块

在这里插入图片描述

2.2 创建两个测试需要用到的接口及其实现类service,dao

interface UserDao.class

package wqx.dao;/*** @author Watching* * @date 2023/9/5* * Describe:*/
public interface UserDao {public void run();
}

class UserDaoImpl.class

package wqx.dao.impl;import wqx.anno.WqxBean;
import wqx.dao.UserDao;/*** @author Watching* * @date 2023/9/5* * Describe:*/
@WqxBean
public class UserDaoImpl implements UserDao {@Overridepublic void run() {System.out.println("userDao-run...");}
}

interface UserService.class

package wqx.service;/*** @author Watching* * @date 2023/9/5* * Describe:*/
public interface UserService {public void add();
}

class UserServiceImpl.class

package wqx.service.impl;import wqx.anno.Di;
import wqx.anno.WqxBean;
import wqx.dao.UserDao;
import wqx.dao.impl.UserDaoImpl;
import wqx.service.UserService;/*** @author Watching* * @date 2023/9/5* * Describe:*/
@WqxBean
public class UserServiceImpl implements UserService {@DiUserDaoImpl userDaoImpl;@Overridepublic void add() {System.out.println("add...");userDaoImpl.run();}
}

2.3 创建两个自定义注解

@WqxBean

package wqx.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author Watching* * @date 2023/9/5* * Describe:该自定义注解用于注册javabean进ioc容器,效果类似于@Component*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface WqxBean {
}

@Di

package wqx.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author Watching* * @date 2023/9/5* * Describe:该注解用于依赖注入,效果类似于@Resource*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Di {
}

2.4 创建BeanFactory接口的子接口ApplicationContext

2.4.1 IoC容器在Spring的实现

Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:

①BeanFactory

这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

②ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

③ApplicationContext的主要实现类

在这里插入图片描述

类型名简介
ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

ApplicationContext
接口中创建了一个map集合,这个map集合就是ioc容器,在实现类中如果扫描到目标包下的类有标记@WqxBean注解,则将该类的实例化对象放进ioc容器,key为该类的Class对象,value为该类的实例化对象。

package wqx.BeanFactory;import java.util.HashMap;
import java.util.Map;/*** @author Watching* * @date 2023/9/5* * Describe:*/
public interface ApplicationContext  {//创建Map集合存放Bean对象(ioc容器Map<Class<?>, Object> iocContainer = new HashMap<>();public Object getBean(Class<?> clazz);
}

2.5 实现ApplicationContext接口

实现ApplicationContext接口之后,在实现类中,我们需要做三件事

  • 实现getBean()方法,提供通过对象的Class对象获取实例化对象的方法
  • 完成@WqxBean注解功能的实现
  • 完成@Di注解功能的实现

在开发中,我们一般使用以下方式获取容器中的bean(将配置文件传入构造器函数),所以我们也可以为我们创建的AnnotationApplicationContext类创建一个构造器,构造器参数为要被扫描的包,在构造器中我们会扫描该包及其子包,如果发现包中的类上有@WqxBean注解,则将该类添加进ioc容器。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");/*** Singleton:单实例(在容器启动完成之前就已经创建好了,保存在容器中了,任何时候获取都是获取之前创建好的那个对象)*/User user = (User) applicationContext.getBean("UserSingleton");User user1 = (User) applicationContext.getBean("UserSingleton");

完整代码:

package wqx.BeanFactory.impl;import wqx.BeanFactory.ApplicationContext;
import wqx.anno.Di;
import wqx.anno.WqxBean;import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @author Watching* * @date 2023/9/5* * Describe:*/
public class AnnotationApplicationContext implements ApplicationContext {//1.通过Class类型直接获取Bean对象@Overridepublic Object getBean(Class<?> clazz) {return iocContainer.get(clazz);}//2.设置包扫描规则//当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化//创建有参构造函数,通过构造器传递包路径public AnnotationApplicationContext(String basePackage) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {String packagePath = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);//获取PackagePath的绝对路径while (urls.hasMoreElements()) {URL url = urls.nextElement();//需要将url中被转码的部分解码String filePath = URLDecoder.decode(url.getFile(), "utf-8");//获取包名前面的物理磁盘路径名String rootPath = filePath.substring(0, filePath.length() - basePackage.length());//1.扫描传入的文件路径下的所有文件,找到带有@WqxBean注解的类,将该类注册进ioc容器loadBean(filePath, rootPath);}//依赖注入loadDi();}//扫描类上有@WqxBean注解的类,使用反射创建该类的对象并存入map集合private void loadBean(String filePath, String rootPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {File file = new File(filePath);//1.判断是否是文件夹if (file.isDirectory()) {//1.1 如果是文件夹,则判断是否为空File[] files = file.listFiles();if (files == null || files.length == 0) {//1.1.1 如果为空,则直接退出return;}//1.2 如果不为空,则遍历文件夹for (File f : files) {if (f.isDirectory()) {//1.3如果遇到文件夹,则递归进入loadBean(f.getAbsolutePath(), rootPath);} else {//2.如果是文件,则扫描是否存在注解//2.1 获取文件路径,判断文件是否为.class类型if (f.getAbsolutePath().endsWith(".class")) {//2.2 将文件路径中的\替换为. 并去除.class,得到全类名String packageClassName = f.getAbsolutePath().substring(rootPath.length() - 1, f.getAbsolutePath().length() - ".class".length()).replaceAll("\\\\", "\\.");//2.3 通过反射获取类上的注解,判断是否存在 @WqxBeanClass<?> clazz = Class.forName(packageClassName);WqxBean annotation = clazz.getAnnotation(WqxBean.class);//2.4 如果存在@WqxBean注解,则使用反射创建该对象并将其存放进Map集合中if (!clazz.isInterface() && annotation != null) {//存入ioc容器,key为类的class对象Object o = clazz.getConstructor().newInstance();iocContainer.put(clazz,o);}}}}}}/*** 实现注解注入*/private void loadDi() throws IllegalAccessException {Set<Map.Entry<Class<?>, Object>> entries = iocContainer.entrySet();//1.从容器中取出所有k-v对象for (Map.Entry<Class<?>, Object> entry : entries) {//2.获取容器中已经实例化好的对象Object obj = entry.getValue();//3.获取clazz对象的所有属性//3.1 获取obj实例化对象的Class对象clazzClass<?> clazz = entry.getKey();//3.2 通过clazz对象获取obj对象的所有属性Field[] declaredFields = clazz.getDeclaredFields();//4.遍历所有属性,判断属性上是否有@Di注解for (Field declaredField : declaredFields) {Di di = declaredField.getAnnotation(Di.class);//4.1 如果有Di注解,那么将容器中的对应value值赋给他,value值是一个被实例化的对象if (di != null) {declaredField.setAccessible(true);//私有属性被修改前,Accessible需要被设置为true//4.2 从ioc容器中获取declaredField属性对应的实例化对象Object o = iocContainer.get(declaredField.getType());//4.3 将实例化对象赋值给declareField代表的属性//public void set(Object obj, Object value) obj:被修改的字段所属的对象 value:被修改的字段的新值declaredField.set(obj,o);}}}}
}

2.6 测试

我们已经在UserDaoImpl类和UserServiceImpl类上添加了***@WqxBean***注解,那么这两个类会被注册进spring容器。

在这里插入图片描述
以及在UserServiceImpl中使用@Di将UserDaoImpl注入进来,可以在UserServiceImpl中使用UserDaoImpl的方法了
在这里插入图片描述
测试类

package wqx.Test;import wqx.BeanFactory.ApplicationContext;
import wqx.BeanFactory.impl.AnnotationApplicationContext;
import wqx.service.UserService;
import wqx.service.impl.UserServiceImpl;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;/*** @author Watching* * @date 2023/9/5* * Describe:*/
public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {//通过构造方法创建Beanfactory对象,并将要扫描的包的包名传进构造函数ApplicationContext applicationContext = new AnnotationApplicationContext("wqx");//通过getBean(Class<?> clazz)方法获取目标对象UserService userService= (UserServiceImpl) applicationContext.getBean(UserServiceImpl.class);System.out.println(userService);//输出该对象//调用userService的add方法,add方法中使用USerDaoimpl调用了userDaoImpl的方法userService.add();}
}

结果:
成功
在这里插入图片描述

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

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

相关文章

微服务-gateway基本使用

文章目录 一、前言二、gateway网关1、什么是微服务网关&#xff1f;2、微服务架构下网关的重要性2.1、没有网关2.2、有网关 3、gateway的功能4、gateway实战4.1、依赖配置4.2、添加网关配置4.3、添加网关启动类4.4、查看项目是否启动成功4.5、验证路由配置是否正确 三、总结 一…

报错:axios发送的所有请求都是404

axios发送的所有请求都是404 一、问题二、分析三、解决一、问题 对后台发送数据请求接口,在 Swagger 上是可以请求到的 但是通过 Ajax 发送请求就会报 404 Swagger 上调用如下 项目接口请求如下

react16之前diff算法的理解和总结

此篇文章所讨论的是 React 16 以前的 Diff 算法。而 React 16 启用了全新的架构 Fiber&#xff0c;相应的 Diff 算法也有所改变&#xff0c;本片不详细讨论Fiber。 fiber架构是为了支持react进行可中断渲染&#xff0c;降低卡顿&#xff0c;提升流畅度。 react16之前的版本&…

二、C#—第一个c#程序(2)

&#x1f33b;&#x1f33b; 目录 一、编写第一个C#程序1.1 使用Visual Studio创建c#程序的步骤1.2 编写第一个程序“Hello Word”1.3 c#程序的基本结构1.3.1 c#中的命名空间1.3.2 c#中的类1.3.3 c#中的程序启动器——Main方法1.3.4 c#中的标识符1.3.5 c#中的关键字1.3.6 c#中的…

Java-HashMap中put()方法是如何实现的,内含详细流程图

文章目录 Java中的HashMap什么是HashMap&#xff1f;对比其他Map中put()方法HashMap中put()方法使用示例 HashMap中put()源码解析手绘流程图实现原理源码探究&#xff08;JDK 1.8&#xff09; 设计put()的意义总结 Java中的HashMap 什么是HashMap&#xff1f; HashMap是Java中…

linux并发服务器 —— 多进程并发 - 进程间的通信及实践(五)

进程间的通信 进程是一个独立的资源分配单元&#xff0c;不能在一个进程中直接访问另一个进程的资源&#xff1b; 进程间通信&#xff08;IPC&#xff09;的目的&#xff1a; 1. 数据传输 - A进程发送数据给B进程 2. 通知事件 - eg. 进程终止通知父进程 3. 资源共享 - 多个…

打包个七夕exe玩玩

前段时间七夕 当别的哥们都在酒店不要不要的时候 身为程序员的我 还在单位群收到收到 正好后来看到大佬些的这个 https://www.52pojie.cn/thread-1823963-1-1.html 这个贱 我必须要犯&#xff0c;可是我也不能直接给他装个python吧 多麻烦 就这几个弹窗 好low 加上bgm 再打包成…

解决Maven依赖下载问题:从阿里云公共仓库入手

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

CSS学习笔记03

CSS笔记03 盒子模型 什么是盒子模型 概念&#xff1a; CSS 盒子模型就是在网页设计中经常用到的一种思维模型&#xff0c;是 CSS 布局的基石&#xff0c;主要规定了元素是如何显示的以及元素间的相互关系。定义所有元素都可以有像盒子一样的平面空间和外形。包含内容区、内边…

如何建设一个安全运营中心(SOC)?

然信息安全管理问题主要是个从上而下的问题&#xff0c;不能指望通过某一种工具来解决&#xff0c;但良好的安全技术基础架构能有效的推动和保障信息安全管理。随着国内行业IT应用度和信息安全管理水平的不断提高&#xff0c;企业对于安全管理的配套设施如安全运营中心&#xf…

招投标系统简介 企业电子招投标采购系统源码之电子招投标系统 —降低企业采购成本

​功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

【图解RabbitMQ-3】消息队列RabbitMQ介绍及核心流程

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表&#xff0c;业余独自创办智源恩创网络科技工作室。会点点Java相关技术栈、帆软报表、低代码平台快速开…