Spring 底层原理与解析 - 容器接口

news2024/11/18 5:45:21

Spring 底层原理与解析 - 容器接口

BeanFactory 能做哪些事

  • BeanFactory 与 ApplicaiotnContext 到底是谁提前做完了对象的加载

在之前的一篇关于 Spring 的文章Spring IoC 与容器的初始化中提到过,BeanFactory 接口与 ApplicationContext 接口之间的关系

image-20230214193235253

image-20230214191907914

可以看到的是 BeanFactorySpring 容器的一个最原始的接口,同时也定义了获取 Bean 对象的方法 getBean()

BeanFactory 接口所提供的 getBEean() 方法获取 Bean 的加载时机与 ApplicationContext 对象获取的 Bean 对象的 GetBean 是不同的。

我们在上一篇 Spring IoC 与容器的初始化 中已经进行过验证,BeanFactory 初始化 Bean 对象是在执行调用 getBean() 的时候,而 ApplcationContext 是在第一次加载 Spring 的配置文件的时候,就已经将 Bean 对象初始化到了容器当中的。

其实在 ApplicationContext 对象中调用的 getBean() 方式是来自于 BeanFactory 对象的,我们可以从代码中证明这一点。

public class App {
    public static void main( String[] args ) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("开始执行 getBean 方法");
        applicationContext.getBean("userService");
    }
}
public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}

我们进入 applicationContext 对象的 getBean() 方法中可以看到,getBean() 方法其实就是调用了 getBeanFactory() 方法首先获取 BeanFactory 对象,然后调用 BeanFactory 对象的 getBean() 方法获取容器中的 Bean 。

  • BeanFactory 能干的事多吗?

BeanFactory 我们前面看到过,该接口所提供的功能似乎很少,唯一用的最多的只要一个 getBean 。那么,我们这么强大的轻量级框架的 Spring 真的就这么”轻“吗?

哎~ 实时不然,我们所用到的绝大多是方法其实都是由 BeanFactory 实现类所提供的,不信你看。

BeanFactory 的一个子接口 ConfiguraableBeanFactory 下的一个实现类 DefaultListableBeanFactory

通过名字就可以看出 ConfiguraableBeanFactory 就是一个可以自己配置的 BeanFactory ,而 DefaultListableBeanFactory 提供了一些默认的 BeanFactory 的方法

可以看到其抽象父类 AbstractAutowireCapableBeanFactory 的继承关系图

image-20230214200046517

image-20230214200549228

我们 F4 进入 SingletonBeanRegistry 的实现类 DefaultSingletonBeanRegistry 中,该实现类中管理了我们在 Spring 容器中创建的所有的对象。

image-20230214200904478

可以看到有一个叫做 singletonObjects 的对象,该对象是一个 HashMap 而这个 Map 集合中就存储这我们 Spring 中创建的所有的 Bean 对象。我们可以通过 DeBug 的方式进行查看,也可以通过反射的方式获取该 singletonObject 中的属性值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <bean id="userService1" class="com.peggy.service.impl.UserServiceImpl1"/>
    <bean id="userService2" class="com.peggy.service.impl.UserServiceImpl2"/>
​
</beans>
public class App {
    public static void main( String[] args ) {
​
        ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
​
        try {
​
            Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
            singletonObjects.setAccessible(true);
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            
            Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
            /* 通过过滤器只保留一部分 */
            map.entrySet().stream().filter(e->e.getKey().startsWith("userService")).forEach((e)->{
                System.out.println(e.getKey()+"="+e.getValue());
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
​
    }
}
​

image-20230214204824710

可以看到 singletonObjects 属性中有我们注入的 UserService 对象

ApplicationContext 有哪些扩展的功能

appllicationContext 的主要功能的扩展来自于 MessageSource (国际化处理)、ResourcePatternResolver (资源匹配,来自用磁盘本地的文件资源匹配)、ApplicationEventPublisher (发布事件对象、新用户短信验证注册)、EnvironmentCapable (处理环境信息,环境变量)

image-20230214211123221

  • 国际化的处理
@SpringBootApplication
public class SpringDomeApplication {
​
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringDomeApplication.class, args);
​
        System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
​
    }
​
}

image-20230214212431657

image-20230214212814688

  • 获取资源
@SpringBootApplication
public class SpringDomeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringDomeApplication.class, args);
        /*
         * classpath 到类路径下找到对应的资源
         * file 到磁盘目录下获取资源
         * */
        try {
            Resource[] resource = context.getResources("application.properties");
            for (Resource re : resource) {
                System.out.println(re);
            }
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

image-20230214212844180

classpath:META-INF/spring.factories* 获取 jar 包下的类路径的所有同名文件

@SpringBootApplication
public class SpringDomeApplication {
​
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringDomeApplication.class, args);
        /*
         * classpath*:META-INF/spring.factories  获取 jar 包下的类路径的所有同名文件
         * */
        try {
            Resource[] resource = context.getResources("classpath*:META-INF/spring.factories");
            for (Resource re : resource) {
                System.out.println(re);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
​
}

image-20230214213648969

  • 获取环境变量与信息
@SpringBootApplication
public class SpringDomeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringDomeApplication.class, args);
        System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
        System.out.println(context.getEnvironment().getProperty("server.port"));    
    }
}

image-20230214214138568

事件的解耦

事件的解耦指的是什么呢?

可以实现这样的一个场景,比如说用户需要进行邮件的发送,用户发送出去一个邮件不需要一直等待,直到接受者接受到邮件后才能做其他的操作。

而我们的事件对象就是就可以达到者一个 发送者接受者 之间相互解耦的目的。

那么,对于我们的发送者来说

可以利用 ApplicationEventPublisher 中提供的 publishEvent 来发送信息,而创建一个监听接受者用户信息的接收。

  • 创建一个监听者
/**
 * @author peggy
 * @data 2023/2/14 21:47
 * 事件的接受者
 */
@Component
public class ListeningOrage {
    /**
     * 注入一个事件的监听器 当接受的监听的对象 执行该方法 
     * 该方法的参数与方法名返回值都随意
     * @param registeredEvder
     */
    @EventListener
    void take(UserRegisteredEvder registeredEvder){
        System.out.println("接受的事件: "+registeredEvder);
    }
}
  • 创建一个时间对象
/**
 * 事件对象
 * @author peggy
 * @data 2023/2/14 21:43
 */
public class UserRegisteredEvder extends ApplicationEvent {
    /**
     * 事件源
     * @param source
     */
    public UserRegisteredEvder(Object source) {
        super(source);
    }
}
  • 事件的发送者
/**
 * 时间的发送者
 * @author peggy
 * @data 2023/2/14 22:00
 */
@Component
public class UserSendEvder {
    @Autowired
    ApplicationEventPublisher eventPublisher;
​
    /**
     * 实现的需要调用的时间发送方法
     * 方法名与方法的参数以及返回值随意
     */
    void send(){
        System.out.println("发送信息");
        eventPublisher.publishEvent(new UserRegisteredEvder(this));
    }
}
  • 开始发送事件
@SpringBootApplication
public class SpringDomeApplication {
​
    public static void main(String[] args) {
​
        ConfigurableApplicationContext context = SpringApplication.run(SpringDomeApplication.class, args);
        /*
        * 通过类信息获容器中的发送者的对象,并执行发送者的发送方法
        * */
        context.getBean(UserSendEvder.class).send();
    }
​
}

这里可以发现我们的接受到的对象就是我们创建的事件发送者的 this 对象本身

image-20230214221417782

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

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

相关文章

MySQL作业四

学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, Cno, Score)…

编译链接实战(8)认识elf文件格式

&#x1f380; 关于博主&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f947; 作者简介&#xff1a; 热衷于知识探索和分享的技术博主。 &#x1f482; csdn主页:&#xff1a;【奇妙之二进制】 ✍️ 微信公众号&#xff1a;【Linux …

FreeRTOS的列表和列表项

目录 列表和列表项的简介 列表和列表项的关系 列表相关API函数介绍 函数vListInitialiseI() 函数vListInitialiseItem() 函数vListInsert() 函数vListInsertEnd() 函数uxListRemove() 列表和列表项的简介 列表是FreeRTOS中的一个数据结构&#xff0c;概念上和链表有点类…

电脑的安全模式安全吗?如何进入安全模式?

“怎么进安全模式&#xff1f;”这条留言成功引起了驱动哥的注意。 在遇到电脑蓝屏、黑屏等系统问题的时候&#xff0c;如何在更安全且不损失电脑文件的情况下修复故障&#xff1f;系统早已为大家准备好了相应的修复策略。 没错&#xff01;电脑也有属于自己的Plan B——安全…

我工作5年测试才8K,应届生刚毕业就拿16K?凭什么

我从事手工测试五年了&#xff0c;还拿着8K的死工资&#xff0c;家里还几张嘴需要喂养&#xff0c;我很累&#xff0c;也很迷茫…【某个粉丝跟我的诉说】 为什么手工测试会迷茫呢&#xff1f; 自动化测试、性能测试倒是不会迷茫。 我认为手工测试的迷茫基于两个原因&#xf…

数据分析与SAS学习笔记5

DATALINES语句&#xff1a; 相当于CARS语句&#xff1b; 该语句必须是数据步的最后一条语句&#xff1b; MISSOVER处理&#xff1b; DATA TEMP; INFILE DATALINES MISSOVER; INPUT X Y Z; DATALINES; 1 10 100 2 20 3 30 300 ; PROC PRINT; RUN; 代码说明&#xff1a; 1&am…

生物信息场景下的用户需求

背景分析概念定义基因测序是一种新型基因检测技术&#xff0c;是基因检测的方法之一&#xff0c;其又叫基因谱测序&#xff0c;是国际上公认的一种基因检测标准。基因测序技术能锁定病变基因&#xff0c;提前预防和治疗。过长的测序周期以及上万美元的仪器成本&#xff0c;成了…

第二章:unity性能优化之drawcall优化-1

目录 前言&#xff1a; 一、什么是drawcall 二、如何合批 1、什么是合批&#xff1f; 2、静态批处理 1、什么是静态批处理&#xff1a; 2、静态合批的规则 3、动态批处理 4、GPU Instancing 1、GPU instancing的定义 2、编写支持GPU instancing Shader步骤 5、…

Blazor 托管模型 BlazorWebAssembly和Blazor Server

BlazorWebAssembly 应用 BlazorWebAssembly 应用使用基于 WebAssembly 的 .NET 运行时在浏览器中直接执行。 BlazorWebAssembly 应用的工作方式类似于 Angular 和 React 等前端 JavaScript 框架。 但不是编写 JavaScript&#xff0c;而是编写 C#。 .NET 运行时与应用、应用程序…

day18_常用API之String类丶Object类

String概述 java.lang.String 类代表字符串&#xff0c;String类定义的变量可以用于指向字符串对象&#xff0c;同时String类提供了很多操作字符串的功能&#xff0c;我们可以直接使用。Java 程序中的所有字符串文字&#xff08;例如“abc”&#xff09;都为此类的对象 特点:St…

【STM32笔记】低功耗模式下GPIO、外设、时钟省电配置避坑

【STM32笔记】低功耗模式下GPIO、外设、时钟省电配置避坑 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置&#xff08;ADC唤醒无法使用、低功耗模式无法烧录解决方案&#xff09; blog.csdn.net/weixin_534033…

最强找茬小程序

文章目录准备工作环境要求安装步骤效果展示源码下载最强找茬小程序&#xff0c;支持好友对战 准备工作 准备一个Linux系统的云服务器 centos7或ubuntu 安装宝塔面板&#xff08;不是必需的&#xff0c;建议安装这个&#xff09; 买一个域名&#xff0c;并配置ssl证书&#x…

NX二次开发编译时dll自动数字签名及拷贝

前言 在UG5.0开始&#xff0c;所有基于UG二次开发的DLL都要“签名”后才能被客户端上正版的NX调用。 一、基于C# 开发签名 1、添加资源文件 &#xff08;1&#xff09;项目类库上右键–>属性–>资源–>添加资源右边小三角–>添加现有文件–>切换到UG安装目录下…

Java SSM 笔记(一)重置版

Spring核心技术 **前置课程要求&#xff1a;**请各位小伙伴先完成《JavaWeb》篇、《Java 9-17新特性》篇视频教程之后&#xff0c;再来观看此教程。 **建议&#xff1a;**对Java开发还不是很熟悉的同学&#xff0c;最好先花费半个月到一个月时间大量地去编写小项目&#xff0…

Source lnsight工具的简单使用

多文件编程推荐用Source lnsight工具来进行编写 一、Source lnsight工具的简单使用 1、在桌面上新建一个文件夹factory&#xff0c;在文件夹里新建一个cat.c文件和si文件夹 2、打开Source lnsight工具&#xff0c;点击上方Project--->New Project 3、把文件夹factory中si文…

2023年初级会计职称考试《经济法基础》大纲变动内容

整体变动:2023年度考试大纲主要作了以下调整:1. 第四章中增加了增值税出口退税和地方教育附加相关内容;2. 第五章中增加了企业重组业务企业所得税处理&#xff0c;企业所得税特别纳税调整和纳税电报表相关内容;3. 第六章中增加了印花税相关内容。具体变动:第一章 总论无变化第二…

QML矩形(Rectangle)

Rectangle 用于绘制矩形 常见的属性&#xff1a; 填充颜色&#xff1a;纯色&#xff1a;color 渐变 &#xff1a;Gradient类 渐变的优先级大于纯色Gradient&#xff08;渐变色&#xff09;&#xff1a; 渐变由多种颜色定义&#xff0c;这些颜色将无缝混合&#xff0c…

【前端基础问题】浏览器调起桌面通知功能 Notification

浏览器调起桌面通知功能 Notification一、Notification二、注意事项三、使用步骤1、向用户发起权限请求2、调用 Notification API 进行推送消息四、完整代码五、效果一、Notification Notifications API 允许网页或应用程序在系统级别发送在页面外部显示的通知;这样即使应用程序…

【Servlet+Jsp+Mybatis+Maven】WEB图书馆管理系统

web图书馆管理系统一、绪论二、流程和其页面展示效果流程页面效果项目结构三、具体实现第一步&#xff1a;备数据库表第二步&#xff1a;编写登录前端代码第三步&#xff1a;利用过滤器处理安全问题第四步&#xff1a;控制层去实现相关调用第五步&#xff1a;实现持久化层与数据…

教你如何搭建人事OA-薪资管理系统,demo可分享

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建人事OA-薪资管理。1.2、应用场景根据设置薪资基础及考勤和绩效的数据计算得到各个员工工资详情。2、设置方法2.1、表单搭建1&#xff09;新建表单【工资表】&#xff0c;字段设置如下&#xff1b;名称类型名称类型人员资料分…