实现Spring底层机制(二)

文章目录

  • 阶段2—封装bean定义信息到Map
    • 1.代码框架图
    • 2.代码实现
        • 1.文件目录
        • 2.新增注解Scope存储单例或多例信息Scope.java
        • 3.修改MonsterService.java指定多例注解
        • 4.新增bean定义对象存储bean定义信息BeanDefinition.java
        • 5.修改pom.xml增加依赖
        • 6.修改容器实现bean定义信息扫描SunSpringApplicationContext.java
        • 7.结果展示
    • 3.该阶段完成的任务
  • 阶段3—初始化bean单例池&实现依赖注入
    • 1.初始化bean单例池
        • 1.代码框架图
        • 2.代码实现
          • 1.修改SunSpringApplicationContext.java增加两个方法
          • 2.修改SunSpringApplicationContext.java在构造方法初始化单例池
          • 3.启动类AppMain.java
          • 4.结果
        • 3.该阶段完成的任务
    • 2.实现依赖注入
        • 1.代码实现
          • 1.增加注解Autowired用来表示自动装配
          • 2.修改MonsterDao.java
          • 3.修改MonsterService.java
          • 4.修改SunSpringApplicationContext.java的createBean,添加依赖注入逻辑
          • 5.启动类AppMain.java
          • 6.结果
    • 3.总结IOC
        • 1.单例bean的创建和依赖注入
        • 2.多例bean的特殊处理

阶段2—封装bean定义信息到Map

1.代码框架图

image-20240221183904450

2.代码实现

1.文件目录

image-20240221195548820

2.新增注解Scope存储单例或多例信息Scope.java
package com.sun.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author 孙显圣* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {String value() default ""; //通过value来决定是singleton,prototype
}
3.修改MonsterService.java指定多例注解
package com.sun.spring.component;import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.Scope;/*** @author 孙显圣* @version 1.0*/@Scope(value = "prototype") //指定是多例的
@Component(value = "monsterService")//自定义注解,自动反射创建bean对象,如果指定了value则id为value否则为首字母小写
public class MonsterService {
}
4.新增bean定义对象存储bean定义信息BeanDefinition.java
package com.sun.spring.ioc;/*** 记录bean定义的信息{1.scope 表示单例还是多例 2.clazz 表示bean的Class对象,方便多例的时候创建实例}** @author 孙显圣* @version 1.0*/
public class BeanDefinition {private String scope; //单例或多例private Class clazz; //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;}@Overridepublic String toString() {return "BeanDefinition{" +"scope='" + scope + '\'' +", clazz=" + clazz +'}';}
}
5.修改pom.xml增加依赖
    <!--工具类,使类名首字母小写--><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency>
6.修改容器实现bean定义信息扫描SunSpringApplicationContext.java
package com.sun.spring.ioc;import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.ComponentScan;
import com.sun.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 类似于Spring原生的ioc容器** @author 孙显圣* @version 1.0*/
public class SunSpringApplicationContext {//传进来一个配置类的Class对象private Class configClass;//bean定义字段private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//bean对象字段private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//构造器,接收配置类的class对象public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException {//bean定义扫描this.beanDefinitionScan(configClass);}public void beanDefinitionScan(Class configClass) throws ClassNotFoundException {this.configClass = configClass;//一、获取要扫描的包//1.首先反射获取类的注解信息ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.通过注解来获取要扫描的包的路径String path = componentScan.value();System.out.println("要扫描的包=" + path);//二、得到要扫描包的.class文件对象,从而得到全路径进行反射//1.获取类加载器ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();//2.获取要扫描包的真实路径,默认刚开始在根目录下path = path.replace(".", "/");URL resource = classLoader.getResource(path);//3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型File file = new File(resource.getFile());//4.遍历该文件夹下的所有.class文件对象if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {//反射注入容器//1.获取所有文件的绝对路径String absolutePath = f.getAbsolutePath();//只处理class文件if (absolutePath.endsWith(".class")) {//2.分割出类名String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf("."));//3.得到全路径String fullPath = path.replace("/", ".") + "." + className;//4.判断是否需要注入容器,查看有没有自定义的注解ComponentClass<?> aClass = classLoader.loadClass(fullPath);//如果该类使用了注解Component则说明是一个spring beanif (aClass.isAnnotationPresent(Component.class)) {System.out.println("这是一个Spring bean=" + aClass);//将Bean的信息封装到BeanDefinition对象中并放入到beanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();//获取scope注解信息Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);//只要scopeAnnotation是空的或者他不是空的但是值是空串,则返回singleon,否则就返回valueString scope = scopeAnnotation == null || scopeAnnotation.value().equals("") ?"singleton" : scopeAnnotation.value();//封装信息到bean定义字段中beanDefinition.setScope(scope);beanDefinition.setClazz(aClass);//获取Component注解的value值作为beanNameComponent componentAnnotation = aClass.getDeclaredAnnotation(Component.class);//只要component注解的值是空串就返回类名首字母小写,否则返回这个注解的值String beanName = componentAnnotation.value().equals("") ?StringUtils.uncapitalize(className) : componentAnnotation.value();//封装到beanDefinitionMap中beanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("这不是一个Spring bean=" + aClass);}}}//遍历输出beanDefinitionMapSystem.out.println("遍历输出beanDefinitionMap");for (Map.Entry<String, BeanDefinition> beanDefinitionMap : beanDefinitionMap.entrySet()) {System.out.println(beanDefinitionMap.getKey() + " " + beanDefinitionMap.getValue());}}}//返回容器中的对象public Object getBean(String name) {return null;}}
7.结果展示

image-20240221200049728

3.该阶段完成的任务

  • 新增注解Scope来存储单例/多例信息
  • 新增bean定义对象,存储bean定义信息Scope和Class对象
  • 扫描所有组件的信息,将bean定义信息以beanName - bean定义对象的形式存储到Map中

阶段3—初始化bean单例池&实现依赖注入

1.初始化bean单例池

1.代码框架图

image-20240221201306781

2.代码实现
1.修改SunSpringApplicationContext.java增加两个方法
    //根据bean定义对象来创建bean对象private Object createBean(BeanDefinition beanDefinition){//获取Class对象,反射创建实例Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().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 name) {//如果是单例的,则从单例池中获取,如果是多例的则直接创建一个实例返回,名字没在bean定义中则抛出异常if (beanDefinitionMap.containsKey(name)) {BeanDefinition beanDefinition = beanDefinitionMap.get(name);//判断是否是单例if ("singleton".equals(beanDefinition.getScope())) {//从单例池中获取对象return singletonObjects.get(name);} else {//直接创建对象return createBean(beanDefinition);}} else {//name不在bean定义中则抛出异常throw new NullPointerException("没有该bean");}}
2.修改SunSpringApplicationContext.java在构造方法初始化单例池
    //构造器,接收配置类的class对象public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException {//bean定义扫描,并将信息封装到beanDefinitionMap中this.beanDefinitionScan(configClass);//初始化单例池//1.获取所有bean的名字Enumeration<String> keys = beanDefinitionMap.keys();//2.遍历名字while (keys.hasMoreElements()) {String beanName = keys.nextElement();//3.是单例类型的就创建bean对象并放到单例池中BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition.getScope().equals("singleton")) {Object bean = createBean(beanDefinition);//4.放到单例池singletonObjects.put(beanName, bean);}}System.out.println("singletonObjects: " + singletonObjects);}
3.启动类AppMain.java
package com.sun.spring;import com.sun.spring.ioc.SunSpringApplicationContext;
import com.sun.spring.ioc.SunSpringConfig;/*** @author 孙显圣* @version 1.0*/
public class AppMain {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {SunSpringApplicationContext ioc = new SunSpringApplicationContext(SunSpringConfig.class);Object bean01 = ioc.getBean("monsterDao111"); //单例对象Object bean02 = ioc.getBean("monsterDao111"); //单例对象System.out.println("单例对象1:" + bean01);System.out.println("单例对象2:" +bean02);//多例对象Object bean1 = ioc.getBean("monsterService");Object bean2 = ioc.getBean("monsterService");System.out.println("多例对象1:" + bean1);System.out.println("多例对象1:" + bean2);}
}
4.结果

image-20240221212306705

3.该阶段完成的任务
  • 新增createBean的方法,根据bean定义对象创建bean对象
  • 新增初始化单例池,在封装完bean定义信息之后,扫描bean定义信息,如果是单例对象则创建bean对象放到单例池中
  • 新增getBean方法,如果是单例的对象,则从单例池中查找,否则直接创建对象

2.实现依赖注入

1.代码实现
1.增加注解Autowired用来表示自动装配
package com.sun.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author 孙显圣* @version 1.0*/
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
2.修改MonsterDao.java
package com.sun.spring.component;import com.sun.spring.annotation.Component;/*** @author 孙显圣* @version 1.0*/
//scope默认是单例的
@Component(value = "monsterDao")
public class MonsterDao {public void hi() {System.out.println("MonsterDao say hi!");}}
3.修改MonsterService.java
package com.sun.spring.component;import com.sun.spring.annotation.Autowired;
import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.Scope;/*** @author 孙显圣* @version 1.0*/@Scope(value = "prototype") //指定是多例的
@Component(value = "monsterService")//自定义注解,自动反射创建bean对象,如果指定了value则id为value否则为首字母小写
public class MonsterService {//自定义注解,按照名字进行依赖注入@Autowiredprivate MonsterDao monsterDao;public void m1() {monsterDao.hi();}
}
4.修改SunSpringApplicationContext.java的createBean,添加依赖注入逻辑
    //根据bean定义对象来创建bean对象private Object createBean(BeanDefinition beanDefinition){//获取Class对象,反射创建实例Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//进行依赖注入//1.遍历所有字段for (Field field : clazz.getDeclaredFields()) {//2.判断字段中是否有autowired注解if (field.isAnnotationPresent(Autowired.class)) {//3.依赖注入String name = field.getName();Object bean = getBean(name);//反射爆破field.setAccessible(true);field.set(instance, bean);}}//如果成功则返回这个对象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);}}
5.启动类AppMain.java
package com.sxs.spring;import com.sxs.spring.aop.SmartAnimalable;
import com.sxs.spring.component.UserAction;
import com.sxs.spring.component.UserDao;
import com.sxs.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 孙显圣* @version 1.0*/
public class AppMain {public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");UserAction action1 = ioc.getBean(UserAction.class);UserAction action2 = ioc.getBean(UserAction.class);System.out.println("action1=" + action1);System.out.println("action2=" + action2);UserService service = ioc.getBean(UserService.class);System.out.println("service=" + service);service.m1();UserDao userDao = ioc.getBean(UserDao.class);System.out.println("userDao=" + userDao);//aop测试SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);bean.getSum(1,2);bean.getSub(2,1);}}
6.结果

image-20240222103029837

3.总结IOC

1.单例bean的创建和依赖注入
  • 获取Spring容器对象,读取配置文件,得到要扫描的包
  • 扫描指定的包,将bean定义信息放到map中
  • 初始化单例池,如果是单例的则直接反射创建bean对象并放到单例池中
  • 依赖注入,扫描单例池中实例的所有字段,如果有自动装配注解则进行依赖注入
  • 初始化bean对象
  • getBean的时候从单例池中获取bean对象
  • 销毁bean
2.多例bean的特殊处理
  • getBean的时候反射创建bean对象,依赖注入,初始化bean,然后再得到bean对象
  • 销毁bean

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

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

相关文章

基于Vue+ElementPlus自定义带历史记录的搜索框组件

前言 基于Vue2.5ElementPlus实现的一个自定义带历史记录的搜索框组件 效果如图&#xff1a; 基本样式&#xff1a; 获取焦点后&#xff1a; 这里的历史记录默认最大存储10条&#xff0c;同时右侧的清空按钮可以清空所有历史记录。 同时搜索记录也支持点击搜索&#xff0c;按…

Linux给磁盘扩容(LVM方式)

Linux给磁盘扩容&#xff08;LVM方式&#xff09; 最近测试性能&#xff0c;在本地打数据时&#xff0c;发现磁盘空间不足&#xff0c;于是想手动给/挂载点添加空间。这里介绍通过LVM方式快速给磁盘扩容。 LVM:是一种技术&#xff0c;方便管理磁盘。如果不用LVM&#xff0c;那…

springboot2集成东方通tongweb嵌入式版

由于最近项目需要国产化信创改造&#xff0c;引入东方通tongweb 联系东方通厂家 &#xff0c;将依赖导入到maven仓库&#xff0c;并获取嵌入式版license文件修改pom.xml&#xff0c;引入依赖&#xff0c;注意springboot版本&#xff0c;这里以springboot2举例 首先移除springb…

Xinlinx FPGA内的存储器BRAM全解

目录 一、总体概述1.7系列FPGA的BRAM特点2.资源情况 二、BRAM分类1.单端口RAM2.简单双端口RAM3.真双端口RAM 三、BRAM的读写1、Primitives Output Registers读操作注意事项2.三种写数据模式&#xff08;1&#xff09;Write_First&#xff08;2&#xff09;Read_First&#xff0…

【学习】软件测试自动化,是未来的趋势还是当前的必需

在当今快速迭代的软件开发周期中&#xff0c;速度和质量成为了企业生存的关键。随着DevOps实践的普及和持续集成/持续部署&#xff08;CI/CD&#xff09;流程的标准化&#xff0c;软件测试自动化已经从未来的趋势转变为当前的必要性。本文将探讨自动化测试的现状、必要性以及其…

大模型训练及推理【硬件选型指南】及 GPU 通识

我们在做大模型应用部署时&#xff08;如训练、微调、RAG&#xff09;&#xff0c;往往需要在前期就分析好硬件选型指标&#xff0c;或者我们给客户报方案之前&#xff0c;可能你已经有了一个方案&#xff0c;但是由于实践经验缺乏&#xff0c;不知道在硬件上该如何评估并上报。…

invidia-smi占用显存,无法显示PID

如果是动用了子线程创建进程&#xff0c;比如利用accelerate训练脚本&#xff0c;那么大概率可以通过这种方式解决&#xff1a;nvidia-smi没有进程&#xff0c;但是显存占用_nvidia-smi有的卡是0%-CSDN博客 如果这种方法不可用&#xff0c;请尝试直接查询所有python进程&#x…

react v18 项目初始化

按照以下命令进行傻瓜式操作即可&#xff1a; 全局安装脚手架工具&#xff1a; npm install -g create-react-app创建项目my-react-app&#xff1a; create-react-app my-react-app安装 antd: yarn add antd安装 react-router-dom&#xff1a; yarn add react-router-dom启动项…

[Qt的学习日常]--初识Qt

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、Qt的基本…

鸿蒙(HarmonyOS)性能优化实战-多线程共享内存

概述 在应用开发中&#xff0c;为了避免主线程阻塞&#xff0c;提高应用性能&#xff0c;需要将一些耗时操作放在子线程中执行。此时&#xff0c;子线程就需要访问主线程中的数据。ArkTS采用了基于消息通信的Actor并发模型&#xff0c;具有内存隔离的特性&#xff0c;所以跨线…

4.23日总结(项目总结)

1.项目&#xff1a; 今日项目通过一个在登录界面的一个静态变量&#xff0c;完成了区分老师和学生&#xff0c;能够分开老师和学生&#xff0c;并且不同身份的人进去会有不同的显示&#xff0c;以及登录链接主界面&#xff0c;还有学生和老师的不同的表&#xff0c;其次就是创…

葡萄书--关系图卷积神经网络

异质图和知识图谱 同质图与异质图 同质图指的是图中的节点类型和关系类型都仅有一种 异质图是指图中的节点类型或关系类型多于一种 知识图谱 知识图谱包含实体和实体之间的关系&#xff0c;并以三元组的形式存储&#xff08;<头实体, 关系, 尾实体>&#xff0c;即异…

用Excel做一个功能完备的仓库管理系统

1 基本设计思路 用到的Excel技术&#xff1a;sumif, vlookup, 表格(table)。基本思路&#xff1a;在有基础的商品、仓库等信息的情况下&#xff0c;对商品的每一个操作都有对应的单据&#xff0c;然后再汇总统计。标识&#xff1a;为了在不同的维度统计数量&#xff0c;各单据…

Abaqus2024 安装教程(附免费安装包资源)

鼠标右击软件压缩包&#xff0c;选择“解压到Abaqus2024”。 鼠标右击“此电脑”&#xff0c;选择“属性”。 点击“高级系统设置”。 点击“环境变量”。 点击“新建”。 变量名输入&#xff1a;NOLICENSECHECK 变量值输入&#xff1a;true 然后点击“确定”。 点击“确定”。…

羊大师解析,夏日消暑羊奶来帮忙

羊大师解析&#xff0c;夏日消暑羊奶来帮忙 炎炎夏日&#xff0c;烈日当空&#xff0c;人们总是寻找各种方式来消暑降温。除了常见的冷饮、空调等&#xff0c;其实还有一种天然、健康的饮品可以帮助我们度过酷暑——那就是羊奶。 羊奶作为一种营养丰富的天然饮品&#xff0c;不…

【git】多仓库开发

通常我们习惯了在单个仓库下多分支开发&#xff0c;最近用了多仓库开发&#xff0c;我拿一个开源项目举例&#xff0c;总结一下基本流程。 1.fork项目到自己仓库 2.把自己仓库项目pull到本地 3.在本地新建开发分支 git checkout -b dev 4.修改/添加分支对应的远程仓库 修…

小案例:ToolBar+选项菜单

使用选项菜单&#xff0c;一般是用于做单选&#xff0c;需要重写方法&#xff1a; public boolean onCreateOptionsMenu(Menu menu) 如果想要实现事件监听&#xff0c;则采用基于回调的事件监听机制&#xff0c;可以监听到具体是哪一项被选中。即重写方法&#xff1a; publi…

Kafka学习笔记(二、linux和docker安装及使用demo)

1.安装启动 1.1.下载解压 官网下载地址将下载的tar包上传到服务器一个目录&#xff0c;然后解压$ tar -xzf kafka_2.13-3.7.0.tgz $ cd kafka_2.13-3.7.01.2. 启动环境 需安装Jdk8&#xff0c;Kafka可以使用ZooKeeper或KRaft启动。 ZooKeeper启动 运行如下命令&#xff0c;…

windows系统下python开发工具安装

一. 简介 前一篇文章学习了安装 python解释器&#xff0c;文章如下&#xff1a; windows系统下python解释器安装-CSDN博客 本文来学习如何下载安装 python开发工具 PyCharm。 二. python开发工具 PyCharm下载安装 1. PyCharm官网 PyCharm开发工具 PyCharm为 python代码…

【学习笔记二十四】EWM补货策略和自动补货配置

一、EWM补货策略概述 1.计划补货 ①以联机或批处理模式启动 ②根据最大和最小数量计算补货 ③仅当库存量低于最低数量时才开始 ④四舍五入至最小补货数量的倍数 2.自动补货 ①在WT确认期间启动 ②根据最大和最小数量计算补货 ③只有当库存量低于最低数量时才开始 ④四舍…