自己写一个简单的IOC

news2024/11/15 21:32:39

什么是SpringIOC?
答:IOC即控制反转,就是我们不在手动的去new一个对象,而是将创建对象的权力交给Spring去管理,我们想要一个User类型的对象,就只需要定义一个User类型的变量user1,然后让Spring去给我们创建对象,然后将创建的对象注入到user1中。
什么是依赖注入?
答:DI机制(DependencyInjection),依赖注入,上面提到的Spring将它创建的对象交给我们创建的变量的过程。依赖注入的方式有三种(set方法注入、构造器注入、注解注入)。下面我们简答的实现注解注入,了解IOC原理。

第一步,创建注解

创建的注解其实没有太大的作用,就是用来标记哪个类需要Spring帮我们去管理,哪个成员变量需要Spring去给我们注入。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*创建注解文件Component.java
*标记需要IOC的类
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*创建注解文件Autowired.java
*标记需要Spring帮忙DI的成员变量
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

第二部,创建两个需要注解的类

我们想要将MyService和MyController都交给Spring管理(项目启动后将这两个类实例化,然后放入一个HashMap中,等待调用)
在这里插入图片描述

import com.xiaoran.springioc.annotation.Component;
@Component
public class MyService {
    private int id;
    private String name;
    private int age;
    public void speck(String name,int age) {
        System.out.println("大家好,我叫"+name+"今年"+age+"岁了,请多多关照!");

    }
}
import com.xiaoran.springioc.annotation.Autowired;
import com.xiaoran.springioc.annotation.Component;
@Component
public class MyController {
    @Autowired
    private MyService myService;
    public void test(){
        myService.speck("moss",67);
    }
}

第三步,IOC过程

创建MyIOC类,用于IOC过程

  • 在项目启动时,实例化MyIOC,加载MyIOC的无参构造器时,对项目下所有的文件进行扫描,调用实例化方法完成被注释类的实例化和依赖注入。
    private String basePath="D:\\Project\\XiaoRanIOC\\src\\main\\java\\com\\xiaoran\\springioc\\";  //项目路径
    private String basePackage="com.xiaoran.springioc"; //包路径
    private List<String> filePaths;//所有文件的路径
    private List<String> beanNames;//所有.java文件的全限定名
    private Map<String, Object> beans = new HashMap<>();
    /**
     * 实例化MyIOC时,让IOC进程伴随无参构造器加载启动
     */
    public MyIOC() throws FileNotFoundException, IllegalAccessException {
        //扫描路径下所有文件
        scan();
        beanNames = new ArrayList<>();
        initBeanNames();
        initBeans();
    }
  • 扫描项目下所有文件,将所有文件的路径存入filePaths中
/**
     * 扫描项目下所有文件,将所有文件的路径存入filePaths中
     */
    public void scan() throws FileNotFoundException {
        File file = new File(basePath);
        filePaths=new ArrayList<>();
        if (file.exists()) {
            //将file放入列,出队后判断,如果是路径那就继续入队,如果是文件,就将文件路径放入filePaths中
            Queue<File> queue = new LinkedList<>();
            queue.add(file);
            while (!queue.isEmpty()) {
                File poll = queue.poll();
                if(poll==null){
                    continue;
                }
                if (poll.isDirectory()){
                    File[] files = poll.listFiles();
                    for (File f :
                            files) {
                        queue.add(f);
                    }
                }else{
                    filePaths.add(poll.getPath());
                }
            }
        }else {
            throw new FileNotFoundException(basePath + "不存在");
        }
    }
  • 将所有的.java文件的全限定名放入beanNames中
/**
     *将所有的.java文件的全限定名放入beanNames中
     */
    public void initBeanNames(){
        for (String string :
                filePaths) {
            String replace = string.replace(basePath, "");
            if (replace.endsWith(".java")) {
                replace = replace.substring(0, replace.length() - 5);
            }
            char[] chars = replace.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                if(chars[i]=='\\'){
                    chars[i] = '.';
                }
            }
            beanNames.add(basePackage+"."+new String(chars));
        }
    }
  • 核心代码:将被@Component注解的类实例化放入beans(HashMap)中,等待调用
   /**
     *核心代码:将被@Component注解的类实例化放入beans(HashMap)中,等待调用
     */
    public void initBeans() throws IllegalAccessException {
        //遍历包路径下所有类,是否被@Component注解,如果被注解就将其实例化放入beans
        for (String beanName :
                beanNames) {
            try {

                Class<?> aClass = Class.forName(beanName);
                //获取类的所有注解
                Annotation[] declaredAnnotation = aClass.getDeclaredAnnotations();
                //遍历所有注解,是否是@Component注解
                for (Annotation annotation :
                        declaredAnnotation) {
                    if (annotation instanceof Component){
                        Object o = aClass.newInstance();
                        beans.put(beanName, o);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }

        //遍历所有的beans的成员变量,如果有成员变量被@Autowired修饰,就根据成员变量的类型从beans中查找到对应的对象,用此对象给成员变量注入
        //因为是从beans中查找对象,所以被注入的成员变量对应的类一定是已经被实例化放入beans中的
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object value = entry.getValue();
            Field[] fields = value.getClass().getDeclaredFields();
            for (Field f :
                    fields) {
                Annotation[] declaredAnnotations = f.getDeclaredAnnotations();
                for (Annotation annotation :
                        declaredAnnotations) {
                    if (annotation instanceof Autowired){
                        //获取被@Autowired注解成员变量的类型(全限定名)
                        String typeName = f.getType().getName();
                        Object o = beans.get(typeName);
                        //暴力反射
                        f.setAccessible(true);
                        //将从beans中获得的对象o,注入到该属性上
                        f.set(value,o);
                    }
                }
            }

        }
    }
    /**
     * 对外提供一个方法:根据全限定名返回对象
     */
    public Object getInstance(String beanName){
        return beans.get(beanName);
    }

第四步,测试

创建测试类IOCTest

import com.xiaoran.springioc.entity.MyController;
import com.xiaoran.springioc.ioc.MyIOC;
import org.junit.Test;
import java.io.FileNotFoundException;
public class IOCTest {
    @Test
    public void test() throws FileNotFoundException, IllegalAccessException {
        MyIOC myIOC = new MyIOC();
        MyController myController = (MyController)myIOC.getInstance(MyController.class.getName());
        myController.test();

    }
}

GitHub

手撕SpringIOC

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

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

相关文章

蓝桥杯-刷题统计

蓝桥杯-刷题统计1、问题描述2、解题思路3、代码实现3.1 方案一&#xff1a;累加方法(超时)3.2 方案二1、问题描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目, 周六和周日每天做 b 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数…

KNN学习报告

原理 KNN算法就是在其表征空间中&#xff0c;求K个最邻近的点。根据已知的这几个点对其进行分类。如果其特征参数只有一个&#xff0c;那么就是一维空间。如果其特征参数只有两个&#xff0c;那么就是二维空间。如果其特征参数只有三个&#xff0c;那么就是三维空间。如果其特征…

软件设计师教程(七)计算机系统知识-操作系统知识

软件设计师教程 软件设计师教程&#xff08;一&#xff09;计算机系统知识-计算机系统基础知识 软件设计师教程&#xff08;二&#xff09;计算机系统知识-计算机体系结构 软件设计师教程&#xff08;三&#xff09;计算机系统知识-计算机体系结构 软件设计师教程&#xff08;…

Redis十大类型——Hash常见操作

Redis十大类型——Hash常见操作命令操作简列存放及获取获取健值对长度元素查找列出健值对对数字进行操作赋值hsetnx很明显咯它也是以健值对方式存在的&#xff0c;只不过value也就是值&#xff0c;在这里也变成了一组简直对。 &#x1f34a;个&#x1f330;&#xff1a; 想必多…

【Linux】P3 用户与用户组

用户与用户组root 超级管理员设置超级管理员密码切换到超级管理员sudo 临时使用超级权限用户与用户组用户组管理用户管理getentroot 超级管理员 设置超级管理员密码 登陆后不会自动开启 root 访问权限&#xff0c;需要首先执行如下步骤设定 root 超级管理员密码 1、解除 roo…

【C++】string的使用及其模拟实现

文章目录1. STL的介绍1.1 STL的六大组件1.2 STL的版本1.3 STL的缺陷2. string的使用2.1 为什么要学习string类&#xff1f;2.2 常见构造2.3 Iterator迭代器2.4 Capacity2.5 Modifiers2.6 String operations3. string的模拟实现3.1 构造函数3.2 拷贝构造函数3.3 赋值运算符重载和…

DevOps实战50讲-(2)Jenkins配置

1. Docker镜像方式安装拉取Jenkins镜像docker pull jenkins/jenkins编写docker-compose.ymlversion: "3.1" services:jenkins:image: jenkins/jenkinscontainer_name: jenkinsports:- 8080:8080- 50000:50000volumes:- ./data/:/var/jenkins_home/首次启动会因为数据…

iis之web服务器搭建、部署(详细)~千锋

目录 Web服务器 部署web服务器 实验一 发布一个静态网站 实验二 一台服务器同时发布多个web站点 网站类型 Web服务器 也叫网页服务或HTTP服务器web服务器使用的协议是HTTPHTTP协议端口号&#xff1a;TCP 80、HTTPS协议端口号&#xff1a;TCP 443Web服务器发布软件&…

【备战面试】每日10道面试题打卡-Day4

本篇总结的是Java集合知识相关的面试题&#xff0c;后续也会更新其他相关内容 文章目录1、HashMap在JDK1.7和JDK1.8中有哪些不同&#xff1f;2、HashMap 的长度为什么是2的幂次方&#xff1f;3、HashMap的扩容操作是怎么实现的&#xff1f;4、HashMap是怎么解决哈希冲突的&…

Android 基础知识4-3.5 RadioButton(单选按钮)Checkbox(复选框)详解

一、RadioButton&#xff08;单选按钮&#xff09; 1.1、简介 RadioButton表示单选按钮&#xff0c;是button的子类&#xff0c;每一个按钮都有选择和未选中两种状态&#xff0c;经常与RadioGroup一起使用&#xff0c;否则不能实现其单选功能。RadioGroup继承自LinearLayout&a…

滚动升级回滚

滚动升级回滚 ReplicationController 资源文件 apiVersion: v1 kind: ReplicationController metadata:name: kubia-v1labels:app: kubia spec:replicas: 3template:metadata:name: kubialabels:app: kubiaspec:containers:- image: luksa/kubia:v1name: nodejes --- apiVer…

【Linux学习】基础IO——软硬链接 | 制作动静态库

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 基础IO&#x1f353;软硬链接&#x1f332;软链接&#x1f332;硬链接&#x1f353;动静态库&…

BPMN2.0规范及流程引擎选型方案

BPMN2.0规范及流程引擎选型方案一、基本概念二、BPMN意义三、主要元素3.1 活动任务子流程调用活动事件子流程事务3.2 网关排他网关包容网关并行网关事件网关3.3 事件开始事件结束事件中间事件3.4 辅助泳道图注释与组数据存储四、图类型4.1 编排图4.2 会话图五、技术选型5.1 前端…

大数据ETL开发之图解Kettle工具

详细笔记参考&#xff1a;https://blog.csdn.net/yuan2019035055/article/details/120409547以下只是简单记录一下我学习过程中的心得3.1.5 JSON输入JSONPath 类似于 XPath 在 xml 文档中的定位&#xff0c;JsonPath 表达式通常是用来路径检索或设置Json的。其表达式可以接受“…

阶段二11_面向对象高级_学生管理系统案例2

主要内容&#xff1a; 添加学生 static关键字一.添加学生时判断id是否存在 0.思路图片&#xff1a; 04/图片/2_添加学生判断id存在的问题分析.png 1.思路实现详细步骤&#xff1a; StudentController【客服接待】 /** 接收到学生id后&#xff0c;判断该id在数组中是否存在 这…

SRS源码分析-SDP内容解析

前言 在学习SRS的RTC模块之前&#xff0c;首先来分析下SRS在将rtmp推流转成rtc流&#xff0c;通过浏览器拉取webrtc流场景下产生的SDP内容 SDP格式介绍 SDP数据是文本格式&#xff0c;由多个 <key><value> 表达式构成&#xff0c;<key>的值只能是一个字符…

第二讲:ambari编译复盘,如何实现一次性成功编译ambari

上节课我们已经讲解了如何成功编译ambari源码,安装ambari-server rpm包以及成功部署ambari。本节课我们来复盘一下上节课的编译过程,以及思考如何实现一次性成功编译ambari。 要想一次性成功编译ambari,那么就需要将预置工作做好,比如: maven镜像源配置,node_moudle模块…

Go项目(商品微服务-2)

文章目录简介handler商品分类轮播图品牌和品牌与分类oss前端直传库存服务数据不一致redis 分布式锁小结简介 开发商品微服务 API 层类似的&#xff0c;将 user-web 目拷贝一份&#xff0c;全局替换掉 user-web修改 config 去掉不用的配置更新本地和远程 nacos 配置文件 把 pro…

OpenGL环境配置

方法一&#xff1a;1.下载GLFW点击GLFW跳转2.下载后解压3.下载glad&#xff0c;解压后4.用vs2019新建Cmake项目5.在新建的Cmake项目下建立depend文件夹在depend里放置我们下载解压的glad和glfw-3.3.8.bin.WIN646.项目中可以看到我们加进来的文件7.编写我们项目的CMakeLists.txt…

Condition 源码解读

一、Condition 在并发情况下进行线程间的协调&#xff0c;如果是使用的 synchronized 锁&#xff0c;我们可以使用 wait/notify 进行唤醒&#xff0c;如果是使用的 Lock 锁的方式&#xff0c;则可以使用 Condition 进行针对性的阻塞和唤醒&#xff0c;相较于 wait/notify 使用…