实现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;
    }

    @Override
    public 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.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = classLoader.loadClass(fullPath);

                    //如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        System.out.println("这是一个Spring bean=" + aClass);
                        //将Bean的信息封装到BeanDefinition对象中并放入到beanDefinitionMap
                        BeanDefinition beanDefinition = new BeanDefinition();
                        //获取scope注解信息
                        Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                        //只要scopeAnnotation是空的或者他不是空的但是值是空串,则返回singleon,否则就返回value
                        String scope = scopeAnnotation == null || scopeAnnotation.value().equals("") ?
                                "singleton" : scopeAnnotation.value();

                        //封装信息到bean定义字段中
                        beanDefinition.setScope(scope);
                        beanDefinition.setClazz(aClass);

                        //获取Component注解的value值作为beanName
                        Component 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);
                    }

                }
            }

            //遍历输出beanDefinitionMap
            System.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 {
    //自定义注解,按照名字进行依赖注入
    @Autowired
    private 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

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

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

相关文章

C语言|关于C语言变量的作用域、链接、存储期及相关知识(C多文件编程基础)

文章目录 作用域块作用域(block scope)函数作用域(function scope)函数原型作用域(function prototype scope)文件作用域(file scope)翻译单元和文件(作用域&#xff09; 链接(linkage)存储期(Storege Duration)静态存储期(static storage duration)线程存储期(thread storage …

kafka启动报错(kafka.common.InconsistentClusterIdException)

文章目录 前言kafka启动报错(kafka.common.InconsistentClusterIdException)1. 查找日志2. 定位问题/解决 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不…

qt实现方框调整

效果 在四周调整 代码 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QWidget>class MainWindow : public QWidget {Q_OBJECT public:explicit MainWindow(QWidget *parent 0);~MainWindow();void paintEvent(QPaintEvent *event);void updateRect();void re…

ZYNQ--PL读写PS端DDR数据

PL 和PS的高效交互是zynq 7000 soc开发的重中之重,我们常常需要将PL端的大量数 据实时送到PS端处理,或者将PS端处理结果实时送到PL端处理,常规我们会想到使用DMA 的方式来进行,但是各种协议非常麻烦,灵活性也比较差,本节课程讲解如何直接通过AXI总 线来读写PS端ddr的数据…

什么是基尼系数

基尼系数是国际上用来综合考察居民内部收入分配差异状况的一个重要分析指标。每个人的收入有多有少&#xff0c;差距大时&#xff0c;基尼系数就高&#xff1b;差距小时&#xff0c;基尼系数就低。 一、基本概念 基尼系数表示在全部居民收入中&#xff0c;用于进行不平均分配…

补充centos7软件包的方式/编译安装源码包软件/企业案例/linux进程管理/企业管理进程系列命令(企业经验)--8820字详谈

cenros7软件包的安装方式 软件包分类安装方式优缺点rpm包软件开发商编译打包&#xff0c;安装简单&#xff0c;快速软件版本可能偏低&#xff0c;安装路径是固定好的源码包自己手动编译安装并且复杂软件爸爸随意选&#xff0c;可以定制安装路径二进制包解压就可以使用不能进行…

什么是AIGC技术

AIGC技术&#xff0c;即人工智能全局优化控制技术&#xff0c;是一种将人工智能与全局优化控制方法相结合的技术。它的主要目标是通过智能化的方法来解决复杂系统的优化问题&#xff0c;提高系统的性能和效率。 AIGC技术的主要目标是利用人工智能算法和技术来实现对系统整体的…

第三篇:Python编程基础:掌握核心语法与开发技巧

Python编程基础&#xff1a;掌握核心语法与开发技巧 1 引言 在这个信息化迅速蔓延的世界中&#xff0c;Python语言如同钥匙一般开启了通往各种可能性的大门。无论你是数据科学家、网络工程师、机器学习专家&#xff0c;还是仅仅对自动化办公感兴趣的办公室人员&#xff0c;Pyt…

【Elasticsearch<二>✈️✈️】基本属性概念与MySQL数据库的不同之处

目录 &#x1f378;前言 &#x1f37b;一、Elasticsearch 基本属性 1.1 ES VS MySQL 1.2 ES 属性概念 1.3 ES 的增删改查 &#x1f37a;二、自动补全场景 2.1 场景举例 2.2 使用数据分词器 2.3 查询的流程 2.4 整个查询流程图 &#x1f379;章末 &#x1f378;前言 上次初步…

Zynq 7000 系列中的Interconnect(互联)简介

PS&#xff08;处理器子系统&#xff09;内部的互联结构包含了多个交换机&#xff0c;用于通过AXI点对点通道连接系统资源。这些通道负责在主机和从机客户端之间进行地址、数据和响应事务通信。该互联结构负责管理多个待处理的事务&#xff0c;并且为Arm CPU设计了低延迟路径&a…

UE4_动画基础_FootIK

角色由于胶囊体的阻挡&#xff0c;双脚与地面平行&#xff0c;不会与斜坡、台阶等贴合&#xff0c;有一条腿会处于悬空状态&#xff0c;通过双骨骼IK节点使一只脚太高&#xff0c;让后胶囊体下降&#xff0c;修正双脚的角度。这就是逆向运动IK的方法。 一、新建第三人称模板游戏…

OpenStack云计算(十四)——综合演练手动部署OpenStack,

本项目的项目实训可以完全参考教材配套讲解的详细步骤实施&#xff0c;总体来说实训工作量较大&#xff0c;可根据需要选做&#xff0c;重点观看配套的微课视频。 项目实训一 【实训题目】 搭建OpenStack云平台基础环境 【实训目的】 掌握OpenStack基础环境的安装和配置方…

CTFshow-PWN-栈溢出(pwn36)

存在后门函数&#xff0c;如何利用&#xff1f; 好好好&#xff0c;终于到了这种有后门函数的了 checksec 检查一下&#xff1a; 32 位程序&#xff0c;RELRO 保护部分开启 RWX: Has RWX segments 存在可读可写可执行的段 使用 ida32 看 main 函数 跟进 ctfshow 函数…

C++系列-命名空间

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 命名空间 在C/C中&#xff0c;变量&#xff0c;函数和后面要学到的类都是大量存在的&#xff0c;这些变量&#xff0c;函数和类的名称都存在于全局作用域中&#xff0c;可能会导…

电机入门1

文章目录 122.12.22.3 33.13.23.33.4 1 2 2.1 电机板 驱动板电机分类 驱动器分类 转速 转向扭矩定时器 ADC 2.2 PID 自动控制 的核心闭环控制算是 PID的应用 2.3 无刷电机用的 可大大提高其控制效率 和控制精度 3 开发板的IO 电流太小了 20~25ma 电机要A 驱动板 信号放大没舵…

Linux防火墙相关命令以及ip白名单配置

Linux防火墙相关命令以及ip白名单配置 firewall防火墙基础命令查看防火墙的服务状态查看防火墙的状态服务的开启、关闭和重启查看防火墙规则端口的查询、开放和关闭重启防火墙 防火墙白名单配置部分参数介绍 firewall防火墙基础命令 查看防火墙的服务状态 systemctl status f…

乘数而上,创邻科技入选2024数商典型应用场景“乘数榜”

4月18日&#xff0c;由浙江省科学技术协会指导的2024未来数商大会在杭州成功举办。本次大会以“场景突破 乘数而上”为主题&#xff0c;国际国内数商共聚未来科技城学术交流中心&#xff0c;聚焦数据要素市场的制度创新、数据治理、场景应用与生态构建等话题展开研讨。 大会现…

C++入门基础(一)

目录 C关键字命名空间命名冲突例子域的概念理解命名空间定义例子1例子2例子3例子4例子5例子6例子7 C输出与输入输出输入 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x…

汽车底盘域的学习笔记

前言&#xff1a;底盘域分为传统车型底盘域和新能源车型底盘域&#xff08;新能源系统又可以分为纯电和混动车型&#xff0c;有时间可以再研究一下&#xff09; 1&#xff1a;传统车型底盘域 细分的话可以分为四个子系统 传动系统 行驶系统 转向系统 制动系统 1.1传动系…

计算机体系结构:向量体系结构介绍

向量体系结构介绍 什么是向量&#xff1f; 在计算机体系结构&#xff0c;"向量"&#xff08;vector&#xff09;是指一个由多个相同类型且逻辑上相关的数据元素组成的有序集合。这些元素可以是整数、浮点数、布尔值或其他数据类型&#xff0c;它们在内存中连续存储…