实现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://www.mzph.cn/diannao/2362.shtml

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

相关文章

nginx开启basic认证

basic认证也叫做http基本认证&#xff0c;防止恶意访问 首先用在线网站生成一个叫做htpasswd的账号密码文件。 将生成结果复制到/etc/nginx/htpasswd文件中 在server的location中配置 server { listen 80; server_name a.com;location / { root html;index index.…

springcloud alibaba 整合seata的TCC

一、seata服务端搭建同上篇。 Seata的AT模式客户端两阶段提交流程源码分析 二、seata客户端的结构 1.示例DEMO工程 下单&#xff0c;扣余额&#xff0c; 减库存。 2. MAVEN配置。 父工程&#xff1a;由于spring-cloud-starter-alibaba-seata依赖的seata-spring-boot-starter…

顺序栈着三种结构定义及其初始化

定义 顺序堆栈这三种结构定义及其初始化 - 知乎 (zhihu.com) 根据以上链接得到&#xff1a; 1.理解为数组&#xff0c;top是这个数组的索引值&#xff1b;定义这个结构体类型时&#xff0c;系统不分配空间 在主函数声明时&#xff0c;定义了关于这个结构体的变量&#xff0c…

Java 【数据结构】 二叉树(Binary_Tree)【神装】

登神长阶 第五神装 二叉树 Binary-Tree 目录 &#x1f3b7;一.树形结构 &#x1fa97;1.概念 &#x1f3b8;2.具体应用 &#x1f3b9; 二.二叉树&#xff08;Binary Tree&#xff09; &#x1f3ba;1.概念 &#x1f3bb;2.表现形式 &#x1fa95;3.特殊类型 &#x1f941…

自己手动在Linux上实现一个简易的端口扫描器

背景 常常听到网络攻击有一个东西叫做端口扫描器&#xff0c;可以扫描指定服务器开放的端口&#xff0c;然后尝试连接&#xff0c;并寻找漏洞&#xff0c;最终攻破服务器。而那些使用的端口扫描器都是一个个现成的程序&#xff0c;看上去很厉害的样子。而实际上这些东西对于懂…

【前端技术】HTML基础入门篇

1.1 HTML简介 ​ HTML&#xff08;HyperText Markup Language&#xff1a;超文本标记语言&#xff09;是一种标识性的语言。它包括一系列标签&#xff0e;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的Internet资源连接为一个逻辑整体。HTML文本是由HTML命令组…

投资网站汇总

1、 中信证券(600030)历年财务指标——亿牛网https://eniu.com/gu/sh600030/cwzb 2、 3、 4、

每日一题 —— 最大子数组之和(动态规划)

1.链接 53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 2.描述 3.思路 4.参考代码 class Solution { public:int maxSubArray(vector<int>& nums) {int n nums.size();vector<int> dp(n1,0);int ret INT_MIN;for(int i 1;i<n;i){dp[i] ma…

跟着野火从零开始手搓FreeRTOS(6)多优先级的配置

在 FreeRTOS 中&#xff0c;数字优先级越小&#xff0c;逻辑优先级也越小。 之前提过&#xff0c;就绪列表其实就是一个数组&#xff0c; 里面存的是就绪任务的TCB&#xff08;准确来说是 TCB 里面的 xStateListItem 节点&#xff09;&#xff0c;数组的下标对应任务的优先级&a…

【Camera Sensor Driver笔记】五、点亮指南之Actuator配置

<slaveInfo> actuatorName dw9714v dirver IC 型号 slaveAddress 0x18 i2c write address i2cFrequencyMode FAST i2c 操作频率(400KHz) actuatorType VCM/BIVCM 马达类型 BIVCM&#xff08;中置马达&#xff…

ROS 2边学边练(33)-- 写一个静态广播(C++)

前言 通过这一篇我们将了解并学习到如何广播静态坐标变换到tf2&#xff08;由tf2来转换这些坐标系&#xff09;。 发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系非常有用。例如&#xff0c;在以激光扫描仪中心的坐标系中推理激光扫描测量数据是最简单的。 这…

服务器 BMC(基板管理控制器,Baseboard Management Controller)认知

写在前面 工作中遇到&#xff0c;简单整理博文内容涉及 BMC 基本认知理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。——村上春树 基板管理控制器&#xff08;BMC&…

数字孪生创新工作流,助力百年大桥翻修

利用 Bentley 的 iTwin Capture 和 iTwin Experience 创建数字孪生模型&#xff0c;将现场施工时间缩短了 20% 重要交通枢纽焕然一新 罗伯特街大桥位于明尼苏达州圣保罗市&#xff0c;外观呈彩虹样拱形&#xff0c;近 100 年来一直是圣保罗市的标志性建筑。这座八跨钢筋混凝土…

Linux复习提纲2

Linux复习提纲 Linux概述 shell&#xff1a;交互式命令解释程序&#xff1b;用户和内核间交互的桥梁Shell不仅是交互式命令解释程序&#xff0c;还是一种程序设计语言shell是一种命令解释程序&#xff0c;批处理shell是linux的外壳&#xff0c;默认是bash2.1 Linux基础概念 log…

线上剧本杀小程序开发,未来行业的发展趋势?

当下&#xff0c;剧本杀成为了大众最喜欢的娱乐方式之一&#xff0c;作为以沉浸式为主的剧本杀正成为新时代下的发展潮流。 数据显示&#xff0c;剧本杀行业已达到了百亿元。面对发展迅猛的剧本杀市场&#xff0c;越来越多的资本进入到了市场中&#xff0c;剧本杀的产业链也逐…

【C语言】手撕二叉树

标题&#xff1a;【C语言】手撕二叉树 水墨不写bug 正文开始&#xff1a; 二叉树是一种基本的树形数据结构&#xff0c;对于初学者学习树形结构而言较容易接受。二叉树作为一种数据结构&#xff0c;在单纯存储数据方面没有 顺序表&#xff0c;链表&#xff0c;队列等线性结构…

菜鸟Java面向对象 2. Java 重写(Override)与重载(Overload)

Java 重写(Override)与重载(Overload) Java 重写与重载 Java 重写(Override)与重载(Overload)1. 重写(Override)1. 概念解释&#xff1a;2. 好处说明3. 异常规则处理 2. 方法的重写规则3. Super 关键字的使用4. 重载(Overload)**重载规则:**实例 5. 重写与重载之间的区别总结 1…

什么是手机运营商三要素验证API接口

手机运营商三要素验证API接口又叫手机运营商三要素核验API接口&#xff0c;指的是输入姓名、身份证号码及手机号&#xff0c;通过运营商数据库实时校验此三项是否匹配。手机运营商三要素核验API接口广泛用于实名注册、风控审核等场景&#xff0c;如电商、直播、游戏、金融等。接…

Leetcode刷题之链表小结(1)|92反转链表|206反转链表

TOC 小结 1. 如何反转某一个节点的指向? 206反转链表(简单)的递归解法——该方法的理念是: 若节点k1到节点m已经被反转&#xff0c;而我们当前处于k位置&#xff0c;那么我们希望k1指向k, 体现在以下代码的head->next->next head;这一句,可以记做一种常用的反转单个…

AI+招聘,激活企业的「新质生产力」

两会以来&#xff0c;「新质生产力」成为热词。而所谓的新质生产力&#xff0c;是创新起主导作用&#xff0c;摆脱传统经济增长方式、生产力发展路径&#xff0c;具有高科技、高效能、高质量特征&#xff0c;符合新发展理念的先进生产力质态。新质之「新」&#xff0c;很重要的…