手写Spring IOC-简易版

news2024/10/18 8:22:44

目录

    • 项目结构
      • entity
      • dao
        • IUserDao
        • UserDaoImpl
      • service
        • IUserService
        • UserServiceImpl
      • ApplicationContext
    • 配置文件初始化 IOC 容器
      • RunApplication
    • 注解初始化 IOC 容器
      • @Bean
      • @Autowired
    • Reference

项目结构

在这里插入图片描述

entity

  1. User
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User implements Serializable {
    private String username;
    private String password;
    private int age;
}

dao

IUserDao
public interface IUserDao {
    /**
     * Add user
     */
    int addUser(User user);

    /**
     * Get user by id.
     */
    User getUserById(int id);

    /**
     * Get all users.
     */
    List<User> getAllUsers();

    /**
     * Get users by name.
     */
    List<User> getUsersByName(String name);
}
UserDaoImpl
public class UserDaoImpl implements IUserDao {
    @Override
    public int addUser(User user) {
        System.out.println("Dao: addUser()");
        return 0;
    }

    @Override
    public User getUserById(int id) {
        System.out.println("Dao: getUserById()");
        return null;
    }

    @Override
    public List<User> getAllUsers() {
        System.out.println("Dao: getAllUsers()");
        return Collections.emptyList();
    }

    @Override
    public List<User> getUsersByName(String name) {
        System.out.println("Dao: getUsersByName()");
        return Collections.emptyList();
    }
}

service

IUserService
public interface IUserService {
    void login();

    void register();
}
UserServiceImpl
public class UserServiceImpl implements IUserService {

    /**
     * Each time this interface is called, aenw UserDaoImpl object is created,
     * resulting in resource waste or causing OutOfMemoryError in serious cases.
     */
    IUserDao userDao = new UserDaoImpl();

    @Override
    public void login() {
        userDao.getAllUsers();
        System.out.println("This is implementation o login method.");
    }

    @Override
    public void register() {
        userDao.getAllUsers();
        System.out.println("This is implementation o register method.");
    }
}

假设 Web 应用程序现在运行正常。
每次用户调用 IUserService 接口时,都会创建一个 IUserDaoImpl 对象,这会导致资源浪费,在严重情况下可能会引发 OutOfMemoryError

IUserDao userDao = new UserDaoImpl();

如何解决这个问题****❓

我们可以将 IUserDaoImplClass 作为 key,IUserDaoImpl 实例作为 value 存储在一个HashMap<Class, Object>中。

  1. 在 Web 应用程序运行之前,将 ApplicationConext 类中的对象加载并初始化到 IOC 容器中。
  2. 当调用 IUserService 接口时,Java 会获取 IUserDaoImpl 对象,而不再需要创建新的 IUserDaoImpl对象。

ApplicationContext

public class ApplicationContext {
    // Store bean objects in a Hashmap, equivalent to ioc container.
    private HashMap<Class<?>, Object> beanFactory = new HashMap<>();

    /**
     * init ApplicationContext, and put bean objects into IOC container.
     */
    public void initContext() throws IOException, ClassNotFoundException {
        // Load specified objects to IOC container.
        beanFactory.put(IUserService.class,new UserServiceImpl());
        beanFactory.put(IUserDao.class,new UserDaoImpl());
    }

    /**
     * get bean object by class from IOC container.
     */
    public Object getBean(Class<?> clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * return all bean objects in IOC container.
     */
    public HashMap<Class<?>, Object> getAllBeans() {
        return beanFactory;
    }
}

配置文件初始化 IOC 容器

❓****问题:如上述代码所示,我们可以将指定的对象加载到IOC容器中,但如果我们需要添加一些新对象时,就必须修改源代码,这非常麻烦。

public void initContext() throws IOException, ClassNotFoundException {
    // Load specified objects to IOC container.
    beanFactory.put(IUserService.class,new UserServiceImpl());
    beanFactory.put(IUserDao.class,new UserDaoImpl());
    // Add new objects into beanFactory.
    // beanFactory.put(xxxx.class,new xxxxx());
    // ...........
}

为了解决这个问题,我们可以使用 Java 中的反射。

  1. 创建一个application.properties文件,在其中可以添加我们需要的对象信息。
IOC.service.IUserService = IOC.service.Impl.UserServiceImpl
IOC.dao.IUserDao = IOC.dao.Impl.UserDaoImpl
  1. 利用反射机制加载对象,并存储到 IOC 容器。
public void initContext() throws IOException, ClassNotFoundException {
    // Load application.properties file and Get information of bean object.
    Properties properties = new Properties();
    properties.load(new FileInputStream("src/main/resources/application.properties"));

    Set<Object> keys = properties.keySet();
    for (Object key : keys) {
        Class<?> keyClass = Class.forName(key.toString());

        String value = properties.getProperty(key.toString());
        Class<?> valueClass = Class.forName(value);
        Object instance = valueClass.newInstance();

        // put bean object into IOC container.
        beanFactory.put(keyClass, instance);
    }
}

RunApplication

public class RunApplication {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ApplicationContext applicationContext = new ApplicationContext();
        applicationContext.initContext();

        // Get all beans.
        HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();
        Set<Class<?>> classes = allBeans.keySet();
        for (Class<?> clazz : classes) {
            System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));
        }
    }
}


注解初始化 IOC 容器

@Bean

  1. 新增 annotation 包,创建 Bean 注解类。
@Target(ElementType.TYPE)    // 只能修饰类型元素
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {}
  1. UserDaoImplUserServiceImpl 类上加上 **@Bean **注解。
@Bean
public class UserServiceImpl implements IUserService {}

@Bean
public class UserDaoImpl implements IUserDao {}
  1. ApplicationContext 类中添加使用注解初始化 bean 对象。
// root path of project.
private String filePath;

/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    // Get the project path.
    filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();

    // Load all bean objects into IOC container.
    loadOne(new File(filePath));
}

/**
* Load all class file with @Bean.
*/
public void loadOne(File fileParent) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    if(!fileParent.isDirectory()){
        return;
    }

    File[] childrenFiles = fileParent.listFiles();
    if (childrenFiles == null) {
        return;
    }
    for (File child : childrenFiles) {
        if (child.isDirectory()) {
            loadOne(child);
        } else {
            String pathWithClass = child.getAbsolutePath().substring(filePath.length() - 1);
            
             // Get file name like UserServiceImpl.class
            if (pathWithClass.contains(".class")) {
                String fullName = pathWithClass
                .replaceAll("\\\\", ".")
                .replace(".class", "");

                Class<?> clazz = Class.forName(fullName);
                if (!clazz.isInterface()) {
                    Bean annotation = clazz.getAnnotation(Bean.class);
                    if (annotation != null) {
                        Object instance = clazz.newInstance();

                        Class<?>[] interfacesList = clazz.getInterfaces();
                        // interface as key, if object has no interface.
                        if (interfacesList.length > 0) {
                            Class<?> interfaceClass = interfacesList[0];
                            beanFactory.put(interfaceClass, instance);
                        }
                        // clazz as key, if object has interface.
                        else {
                            beanFactory.put(clazz, instance);
                        }
                    }
                }
            }
        }
    }
}
  1. 修改 RunApplication 启动类中使用注解初始化 IOC 容器。
public class RunApplication {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ApplicationContext applicationContext = new ApplicationContext();
        //applicationContext.initContext();
        // Load beans by @Bean.
        applicationContext.initContextByAnnotation();
        
        // Get all beans.
        HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();
        Set<Class<?>> classes = allBeans.keySet();
        for (Class<?> clazz : classes) {
            System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));
        }
    }
}

输出:

@Autowired

  1. 在 annotation 包中添加 Aurowired 注解类。
@Target(ElementType.FIELD)    // 只能修饰成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
  1. UserServiceImpl 成员变量添加 @Autowired 注解。
@Bean
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;
}
  1. ApplicationContext 类中添加使用注解初始化 bean 对象后,初始化各个 bean 对象的成员变量。
    1. 这里默认只实现了根据类型初始化成员变量(在 springboot 中支持类型名称)。
/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    // Get the project path.
    filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();

    // Load all bean objects into IOC container.
    loadOne(new File(filePath));

    // initiate fields of object.
    assembleObject();
}

/**
* @Autowired annotation to initiate fields of bean objects.
*/
public void assembleObject() throws IllegalAccessException {
    Set<Class<?>> keys = beanFactory.keySet();
    for (Class<?> key : keys) {
        Object value = beanFactory.get(key);
        Class<?> clazz = value.getClass();
    
        // Set value of field by @Autowired.
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field filed : declaredFields) {
            Autowired annotation = filed.getAnnotation(Autowired.class);
            if (annotation != null) {
                filed.setAccessible(true);
    
                // Get bean object by type from IOC container.
                Object object = beanFactory.get(filed.getType());
                filed.set(value, object);
            }
        }
    }
}
  1. 答复
public class RunApplication {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ApplicationContext applicationContext = new ApplicationContext();
        //applicationContext.initContext();
        applicationContext.initContextByAnnotation();
        
        // Get all beans.
        HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();
        Set<Class<?>> classes = allBeans.keySet();
        for (Class<?> clazz : classes) {
            System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));
        }

        // Get bean object by class type.
        IUserService bean = (IUserService) applicationContext.getBean(IUserService.class);
        // call login method.
        bean.login();
    }
}

输出:

Dao 层调用了 getAllUsers() 方法,说明依赖注入成功。

源码:https://github.com/RainbowJier/HandWrittenSpring

Reference

  1. 一堂课深挖java反射机制,手撸一个ioc,实现自动装配,顺便spring也了解了_哔哩哔哩_bilibili

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

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

相关文章

面试八股(自用)

什么是java序列化&#xff0c;什么时候需要序列化? 序列化是指将java对象转化成字节流的过程&#xff0c;反序列化是指将字节流转化成java对象的过程。 当java对象需要在网络上传输 或者 持久化到存储文件中&#xff0c;就需要对java对象进行序列化处理。 JVM的主要组成部分…

Leetcode—1242. 多线程网页爬虫【中等】Plus(多线程)

2024每日刷题&#xff08;187&#xff09; Leetcode—1242. 多线程网页爬虫 实现代码 /*** // This is the HtmlParsers API interface.* // You should not implement it, or speculate about its implementation* class HtmlParser {* public:* vector<string>…

【Java 并发编程】阻塞队列与仿真餐厅

前言 阻塞队列 (BlockingQueue) 顾名思义是一种支持阻塞操作的队列&#xff0c;因为内部机制中添加了 wait 和 notify 方法&#xff0c;所以阻塞队列具备了线程之前相互协调的功能。阻塞队列主要运用于两个场景&#xff0c;一是生产者与消费者模型&#xff0c;二是线程池。本章…

【C语言】循环嵌套:乘法表

循环嵌套&#xff0c;外层循环执行一次&#xff0c;内层循环执行i次。分别控制 在循环的过程中加一层循环。 多层循环属于循环嵌套、嵌套循环 #include <stdio.h> #include <math.h> /* 功能&#xff1a;循环嵌套 乘法表 时间&#xff1a;2024年10月 地点&#xf…

可编辑73页PPT | 企业智慧能源管控平台建设方案

荐言分享&#xff1a;随着全球能源形势的日益紧张和智能化技术的快速发展&#xff0c;企业对于能源的高效利用和精细化管理需求愈发迫切。智慧能源管控平台作为一种集成了物联网、大数据、云计算、人工智能等先进技术的综合管理系统&#xff0c;旨在帮助企业实现能源数据的实时…

【线性回归分析】:基于实验数据的模型构建与可视化

目录 线性回归分析&#xff1a;基于实验数据的模型构建与可视化 1. 数据准备 2. 构建线性回归模型 3. 可视化 数据分析的核心 构建预测模型 应用场景 预测模型中的挑战 结论 线性回归分析&#xff1a;基于实验数据的模型构建与可视化 在数据分析领域&#xff0c;线性…

永恒之蓝漏洞

MS17-010是微软于2017年3月发布的一个安全补丁&#xff0c;旨在修复Windows操作系统中的一个严重漏洞&#xff0c;该漏洞被称为“永恒之蓝”&#xff08;EternalBlue&#xff09;。这个漏洞影响了Windows的Server Message Block&#xff08;SMB&#xff09;协议&#xff0c;允许…

JavaEE-线程安全问题

1.多线程带来的的⻛险-线程安全 1.1 观察线性不安全 // 此处定义⼀个 int 类型的变量 private static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() -> {// 对 count 变量进⾏⾃增 5w 次for (int i 0; i…

基于Java微信小程序的水果销售系统详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

Prometheus运维监控平台之监控指标注册到consul脚本开发、自定义监控项采集配置调试(三)

系列文章目录 运维监控平台搭建 运维监控平台监控标签 golang_Consul代码实现Prometheus监控目标的注册以及动态发现与配置V1版本 文章目录 系列文章目录目的一、监控指标注册到consul的golang脚本开发1、修改settings.yaml文件2、修改config/ocnsul,go文件3、修改core/consul…

视频剪辑和转换gif一体化UI页面【可以解决gif体积过大】

视频剪辑和转换gif一体化UI页面 一&#xff0c;简介 这段代码实现了一个简单的 GUI 应用程序&#xff0c;主要功能包括&#xff1a; 选择视频文件&#xff1a;用户可以通过点击“选择视频”按钮打开文件选择对话框&#xff0c;选择 MP4 格式的视频文件。 转换为 GIF&#xf…

Axure复选框全选反选取消高级交互

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;复选框全选反选取消制作 主要内容&#xff1a;点击复选框&#xff0c;实现列表数据项全选选中、反选和取消选中效果 应用场景&#xff1a;多项选定…

【完-网络安全】Windows防火墙及出入站规则

文章目录 防火墙入站和出站的区别域网络、专用网络、公用网络的区别 防火墙 防火墙默认状态一般是出站允许&#xff0c;入站阻止。 入站和出站的区别 入站就是别人来访问我们的主机&#xff0c;也就是正向shell的操作 出站就是反向shell&#xff0c;主机需要主动连接kali&am…

语音信号去噪 Matlab语音信号去噪,GUI界面。分别添加了正弦噪声和高斯噪声,分别用了巴特沃斯低通滤波器和小波分解去噪。每步处理都可以播放出信号声音。

Matlab语音信号去噪&#xff0c;GUI界面。分别添加了正弦噪声和高斯噪声&#xff0c;分别用了巴特沃斯低通滤波器和小波分解去噪。每步处理都可以播放出信号声音。 具体工作如下&#xff1a; 1、加载语音信号&#xff0c;显示时域频域图&#xff1b; 2、添加正弦噪声&#xff1…

数据结构代码题备考

文章目录 快速排序2011真题2013真题2018真题2016真题 快速排序的划分思想2016快速排序的最优解二路归并排序2011真题 链表备考思路基本功练习 图图的数据定义-邻接矩阵图的数据定义-邻接表2021-邻接矩阵2023-邻接矩阵2021-邻接表2023-邻接表 二叉树常用思路基本功练习前中后序遍…

思想实验思维浅谈

思想实验思维浅谈 思想实验&#xff08;Thought Experiment&#xff09;是一种在思想中进行的假想实验&#xff0c;思想实验激发人的想象力和思辨能力&#xff0c;是科学家思考问题的重要工具&#xff0c;通过想象、推理和分析来探索某种理论、假设或概念的可能性和内在逻辑&am…

项目管理软件真的能让敏捷开发变得更简单吗?

敏捷开发是一种以快速交付和适应变化为核心特点的软件开发方法。其特点包括尽早并持续交付、能够驾驭需求变化、版本周期内尽量不加任务、业务与开发协同工作、以人为核心、团队配置敏捷等。 例如&#xff0c;尽早并持续交付可使用的软件&#xff0c;使客户能够更早地体验产品…

涉密网和非涉密网之间企业如何进行安全跨网文件交换?

在数字化时代&#xff0c;企业面临着跨网文件交换的挑战&#xff0c;尤其是涉密网和非涉密网之间的数据传输。这种交换不仅要求高效&#xff0c;更要求安全&#xff0c;以防止数据泄露和网络攻击。以下是一些关键点&#xff0c;帮助企业实现安全跨网文件交换。 应用场景和重要性…

Linux 命令—— ping、telnet、curl、wget(网络连接相关命令)

文章目录 网络连接相关命令pingtelnetcurlwget 网络连接相关命令 ping ping 命令是用于测试网络连接和诊断网络问题的工具。它通过向目标主机发送 ICMP&#xff08;Internet Control Message Protocol&#xff09;回显请求&#xff0c;并等待回复&#xff0c;以确定目标主机是…

植物大战僵尸杂交版即将新增内容介绍

新BOSS僵尸&#xff1a;埃德加二世 特点&#xff1a;埃德加博士的克隆体&#xff0c;驾驶小型机甲。体型&#xff1a;小于原版僵王的头。血量&#xff1a;120000&#xff0c;是原版僵王复仇的2倍。免疫效果&#xff1a;减速、冰冻、黄油效果&#xff0c;能阻挡子弹。行为模式&…