【1】Spring手写模拟-ApplicationContext获取对象

news2024/10/7 18:20:06

ApplicationContext获取对象

阶段目标

当前阶段任务,实现ApplicationContext 加载配置文件,实现对配置的包扫描,获取其字节码文件,查看是否包含注需要Spring管理,以及实现单例或者多例获取Bean对象

  • 实现配置文件config,在其上添加注解ComponentScan扫描包
  • 创建注解类ComponentComponentScanScope
    • Component 是否交给Spring管理
    • ComponentScan 扫描包的路径,一般在配置文件进行配置
    • Scope 单例还是多例获取对象
  • 创建BeanDefinition 这个类来辅助保存需要Spring管理的Bean信息
    • 单例 多例
    • Class

当前框架

我们先完善流程的一个大致框架,之后往里面填充内容

Test.Java

主要是要实现获取User 类啦

public class Test {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new ApplicationContext(Config.class);
        Useruser= (User) applicationContext.getBean("User");
        
    }
}

Config.java@ComponentScanComponentScope 注解

  • @Retention(RetentionPolicy.RUNTIME) 指定了注解的保留期为运行时。这意味着,注解的信息将被保留到程序运行时,并且可以通过Java反射API在运行时获取和处理注解信息。
  • @Target(ElementType.TYPE)指定了注解的目标元素类型为类、接口或枚举类型。这意味着,该注解只能应用于类、接口或枚举类型的声明上,不能应用于方法、字段或其他类型的声明上
@ComponentScan("com.yqyang.service")
public class Conifg {
}


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

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



@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "singleton";
}




ApplicationContext

这里需要注意我们用Class 来修饰配置类

package com.yqyang.spring;

import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Enumeration;

/*
*
* Spring
*  */
public class ApplicationContext {
    private Class conifgClass;

    public ApplicationContext(Class conifgClass) {
        this.conifgClass = conifgClass;
        // 扫描--->BeanDefinition-->beanDefinitionMap
    
    } 

    public void getBean(String user) {
    }
}

完善ApplicationContext获取Bean对象

当前的流程

  • 根据配置类ComponentScan 注解获取要加载的包

  • 获取包的字节码文件绝对路径

  • 加载包的各个文件,查看是否包含Component

    包含,继续查看是否包含Scope 及其值为单例还是多例,获取类信息保存到BeanDefinition ,使用beanDefinitionMap 用来保存beanName及其对应的BeanDefinition 信息

  • 最后懒汉式加载单例的对象到ConcurrentHashMap<String, Object> singletonObjects

    如果是单例,获取其对象时,是提前创建好的

    如果是多例,获取其对象时,是刚刚创建的


  • 我们同时需要创建BeanDefinition 这个类来辅助保存信息
    • 单例 多例
    • Class
  • beanDefinitionMap 用来保存beanName及其对应的BeanDefinition 信息
    • key<String>
    • value<BeanDefinition>
package com.yqyang.spring;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/*
*
* Spring
*  */
public class ApplicationContext {
    private Class conifgClass; // 加载配置类
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();


    public ApplicationContext(Class conifgClass) {
        this.conifgClass = conifgClass;

        // 包 -> 包字节码路径 -> 加载class -> 是否包含注解component ——
        if (conifgClass.isAnnotationPresent(ComponentScan.class)){
            ComponentScan componentScan = (ComponentScan) conifgClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value(); // com.yqyang.service

            path = path.replace(".","/"); // com/yqyang/service


            ClassLoader classLoader = ApplicationContext.class.getClassLoader();   // 通过ClassLoader获取编译后字节码绝对路径,以便加载
            URL resource = classLoader.getResource(path);
            File file=new File(resource.getFile()); /// D:/WorkSpace/JavaWorkSpace/spring-yqyang/target/classes/com/yqyang/service
            if (file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fName = f.getAbsolutePath();
                    if(fName.endsWith(".class")){ // 我们只需要字节码文件

                        String className = fName.substring(fName.indexOf("com"), fName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            Class<?> clazz = classLoader.loadClass(className);
                            if(clazz.isAnnotationPresent(Component.class)){
                                Component component = clazz.getAnnotation(Component.class);// 获得注解value值 bean规定的名称
                                String beanName = component.value();

                                // beanName == 空 省略

                                BeanDefinition beanDefinition=new BeanDefinition();
                                beanDefinition.setType(clazz);

                                if(clazz.isAnnotationPresent(Scope.class)){
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);




                            }
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }

                    }


                }
            }

            // 获取 bean 单例对象
            for (String beanName : beanDefinitionMap.keySet()) {
                BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
                if (beanDefinition.getScope().equals("singleton")){
                    Object o = creatBean(beanDefinition.getType());
                    singletonObjects.put(beanName, o);
                }
            }


        }
    }

    private Object creatBean(Class clazz) {
        try {
            Object instance = clazz.getConstructor().newInstance();
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")){
            Object bean = singletonObjects.get(beanName);
            if (bean==null){
                Object o = creatBean(beanDefinition.getType());
                singletonObjects.put(beanName, o);
            }
            return bean;
        }else {
            return creatBean(beanDefinition.getType());
        }

    }
}

package com.yqyang.spring;

public class BeanDefinition {
    private Class type;   //
    private String scope; // 单例多例

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

测试

单例:

在这里插入图片描述

多例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILdA6OMO-1689235265472)(https://secure2.wostatic.cn/static/2riaLfRtgMaoZ2TwWE4kDY/image.png?auth_key=1689234689-8GbA3uYTkzo2T5xuybZMn8-0-d5af610d7cce60ff83ee1f7d26ba7261)]

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

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

相关文章

实现虚拟机(VM15.5.0)与本机相互通信

目录 1、如果虚拟机一打开请先关机 2、进入“控制面板”-“网络和Internet”-“网络连接”&#xff0c;可以看到有两个虚拟网口&#xff0c;关闭虚拟网口VMnet1和VMnet8&#xff0c;如图所示&#xff1a; 3、选择需要通信的虚拟机&#xff0c;点击“编辑虚拟机设置”&#x…

力扣刷题 - 数组篇

这里写目录标题 数组的遍历485495414628 统计数组中的元素64569744844241数组的改变与移动453665---283 二维数组以及滚动数组118119661419--- 数组的旋转189396 特定顺序遍历二维数组5459498 二维数组的变换5664873--- 前缀和数组303238506 数组的遍历 485 https://leetcode.…

【HCIA】07.OSPF

动态路由的协议分类 按工作区域分&#xff1a; IGP&#xff08;内部网关协议 interior gateway protocols&#xff09;&#xff1a;OSPF、IS-IS、RIPEGP&#xff08;外部网关协议 exterior gateway protocols&#xff09;&#xff1a;BGP 按工作机制及算法分类&#xff1a; 距离…

机器学习(12)--K-Means

目录 一、聚类 二、机器学习中的距离 三、sklearn中的聚类算法 四、KMeans簇内平方和和时间复杂度 五、sklearn中的KMeans 1、建立一个数据集 2、使用K-Means进行聚类操作 3、根据上面的模型绘散点图观察分类效果。 4、评估指标 4.1对于真实标签已知情况 4.2当真实标…

[RocketMQ] Broker 消息重放服务源码解析 (十三)

构建消息文件ConsumeQueue和IndexFile。 ConsumeQueue: 看作是CommitLog的消息偏移量索引文件, 存储了它所属Topic的消息在Commit Log中的偏移量。消费者拉取消息的时候, 可以从Consume Queue中快速的根据偏移量定位消息在Commit Log中的位置。IndexFile索引文件: 看作是Commi…

【bash:xxx:command not found问题,在英伟达nvidia的jetson-orin-nx上遇到的>>>解决方式之一】

【bash:xxx:command not found问题,在英伟达nvidia的jetson-orin-nx上遇到的>>>解决方式之一】 1、概述2、实验环境3、问题描述&#xff1a;bash:xxx:command not found问题4、我的努力第一种方式&#xff1a;加入指令方式第二种方式&#xff1a;使用echo $PATH命令查…

设计模式(七)-----桥接模式(Bridge Pattern)

目录 什么是桥接模式优点缺点应用场景 基本结构业务场景不使用模式的解决方案实现发送普通消息实现发送加急消息实现发送特急消息添加发送手机消息的处理方式 使用桥梁模式来解决问题 什么是桥接模式 将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变…

Es直方图聚合--date_histogram

文章目录 1、背景2、bucket_key如何计算3、前置知识4、日历和固定时间间隔 4.1 Calendar intervals 日历间隔4.2 Fixed intervals 固定间隔 5、数据准备 5.1 准备mapping5.2 准备数据 6、聚合案例 6.1 dsl6.2 java代码6.3 聚合结果 7、完整代码8、参考文档 1、背景 此处来简单学…

云计算UPS监控,怎么办?

在大型数据机房中&#xff0c;UPS系统扮演着关键的角色&#xff0c;为计算机和网络设备提供可靠的电力备份。由于数据机房的规模庞大且关键性强&#xff0c;监控UPS系统的可靠性和效率至关重要。 UPS监控可以提供实时的电池状态、负载信息、电网电压等监测数据&#xff0c;并能…

c++中assert

参考:https://blog.csdn.net/bitcarmanlee/article/details/124283683 1.什么是assert assert&#xff0c;中文翻译为断言&#xff0c;注意是一个宏定义&#xff0c;不是函数。 c中&#xff0c;要使用assert&#xff0c;可以将cassert头文件include进来&#xff0c;而cassert最…

路径规划算法:基于孔雀优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于孔雀优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于孔雀优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法孔雀…

【HCIA】09.STP

STP的选举之发波原理 设备启动之后&#xff0c;经过选举会分别显示出它们的等级&#xff08;最强&#xff0c;次强&#xff0c;最弱&#xff09;选出等级之后&#xff0c;两两设备开始互相发波&#xff0c;等级强的设备会将弱的设备的光顶回去此时两两设备之间的波就是单方向的…

家政服务小程序软件解决方案

家政服务小程序软件是近年来随着人们对家政服务需求的增长而逐渐兴起的一种数字化服务解决方案。通过小程序软件&#xff0c;用户可以轻松预约家政服务&#xff0c;包括保姆、月嫂、钟点工等&#xff0c;而且价格透明、服务规范&#xff0c;大大提高了用户对家政服务的满意度。…

神经网络结构可视化-netron

网址&#xff1a;https://netron.app/ 点选择模型&#xff0c;将oonx文件拉到netron界面&#xff0c;即可 输出; 如何将pytorch模型转换为onnx的格式&#xff1f; 在测试&#xff08;训练好的模型&#xff09;里输入代码 to_onnx(model, 3, 28, 28, output/params.onnx)其…

【Linux】十分钟理解动静态库

目录 一 前置概念二 静态库2.12.22.3放入指定路径2.4 第三方库的使用 四 动态库3.1 环境变量3.2 软链接方案3.3 配置文件方案 一 前置概念 我们在VS2022下安装开发环境实际上就是安装编译器软件、安装要开发的语言配套的库和头文件。我们使用编译器有语法的自动提醒功能&#…

python常用库之colorama (python命令行界面打印怎么加颜色)

文章目录 python常用库之colorama (python命令行界面打印怎么加颜色)背景colorama介绍colorama使用colorama打印红色闪烁打印颜色组合 python常用库之colorama (python命令行界面打印怎么加颜色) 背景 在Python开发项目过程中&#xff0c;为了方便调试代码&#xff0c;经常会…

Java中Map中10w条数据用什么循环性能最好呢?

加油&#xff0c;新时代打工人&#xff01; 1、java中List集合三种获取集合元素方式 2、Java中Map使用增强for循环和迭代器获取key和value 选择合适的循环方式&#xff0c;让性能最优&#xff01; public class Test2 {public static void main(String[] args) {//初始化 10w…

vue upload 上传下载

目录 上传 下载 对象/文件流 download处理返回 文件流 axios.post 封装axios 1.请求设置类型responseType: blob 2.若有请求拦截(直接返回即可) 3.download 4.请求下载 相关基础 blob MIME vue 实现文件上传、下载的方法 - 掘金 上传 submitAddFile(){var form…

基础算法-【离散化】

离散化的本质&#xff1a;是建立了一段数列到自然数之间的映射关系&#xff08;value -> index)&#xff0c;通过建立新索引&#xff0c;来缩小目标区间&#xff0c;使得可以进行一系列连续数组可以进行的操作比如二分&#xff0c;前缀和等… 相应的算法模板&#xff1a; v…

【Linux】—— 进程地址空间

序言&#xff1a; 在上篇中&#xff0c;我们讲解了关于进程优先级的概念。本期&#xff0c;我将给大家介绍的是关于进程地址空间的话题。 目录 &#xff08;一&#xff09;程序地址空间回顾 &#xff08;二&#xff09;代码演示 &#xff08;三&#xff09;进程地址空间的引…