简单实现Spring容器(二) 封装BeanDefinition对象放入Map

阶段2:

 // 1.编写自己的Spring容器,实现扫描包,得到bean的class对象.
 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map.

思路:

1.将 bean 信息封装到 BeanDefinition对象中,再将其放入到BeanDefinitionMap集合中,集合的结构大概是
key[beanName]–value[beanDefintion]
key--------->对应指定的名字,未指定则以类的首字母小写为其名字
value------->对应封装好的BeanDefintion对象

2.因为bean的作用域可能是singleton,也可能是prototype,所以Spring需要扫描到bean信息,保存到集合,这样当getBean()根据实际情况处理.

具体实现

1.加一个自定义Scope注解

package com.elf.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 45~
 * @version 1.0
 * Scope 可以指定一个Bean的作用范围[singleton,prototype]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    //通过value可以指定singleton,prototype
    String value() default "";
}

2.在MonsterService.java上加上@Scope多实例注解

package com.elf.spring.component;
import com.elf.spring.annotation.Component;
import com.elf.spring.annotation.Scope;

/**
 * @author 45~
 * @version 1.0
 * 说明 MonsterService 是一个Servic
 */
@Component //把MonsterService注入到我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {

}

3.准备ioc包下写一个BeanDefinition.java 用于封装记录Bean信息.

package com.elf.spring.ioc;

/**
 * @author 45~
 * @version 1.0
 * BeanDefinition 用于封装和记录Bean的信息 [1.scope 2.存放bean对应的Class对象,反射可以生成对应的对象]
 *  2:因为将来getBean()时有可能是多实例,有可能是动态生成的,还要存放bean的class对象
 */
public class BeanDefinition {
    private String scope;
    private Class clazz;//存放bean的class对象

    public String getScope() {
        return scope;
    }

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

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

3.pom.xml文件引入jar包下的工具类commons-lang,完成首字母小写的功能.而不用springframework自带的StringUtils工具类
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.elf</groupId>
    <artifactId>elf-myspring1207</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

</project>

4.容器文件,把构造器里边的方法抽取出来封装成一个方法,直接在构造器中调用,使代码简洁.
这里完成生成BeanDefinition对象并放入到Map里面

添加内容1:
//定义属性BeanDefinitionMap -> 存放BeanDefinition对象(多例对象)
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private ConcurrentHashMap<String,Object> singletonObjects =
            new ConcurrentHashMap<>();
添加内容2:
//先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if("".equals(beanName)){//如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)){
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotatiion.value());
                            }else{
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }else {
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }

容器文件

package com.elf.spring.ioc;

import com.elf.spring.annotation.*;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author 45~
 * @version 1.0
 */
public class ElfSpringApplicationContext {
    //第一步,扫描包,得到bean的class对象,排除包下不是bean的,因此还没有放到容器中
    //因为现在写的spring容器比原先的基于注解的,要更加完善,所以不会直接把它放在ConcurrentHashMap
    private Class configClass;

    //定义属性BeanDefinitionMap -> 存放BeanDefinition对象
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private ConcurrentHashMap<String,Object> singletonObjects =
            new ConcurrentHashMap<>();

    //构造器
    public ElfSpringApplicationContext(Class configClass) {
        beanDefinitionScan(configClass);//调用封装方法,简洁
        System.out.println("beanDefinitionMap=" + beanDefinitionMap);

    }//构造器结束
    //该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入到Map中
    public void beanDefinitionScan(Class configClass){
        this.configClass = configClass;

        /**获取要扫描的包:
         1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")
         2.通过 @ComponentScan的value => 即要扫描的包 **/
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("要扫描的包path=" + path);

        /**
         * 得到要扫描包下的所有资源(类.class)
         * 1.得到类的加载器 -> APP类加载器是可以拿到 target目录下的classes所有文件的
         * 2.通过类的加载器获取到要扫描的包的资源url => 类似一个路径
         * 3.将要加载的资源(.class)路径下的文件进行遍历 => io
         */
        ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();
        path = path.replace(".", "/"); // 把.替换成 /
        URL resource = classLoader.getResource(path);
        System.out.println("resource=" + resource);

        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) { //把所有的文件都取出来
                System.out.println("============================");
                System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());
                String fileAbsolutePath = f.getAbsolutePath();//到target的classes目录下了

                //这里只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {
                    //1.获取类名
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("classFullName=" + classFullName);
                    //3.判断该类是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....
                    try {
                        Class<?> cla = classLoader.loadClass(classFullName);
                        if (cla.isAnnotationPresent(Component.class) ||
                                cla.isAnnotationPresent(Controller.class) ||
                                cla.isAnnotationPresent(Service.class) ||
                                cla.isAnnotationPresent(Repository.class)) {
                            //演示机制
                            //如果该类使用了@Component注解,说明是一个Spring bean
                            System.out.println("这是一个Spring bean=" + cla + " 类名=" + className);

                            //先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if("".equals(beanName)){//如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)){
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotatiion.value());
                            }else{
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }else {
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }//遍历文件for循环结束
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        }
    }

    //编写放法返回容器中的对象
    public Object getBean(String name) {
        return null;
    }
}

运行结果

beanDefinitionMap={
monsterService=BeanDefinition{scope=‘prototype’, clazz=class com.elf.spring.component.MonsterService},

monsterDao=BeanDefinition{scope=‘singleton’, clazz=class com.elf.spring.component.MonsterDao}
}
ok
在这里插入图片描述
这里存在一个问题:单例多例对象都是放在beanDefinitionMap, singletonObjects里没有单例对象.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【图对比学习】GACN:使用对抗网络增强图对比学习

#论文题目&#xff1a;Graph Contrastive Learning with Generative Adversarial Network&#xff08;使用对抗网络增强图对比学习&#xff09; #论文地址&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3580305.3599370 #论文源码开源地址&#xff1a;暂无 #论文所属会议&am…

如何公网访问内网的群晖NAS随时随地远程访问本地存储的学习资源

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

超卓航科引领冷喷涂增材制造革新,推动先进核反应堆发展

近日&#xff0c;超卓航科凭借其卓越的冷喷涂增材制造技术&#xff0c;成为推动核能领域创新的重要力量。该公司利用冷喷涂工程技术&#xff0c;或为核反应堆的制造和修复开辟了全新的道路。 冷喷涂技术是一种颇具前景的固态粉末沉积方法&#xff0c;可用于涂层制造、增材制造和…

【日志技术】附Logback入门教程

文章目录 日志概论日志的体系Logback快速入门日志配置文件配置日志级别 日志概论 什么是日志&#xff1f;其实可以通过下面几个问题来了解的。 系统系统能记住某些数据被谁操作&#xff0c;比如被谁删除了&#xff1f;想分析用户浏览系统的具体情况&#xff0c;比如挖掘用户的…

脱碱软化树脂Tulsimer CXO-5 MP 高盐水除钙镁树脂

一、产品介绍 Tulsimer CXO-5 MP 是一款大孔弱酸性丙烯酸系阳离子交换树脂&#xff0c;能除去水中的碱度和硬度&#xff0c;特别是除去水中的碳酸氢盐、碳酸盐及其它碱性盐类&#xff0c;适合运用于纯水 ,脱碱软化及选择性的去除重金属。适合在宽广的 pH 及温度范围情况下操作…

数据结构之单链表(不带头单向非循环链表)

一.引言 上一节我们学过了顺序表&#xff0c;那么我们想想顺序表有没有问题呢&#xff1f;我们来讨论顺序表的问题及思考。 顺序表问题&#xff1a; 1.中间/头部的插入删除&#xff0c;时间复杂度为O(N) 2. 增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间。会…

Go语言基础知识学习(一)

Go基本数据类型 bool bool型值可以为true或者false,例子&#xff1a; var b bool true数值型 类型表示范围int8有符号8位整型-128 ~ 127int16有符号16位整型-32768 ~ 32767int32有符号32位整型-2147783648 ~ 2147483647int64有符号64位整型uint8无符号8位整型0 ~ 255uint16…

使用CLion进行cuda编程,并使用cuda-gdb对核函数进行debug,这可能是全网你能够找到的最详细的CLion和cuda编程环境配置教程了

文章目录 前言一、环境准备二、相关学习资料三、环境配置1.新建Clion C Executable项目2.在Clion中的ToolChains中配置cuda-gdb3.配置CMake options4.配置CMakeLists.txt(1) Failed to compute shorthash for libnvrtc.so(2) c: error: unrecognized command-line option -G(3)…

【华为数据之道学习笔记】3-2 基础数据治理

基础数据用于对其他数据进行分类&#xff0c;在业界也称作参考数据。基础数据通常是静态的&#xff08;如国家、币种&#xff09;&#xff0c;一般在业务事件发生之前就已经预先定义。它的可选值数量有限&#xff0c;可以用作业务或IT的开关和判断条件。当基础数据的取值发生变…

AI PC行业深度研究报告:AI PC革新端侧AI交互体验

今天分享的人工智能系列深度研究报告&#xff1a;《AI PC行业深度研究报告&#xff1a;AI PC革新端侧AI交互体验》。 &#xff08;报告出品方&#xff1a;华创证券&#xff09; 报告共计&#xff1a;28页 点击添加图片描述&#xff08;最多60个字&#xff09;编辑 一、硬件端…

mybatis多表映射-对多关联

1、建库建表 create database mybatis-example; use mybatis-example; create table t_book (bid varchar(20) primary key,bname varchar(20),stuid varchar(20) ); insert into t_book values(b001,Java,s001); insert into t_book values(b002,Python,s002); insert into …

【SpringBoot教程】SpringBoot 实现前后端分离的跨域访问(CORS)

作者简介&#xff1a;大家好&#xff0c;我是撸代码的羊驼&#xff0c;前阿里巴巴架构师&#xff0c;现某互联网公司CTO 联系v&#xff1a;sulny_ann&#xff08;17362204968&#xff09;&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗…

xtts和ogg不选择?

不选择ogg的理由&#xff1a; 1.需要在源端创建用户赋权&#xff0c;启用数据库最小日志&#xff0c;附加日志等操作--对生产影响较大 2.外键约束过多&#xff0c;割接启用可能很慢https://www.modb.pro/db/201126--割接停机时间影响 3.初始化配置expdp导出可能快照过旧&#x…

基于Java swing的医院信息管理系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

秒级监控、精准迅速:全面保障业务可用性 | 开源日报 No.101

louislam/uptime-kuma Stars: 41.1k License: MIT Uptime Kuma 是一个易于使用的自托管监控工具&#xff0c;主要功能和核心优势包括&#xff1a; 监控 HTTP(s) / TCP / HTTP(s) 关键词 / HTTP(s) Json 查询 / Ping / DNS 记录等服务的可用性提供时尚、响应迅速且良好用户体验…

docker配置连接harbor私有仓库

一、前言 以下分为两种情况说明docker对harbor私有仓库的访问配置&#xff0c;一种是harbor使用自建证书配置https&#xff0c;一种是使用公有证书配置https 二、docker配置 harbor使用自建证书的情况 使用自建证书对harbor进行https配置&#xff0c;docker会将该仓库识别成不…

客服工单系统推荐:哪个最适合您?

客服工单系统是企业的业务过程的“保安”&#xff0c;保障业务流程的顺利开展&#xff0c;同时保障企业客户的权益。所以&#xff0c;市场上有越来越多的企业纷纷配置了客服工单系统&#xff0c;以提供客户服务质量。 对于有购买意向的中小企业来讲&#xff0c;需要关注哪些因…

pycharm中py文件设置参数

在py文件中右键 直接对应复制进去即可

供应链管理痛点大解析!内附解决方案

供应链是指涉及产品或服务生产、运输、分销和最终交付给客户的过程。 用一个汽车制造的例子来帮助大家理解&#xff1a; 原材料采购&#xff1a; 汽车制造商需要从供应商处采购制造汽车所需的原材料&#xff0c;例如金属、橡胶、塑料和玻璃。生产制造&#xff1a;获得原材料&…

关于mysql高版本使用groupby导致的报错

在开发时&#xff0c;遇到mysql版本在5.7.X及以上版本时使用group by 语句会报以下的错误 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column business_typ…