手写模拟Spring的基本功能

news2024/11/24 18:32:55

文章目录

  • 1. Spring的基本功能
  • 2. 容器启动+ 容器启动,即创建容器对象并赋予配置对象
  • 3. BeanDefinition扫描
  • 4. Bean的生命周期
  • 5. 单例Bean与多例Bean
  • 6. 依赖注入
  • 7. AOP
  • 8. Aware 回调
  • 9. 初始化
  • 10. BeanPostProcessor
  • 附录:

1. Spring的基本功能

在这里插入图片描述

2. 容器启动+ 容器启动,即创建容器对象并赋予配置对象

// 自定义的 IOC 容器对象
GyhApplicationContext gyhApplicationContext =
                new GyhApplicationContext(AppConfig.class);

3. BeanDefinition扫描

  • 对于基于注解的方式,会在构造方法中扫描 @ConponentScan 的 value 值对应其下的所有类对应的 .class 文件(存在于 out 文件夹下)
  • 将 扫描到的bean信息(Class, scope)封装至 BeanDefinition 对象中并由 一个 Map (beanDefinitionMap) 保存 beanName ---> BeanDefinition

4. Bean的生命周期

在这里插入图片描述

createBean 中:

  1. 利用 Bean 的空构造器通过反射创建
  2. 设置对象属性(伴随着依赖注入)
  3. 执行前置处理器
  4. 执行初始化方法
  5. 执行后置处理器
  6. 调用使用
  7. 执行 destory 方法

5. 单例Bean与多例Bean

  • 对于 单例Bean 是在初始化容器时(扫描过程中)创建并由 一个 Map(singletonObjects)管理 beanName —> beanInstance
  • 对于 多例Bean 是在 getBean() 方法中实时创建的

6. 依赖注入

  • 在 createBean 方法中,扫描所有 bean 被 @Autowire 标注的属性为其注入容器中的 bean
  • 若当前容器中还没有所依赖bean,则会创建该bean,然后再注入

7. AOP

  • spring是基于 动态代理
  • 初始化后,通过动态代理生成实现了指定接口的代理类
  • 使用了AOP对应的 bean 会被代理类的对象所替换

8. Aware 回调

  • 对应自定义的bean的类实现了Spring中指定的 Aware接口。可以在创建 bean 的过程中被检索到,并在定制的顺序中执行 Aware接口中定义的方法(即调用bean中实现的Aware接口的方法)

9. 初始化

  • 是spring的Aware接口中的一种,spring会检索实现了 InitializationBean 接口的 Bean。在创建这些bean是会调用其实现的 afterPropertiesSet 方法

10. BeanPostProcessor

  • 后置处理器,也是Aware回调接口中的一种,其定义的前置与后置方法会被用在所有bean的创建过程中

附录:

GyhApplicationContext

package com.gyh.spring;

import com.gyh.service.UserService;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Gao YongHao
 * @version 1.0
 * 模拟Spring的javaConfig的容器启动方式
 */

public class GyhApplicationContext {
    private Class<?> configClass;

    // 用于保存Bean对象的信息(class 对象, 作用域信息)
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();

    // 用于保存单例的对象
    private ConcurrentHashMap<String, Object> singletonObjects =
            new ConcurrentHashMap<>();

    // 用于保存 BeanPostProcessor 的集合
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public GyhApplicationContext(Class<?> configClass) {
        this.configClass = configClass;

        // 扫描,单例的对象被直接创建,多例的对象在使用时创建
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan annotation = configClass.getAnnotation(ComponentScan.class);
            String packagePath = annotation.value(); // 扫描路径 "com.gyh.service"

            packagePath = packagePath.replace(".", "/");

            // 在 out 文件夹中定位 .class 文件的路径
            ClassLoader classLoader = GyhApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(packagePath);
            File file = new File(resource.getFile());
//            System.out.println(file);
            // 如果是文件夹
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    String absolutePath = f.getAbsolutePath();
                    // 筛选出 .class 结尾的字节码文件
                    if (absolutePath.endsWith(".class")) {
                        String loadClassPath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                        loadClassPath = loadClassPath.replace("\\", ".");
                        // loadClassPath == "com\gyh\service\UserService"
//                        System.out.println(loadClassPath);
                        try {
                            // 利用类加载器加载指定文件夹下的 .class 文件
                            Class<?> aClass = classLoader.loadClass(loadClassPath);
                            // 查看是否有标注 Bean 对应的的注解
                            if (aClass.isAnnotationPresent(Component.class)) {
                                // 扫描出实现了 BeanPostProcessor 的 bean,将其实例化对象放置于 集合 中
                                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                    beanPostProcessors.add((BeanPostProcessor) aClass.newInstance());
                                }

                                // 设置的Bean名称(若为""或null)则设置为首字母小写的类名
                                String beanName = aClass.getAnnotation(Component.class).value();
                                if ("".equals(beanName)) {
                                    beanName = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                                }

                                // 创建保存Bean信息的对象
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(aClass); // 该对象类型

                                if (aClass.isAnnotationPresent(Scope.class)) {
                                    Scope annotation1 = aClass.getAnnotation(Scope.class);
                                    String value = annotation1.value();
                                    beanDefinition.setScope(value);
                                } else { // 默认是单例的
                                    beanDefinition.setScope("Singleton");
                                }

                                // 放置于 beanDefinitionMap 中管理
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }

                            // 填充单例对象
                            for (Map.Entry<String, BeanDefinition> s : beanDefinitionMap.entrySet()) {
                                BeanDefinition value = s.getValue();
                                if (value.getScope().equals("Singleton")) {
                                    // 存有依赖注入的单例对象生成
                                    singletonObjects.put(s.getKey(), createBean(s.getKey(), value));
                                }
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
    }

    /**
     * 创建Bean对象,
     *
     * @param beanName       bean 对象名称
     * @param beanDefinition bean 对象的信息
     * @return
     */
    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> type = beanDefinition.getType();
        Object o = null;
        try {
            o = type.getConstructor().newInstance();

            // 依赖注入
            // 遍历属性对属性进行依赖注入
            for (Field f : type.getDeclaredFields()) {
                if (f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true); // 关闭访问权限
                    // 将容器中管理的bean注入到该属性中(可能先于当前类创建,也可能后于当前类创建)
                    f.set(o, getBean(beanName, type));
                }
            }

            // 回调机制,查看当前的Bean是否实现了指定的接口
            // 如果实现则执行相关方法(BeanPostProcessor即是如此实现)
            if (o instanceof BeanNameAware) {
                ((BeanNameAware) o).setBeanName(beanName);
            }
            for(BeanPostProcessor b:beanPostProcessors){
                b.beforePostProcess(beanName, o);
            }

            // 初始化也使用回调实现

            // 基于AOP会在后置处理器中创建 bean 的动态代理对象,并封装切面逻辑
            for(BeanPostProcessor b:beanPostProcessors){
                b.afterPostProcess(beanName, o);
            }


        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return o;
    }

    /**
     * 用于返回 Bean 对象的方法
     *
     * @param beanName Bean 的名称
     * @param clazz    Bean 对应的 Class 对象
     * @param <T>
     * @return
     */
    public <T> T getBean(String beanName, Class<T> clazz) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            String scope = beanDefinition.getScope();
            if (scope.equals("Singleton")) {
                // 从单例池中获取对象
                Object o = singletonObjects.get(beanName);

                if (o == null) { // 对应依赖注入时可能会用到
                    o = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, o); // 放入单例池中
                }
                return (T) o;

            } else { // 多例则实时创建
                Object o = null;
                try {
                    o = beanDefinition.getType().newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return (T) o;
            }
        }
    }
}

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

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

相关文章

【鸡翅Club】项目启动

一、项目背景 这是一个 C端的社区项目&#xff0c;有博客、交流&#xff0c;面试学习&#xff0c;练题等模块。 项目的背景主要是我们想要通过面试题的分类&#xff0c;难度&#xff0c;打标&#xff0c;来评估员工的技术能力。同时在我们公司招聘季的时候&#xff0c;极大的…

mingw64的Windows安装及配置教程gcc、g++等

mingw64.rar 链接&#xff1a;https://pan.baidu.com/s/18YrDRyi5NHtqnTwhJG6PuA 提取码&#xff1a;pbli &#xff08;免费永久有效&#xff0c;免安装&#xff0c;解压后配置环境变量即可使用&#xff09; 1 下载 解压后随便放到一个地方&#xff1a; 复制“bin”路径&am…

SpringCloudAlibaba[Nacos]注册配置中心注册与发现服务

Nacos的全称是Dynamic Naming and Configuration Service&#xff0c;Na为naming/nameServer即注册中心,co为configuration即注册中心&#xff0c;service是指该注册/配置中心都是以服务为核心。是阿里巴巴开源易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nac…

安装vue发生异常:npm ERR! the command again as root/Administrator.

一、异常 npm ERR! The operation was rejected by your operating system. npm ERR! Its possible that the file was already in use (by a text editor or antivirus), npm ERR! or that you lack permissions to access it. npm ERR! npm ERR! If you believe this might b…

【LeetCode:910. 最小差值 II + 模拟 + 思维】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

html,css,js实现Upload 上传

实现效果&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Docum…

基于Multisim的汽车尾灯控制电路设计与仿真

假设汽车尾部左右量测各有3个指示灯&#xff08;用发光二极管模拟&#xff09;1. 汽车正常运行时指示灯全灭&#xff1b;2.右转弯时&#xff0c;右侧3个指示灯按右循环顺序点亮&#xff1b;.3. 左转弯时&#xff0c;左侧3个指示灯按左循环顺序点亮&#xff1b;4.临时刹车时所有…

【C语言刷力扣】910.最小差值 ||

题目&#xff1a; 解题思路; 由于需要对数组中的每一个进行 k 或 -k 的操作&#xff0c;这时要求最小差值&#xff0c;即对较大的数 -k 对较小的数 k。 需要先对数组进行排序&#xff0c;再枚举对元素的操作。 对 nums[0] 到 nums[i - 1] 的数进行 k对 nums[i] 到 nums[numsS…

Java笔试05

在Java中&#xff0c;final 关键字可以用于修饰类、方法和变量&#xff1a; 修饰类&#xff1a; 被 final 修饰的类不能被继承&#xff0c;即不能有子类。这通常用于工具类或者不希望被扩展的类。 修饰方法&#xff1a; 被 final 修饰的方法不能被子类覆盖。这通常用于确保类…

Yolo目标检测:实时性与准确性的完美结合

在目标检测领域&#xff0c;Yolo&#xff08;You Only Look Once&#xff09;算法无疑是一颗璀璨的明星。自2016年由Joseph Redmon等人提出以来&#xff0c;Yolo凭借其出色的实时性和准确性&#xff0c;迅速在多个应用场景中崭露头角。本文将详细介绍Yolo目标检测的基本原理、优…

Qt学习笔记第21到30讲

第21讲 new/delete关键字 new关键字 在 C 中&#xff0c; new 关键字用于动态分配内存。它是 C 中处理动态内存分配的主要工具之一&#xff0c;允许在程序运行时根据需要分配内存。 用法 ①分配单个对象&#xff1a;使用 new 可以在堆上动态分配一个对象。例如&#xf…

面向对象进阶(上)(JAVA笔记第二十二期)

p.s.这是萌新自己自学总结的笔记&#xff0c;如果想学习得更透彻的话还是请去看大佬的讲解 目录 static修饰符静态变量静态方法 工具类工具类的使用例子第一题第二题 static注意事项继承关系建立继承关系的格式继承的好处及使用场景继承的特点继承体系的设计继承中类的三大要素…

JavaWeb 22.Node.js_简介和安装

有时候&#xff0c;后退原来是向前 —— 24.10.7 一、什么是Node.js Node.js 是一个于 Chrome V8 的 JavaScript 运行时环境&#xff0c;可以使 JavaScript 运行在服务器端。使用 Node.js&#xff0c;可以方便地开发服务器端应用程序&#xff0c;如 Web 应用、API、后端服务&a…

使用Three.js和Force-Directed Graph实现3D知识图谱可视化

先看样式&#xff1a; 在当今信息爆炸的时代&#xff0c;如何有效地组织和展示复杂的知识结构成为一个重要的挑战。3D知识图谱可视化是一种直观、交互性强的方式来呈现知识之间的关系。本文将详细介绍如何使用HTML、JavaScript、Three.js和Force-Directed Graph库来实现一个交互…

基于SpringBoot+Vue+uniapp微信小程序的社区门诊管理系统的详细设计和实现(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

解决:YOLOv8训练数据集时P、R、mAP等值均为0的问题

文章目录 问题解决1.匹配pytorch与cuda的版本2.使用 Adam 优化器3.加大训练轮数epoch4. 删除data/labels下的train.cache和val.cache 问题 使用YOLOv8训练自己的数据集时&#xff0c;出现P、R、mAP等值均为0的问题 Model summary (fused): 186 layers, 2,685,733 parameters, …

【 Git 】git push 出现报错 fatal: Could not read from remote repository.

git push 出现报错 fatal: Could not read from remote repository. 问题描述解决方案 问题描述 Connection closed by 198.18.0.xx port xx fatal: Could not read from remote repository.Please make sure you have the correct access rights and the repository exists.解…

【JavaScript】Javascript基础Day04:函数

Javascript——Day04 01. 函数的基本使用02. 函数的参数以及默认参数03. 匿名函数之函数表达式04. 逻辑中断05. 转换为布尔型 01. 函数的基本使用 02. 函数的参数以及默认参数 注意&#xff1a; 03. 匿名函数之函数表达式 函数&#xff1a;匿名函数/具名函数 为什么这个外部…

【wpf】07 后端验证及令牌码获取步骤

由于在用wpf开发应用程序时&#xff0c;从后端获取数据需要用到 Authorization 授权的Bearer令牌&#xff0c;而这个令牌的获取需要登录后台进行获取&#xff0c;这里登录时还涉及到的验证码的操作&#xff0c;所以在获取过程中&#xff0c;需要对后台系统进行登录并拿到这个Be…

【天池比赛】【零基础入门金融风控 Task2赛题理解】【2.3.6】

【天池比赛】【零基础入门金融风控 Task2赛题理解】【2.3.1-2.3.5】 2.3.6 变量分布可视化 2.3.6.1 单一变量分布可视化 对于 pandas.core.series.Series 类型的变量&#xff1a; index&#xff1a;含义&#xff1a;它表示 Series 对象的索引&#xff0c;也就是每个数据点对…