Spring之丐版IOC实现

news2024/11/26 4:25:26

文章目录

    • IOC控制反转
      • 依赖注入
        • Bean的自动装配方式
      • 丐版IOC实现
        • BeanDefinition.java
        • ResourceLoader.java
        • BeanRegister.java
        • Bean和DI的注解
        • BeanFactory.java
        • ApplicationContext
        • 测试,实现
    • 在这里插入图片描述

大家好,我是Leo。Spring核心中依赖注入和IOC容器是非常常见的,用起来也是非常的顺手,只能说是真香,那如何实现一个丐版的SpringIOC呢?那今天我们就来讲如何实现一个乞丐版的IOC和通过注解进行依赖注入。

IOC控制反转

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

说白了就是,IOC负责我们的饮食,我们要饭和菜,只需要从IOC容器获取就行。

那这样的作用也就非常明显了,解耦、方便

依赖注入

依赖注入肯定是IOC的精髓所在,我们平常使用@Autowired注解等自动注入方式,用起来确实舒服。

那依赖注入常用方式有哪些呢?

  • 构造函数的参数实现注入

  • 基于set注入(静态工厂和动态工厂)

  • 基于属性的注入

Bean的自动装配方式

可以在文件中设置自动装配(autowire)方式,支持的装配方式有

方式描述
no手动装配
byName通过id的名字自动注入对象
byType通过类型自动注入对象
constructor通过构造方法自动注入对象
autodectect完全交给Spring管理,按先Constructor后byType的顺序进行匹配

丐版IOC实现

BeanDefinition.java

Bean的定义类,主要是包括配置Bean定义的对应的实体,对应的构造方法和getset方法就省略了

public class BeanDefinition {

    private String beanName;

    private Class beanClass;

ResourceLoader.java

资源加载器,用来完成包的扫描和Bean的加载

public class ResourceLoader {

    private static String rootPath;

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap(16);

    public Map<String, BeanDefinition> getResource(String basePackage) {

        // com.zly
        try {
            // 1. 把.换成/
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            // 2.获取包的绝对路径
            Enumeration<URL> urls =
                    Thread.currentThread()
                            .getContextClassLoader()
                            .getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                // 包扫描
                try {
                    loadBean(new File(filePath));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this.beanDefinitionMap;
    }

    private void loadBean(File file) throws Exception {
        // 1. 判断是否是文件夹
        if (file.isDirectory()) {
            // 2. 获取文件夹的所有内容
            File[] childFiles = file.listFiles();
            // 3. 判断文件夹内为空,直接返回
            if (childFiles == null || childFiles.length == 0) {
                return;
            }
            // 4. 如果文件夹里面不为空,遍历文件夹所有的内容
            for (File childFile : childFiles) {
                // 4.1 遍历得到每个File对象,继续是文件夹,递归
                if (childFile.isDirectory()) {
                    loadBean(childFile);
                } else {
                    // 4.2 得到不是文件夹,是文件
                    // 4.3 得到包路径 和类名称
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
                    // 4.4 判断当前文件类型是否为class
                    if (pathWithClass.contains(".class")) {
                        // 4.5 是class类型,把\替换成. 把.class去掉
                        String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        // 4.6 判断类上面是否有注解@Bean,放到map集合beanFactory
                        // 4.6.1 获取类的Class对象
                        Class<?> clazz = Class.forName(allName);
                        // 4.6.2 判断不是接口
                        if (!clazz.isInterface()) {
                            // 4.6.3 判断上面是否有@Bean注解
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                // 4.6.4 实例化
                                String beanName = annotation.value();
                                if ("".equals(beanName)) {
                                    beanName = allName;
                                }
                                if (clazz.getInterfaces().length > 0) {
                                    System.out.println("正在加载【"+ clazz.getInterfaces()[0] +"】");
                                    BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
                                    beanDefinitionMap.put(beanName, beanDefinition);
                                } else {
                                    System.out.println("正在加载【"+ clazz.getInterfaces()[0]);
                                    BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
                                    beanDefinitionMap.put(beanName, beanDefinition);
                                }
                            }
                        }
                    }
                }
            }
        }

    }

}

BeanRegister.java

用于向容器中注册一个Bean,在扫描后,我们的Bean主要是放在了beanDefinitionMap中,还没有进行加载和初始化,在获取中再进行加载到这个缓存Map中

public class BeanRegister {

    private Map<String, Object> singletonMap = new HashMap<>(32);

    public Object getSingletonBean(String beanName) {
        return singletonMap.get(beanName);
    }


    public void registerSingletonBean(String beanName, Object bean) {
        if (singletonMap.containsKey(beanName)) {
            return;
        }
        singletonMap.put(beanName, bean);
    }

}

Bean和DI的注解

这里的两个注解主要是用来标志这是一个Bean和进行依赖注入的

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {

    String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

BeanFactory.java

这里的对象工厂主要是我们最重要的一个类,在创建时,需要加载注册器和资源加载,在获取Bean中,需要进行依赖注入,并创建一个Bean

public class BeanFactory {

    /**
     * 创建一个map集合,放bean对象
     */
    private Map<String , BeanDefinition> beanDefinitionMap = new HashMap<>();

    private BeanRegister beanRegister;

    public BeanFactory(String basePackage) {
        beanRegister = new BeanRegister();
        this.beanDefinitionMap = new ResourceLoader().getResource(basePackage);
    }


    public Object getBean(String beanName) {
        // 从缓存中获取
        Object bean = beanRegister.getSingletonBean(beanName);
        if (bean != null) {
            return bean;
        }
        // 创建bean
        return createBean(beanDefinitionMap.get(beanName));
    }

    public Object getBean(Class<?> clazz) {
        BeanDefinition beanDefinition = getBeanNameByType(clazz);
        if (Objects.isNull(beanDefinition)) {
            log.error("can not find {}", clazz);
            return null;
        } else {
            return getBean(beanDefinition.getBeanName());
        }
    }

    private Object createBean(BeanDefinition beanDefinition) {
        try {
            Object bean = beanDefinition.getBeanClass().getConstructor().newInstance();
            beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
            loadDi(beanDefinition.getBeanName());
            return bean;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 属性注入
     */
    private void loadDi(String beanName) {
        // 1. 实例化对象都在beanFactory的map集合中,遍历
        Object bean = this.beanRegister.getSingletonBean(beanName);
        // 2. 获取map集合每个对象和属性
        Class<?> objectClass = bean.getClass();
        // 3. 遍历得到每个对象属性数组,得到每个属性
        Field[] declaredFields = objectClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            // 4. 判断属性上面是否有@Di注解
            Di annotation = declaredField.getAnnotation(Di.class);
            if (annotation != null) {
                declaredField.setAccessible(true);
                // 如果私有属性,可以设置值
                // 5. 如果有@Di注解,把对象进行注入
                try {
                    BeanDefinition beanDefinition = getBeanNameByType(declaredField.getType());
                    if (Objects.isNull(beanDefinition)) {
                        declaredField.set(bean, null);
                    } else {
                        declaredField.set(bean, getBean(beanDefinition.getBeanName()));
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }

    private BeanDefinition getBeanNameByType(Class clazz) {
        Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            if (entry.getValue().getBeanClass().equals(clazz)) {
                return entry.getValue();
            }
            if (entry.getValue().getBeanClass().getInterfaces()[0].equals(clazz)) {
                return entry.getValue();
            }
        }
        return null;
    }

}

ApplicationContext

我也模仿了Spring一样定义了一个ApplicationContext容器,获取Bean从这个容器获取,context容器再从BeanFactory中获取

public class AnnotationApplicationContext implements ApplicationContext {

    /**
     * 创建一个map集合,放bean对象
     */
    private BeanFactory beanFactory;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.getBean(clazz);
    }

    @Override
    public Object getBean(String beanName) {
        return beanFactory.getBean(beanName);
    }

    /**
     * 设置包扫描规则
     * 当前包及其子包,哪个类有@Bean注解,把这个类通过反射化
     */
    public AnnotationApplicationContext(String basePackage) throws Exception {
        this.beanFactory = getBeanFactory(basePackage);
    }

    private BeanFactory getBeanFactory(String baskPackage) {
        return new BeanFactory(baskPackage);
    }

}


测试,实现

@Bean
public class UserServiceImpl implements UserService {

    @Di
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("service.........");
        userDao.add();
    }
}
public interface UserService {
    void add();
}
@Bean
public class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("dao add...");
    }
}
public interface UserDao {
    void add();
}
public class UserTest {
    public static void main(String[] args){
        try {
            ApplicationContext context = new AnnotationApplicationContext("com.zly");
            UserService userService = (UserService)context.getBean(UserService.class);
            userService.add();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

相信你看到这里,就可以很容易的理解了这个丐版的Spring就算完成了,希望可以对大家有帮助啦👍👍👍

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

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

相关文章

JWT令牌

1.普通令牌的问题 客户端申请到令牌&#xff0c;接下来客户端携带令牌去访问资源&#xff0c;到资源服务器将会校验令牌的合法性。 从第4步开始说明&#xff1a; 1、客户端携带令牌访问资源服务获取资源。 2、资源服务远程请求认证服务校验令牌的合法性 3、如果令牌合…

QML Image and Text(图像和文字)

Image&#xff08;图片&#xff09; 图像类型显示图像。 格式&#xff1a; Image {source: "资源地址" } source&#xff1a;指定资源的地址 自动检测文件拓展名&#xff1a;source中的URL 指示不存在的本地文件或资源&#xff0c;则 Image 元素会尝试自动检测文件…

MVI 架构更佳实践:支持 LiveData 属性监听

前言MVI架构为了解决MVVM在逻辑复杂时需要写多个LiveData(可变不可变)的问题,使用ViewState对State集中管理&#xff0c;只需要订阅一个 ViewState 便可获取页面的所有状态通过集中管理ViewState&#xff0c;只需对外暴露一个LiveData&#xff0c;解决了MVVM模式下LiveData膨胀…

Linux_vim编辑器入门级详细教程

前言&#xff08;1&#xff09;vim编辑器其实本质上就是对文本进行编辑&#xff0c;比如在.c文件中改写程序&#xff0c;在.txt文件写笔记什么的。一般来说&#xff0c;我们可以在windows上对文本进行编译&#xff0c;然后上传给Linux。但是有时候我们可能只是对文本进行简单的…

MySQL运维

目录 1、日志 1、错误日志 2、二进制日志 3、查询日志 4、慢查询日志 2、主从复制 搭建 1、主库配置 2、从库配置 3、分库分表 1、简介 ​编辑 1、垂直拆分 2、水平拆分 3、实现技术 2、MyCat 3、MyCat使用和配置 配置 4、MyCat分片 1、垂直拆分 2、水平拆分…

线材-电子线载流能力

今天来讲的是关于电子线的一个小知识&#xff0c;可能只做板子的工程师遇到此方面的问题会比较少&#xff0c;做整机的工程师则必然会遇到此方面问题&#xff0c;那就是线材问题。 下面主要说下电子线的过电流能力。&#xff08;文末有工具下载&#xff09;电子线&#xff08;h…

vue3+rust个人博客建站日记2-确定需求

反思 有人说过我们正在临近代码的终结点。很快&#xff0c;代码就会自动产生出来&#xff0c;不需要再人工编写。程序员完全没用了&#xff0c;因为商务人士可以从规约直接生成程序。 扯淡&#xff01;我们永远抛不掉代码&#xff0c;因为代码呈现了需求的细节。在某些层面上&a…

CTFer成长之路之Python中的安全问题

Python中的安全问题CTF 1.Python里的SSRF 题目提示 尝试访问到容器内部的 8000 端口和 url path /api/internal/secret 即可获取 flag 访问url&#xff1a; http://f5704bb3-5869-4ecb-9bdc-58b022589224.node3.buuoj.cn/ 回显如下&#xff1a; 通过提示构造payload&…

Pytorch复习笔记--Conv2d和Linear的参数量和显存占用量比较

目录 1--nn.Conv2d()参数量的计算 2--nn.Linear()参数量计算 3--显存占用量比较 1--nn.Conv2d()参数量的计算 conv1 nn.Conv2d(in_channels3, out_channels64, kernel_size1) 计算公式&#xff1a; Param in_c * out_c * k * k out_c; in_c 表示输入通道维度&#xff1b…

微信小程序-1:比较两数的大小

程序来源》微信小程序开发教程&#xff08;第二章&#xff09; 主编&#xff1a;黄寿孟、易芳、陶延涛 ISBN&#xff1a; 9787566720788 程序运行结果&#xff1a; <!--index.wxml--> <view class"container"> <text>第一个数字&#xff1a;&…

从认知智能的角度认识ChatGPT的不足

OpenAI的ChatGPT带来了一些令人欣喜的成果&#xff0c;但是从认知智能的角度来看&#xff0c;也有很多不足。今天我就来为大家说一说。首先我会为大家简单介绍人工智能&#xff0c;认知智能&#xff0c;然后再分析ChatGPT的能力和不足&#xff0c;最后分享目前优秀的学术论文以…

网络应用之静态Web服务器

静态Web服务器-返回固定页面数据学习目标能够写出组装固定页面数据的响应报文1. 开发自己的静态Web服务器实现步骤:编写一个TCP服务端程序获取浏览器发送的http请求报文数据读取固定页面数据&#xff0c;把页面数据组装成HTTP响应报文数据发送给浏览器。HTTP响应报文数据发送完…

[11]云计算|简答题|案例分析|云交付|云部署|负载均衡器|时间戳

升级学校云系统我们学校要根据目前学生互联网在线学习、教师教学资源电子化、教学评价过程化精细化的需求&#xff0c;计划升级为云教学系统。请同学们根据学校发展实际考虑云交付模型包含哪些&#xff1f;云部署采用什么模型最合适&#xff1f;请具体说明。9月3日买电脑还是租…

python之并发编程

一、并发编程之多进程 1.multiprocessing模块介绍 python中的多线程无法利用多核优势&#xff0c;如果想要充分地使用多核CPU的资源&#xff08;os.cpu_count()查看&#xff09;&#xff0c;在python中大部分情况需要使用多进程。Python提供了multiprocessing。 multiprocess…

SEO优化:干货技巧分享,包新站1-15天100%收录首页

不管是老域名还是新域名&#xff0c;不管是多久没有收录首页的站&#xff0c;此法周期7-30天&#xff0c;包首页收录&#xff01;本人不喜欢空吹牛逼不实践的理论&#xff0c;公布具体操作&#xff1a;假如你想收录的域名是a.com&#xff0c;那么准备如下材料1.购买5-10个最便宜…

【Tomcat】Tomcat安装及环境配置

文章目录什么是Tomcat为什么我们需要用到Tomcattomcat下载及安装1、进入官网www.apache.org&#xff0c;找到Projects中的project List2、下载之后&#xff0c;解压3、找到tomcat目录下的startup.bat文件&#xff0c;双击之后最后结果出现多少多少秒&#xff0c;表示安装成功4、…

【Python工具篇】Anaconda中安装python2和python3以及在pycharm中使用

背景&#xff1a;已经安装好anaconda、python3、pycharm&#xff0c;因为项目使用的是python2语法&#xff0c;所以需要在anaconda中安装python2&#xff0c;并在pycharm中使用&#xff0c;下面给出步骤。 1. 打开cmd或者是Anaconda Prompt。 下面是anaconda prompt. 2. 查…

hadoop03-MapReduce【尚硅谷】

大数据学习笔记 MapReduce 一、MapReduce概述 MapReduce是一个分布式运算程序的编程框架&#xff0c;是基于Hadoop的数据分析计算的核心框架。 MapReduce处理过程为两个阶段&#xff1a;Map和Reduce。 Map负责把一个任务分解成多个任务&#xff1b;Reduce负责把分解后多任务处…

必应ai注册方法

1.进行配置下载Microsoft Edge Canary&#xff0c;链接下载 Microsoft Edge Insider Channels&#xff0c;打开Microsoft Edge Canary&#xff0c;在Header Editor - Microsoft Edge Addons中安装Header Editor插件&#xff0c;点击Header Editor图标&#xff0c;选择右下角的&…

网页唤起 APP中Activity的实现原理

疑问的开端大家有没有想过一个问题&#xff1a;在浏览器里打开某个网页&#xff0c;网页上有一个按钮点击可以唤起App。这样的效果是怎么实现的呢&#xff1f;浏览器是一个app&#xff1b;为什么一个app可以调起其他app的页面&#xff1f;说到跨app的页面调用&#xff0c;大家是…