spring中的容器接口的实现类和功能

容器实现

BeanFactory 实现

这里我们就来一步步实现BeanFactory的功能。

首先创建我们需要的类

@Configuration
static class Config{@Beanpublic Bean1 bean1(){return new Bean1();}@Beanpublic Bean2 bean2(){return new Bean2();}}static class Bean1{private static final Logger log = LoggerFactory.getLogger(Bean1.class);public Bean1() {log.debug("构造了Bean1()");}@Autowiredprivate Bean2 bean2;public Bean2 getBean2(){return bean2;}
}static class Bean2{private static final Logger log = LoggerFactory.getLogger(Bean2.class);public Bean2() {log.debug("构造了Bean2()");}
}

然后我们创建BeanFactory的一个实现类DefaultListableBeanFactory。这是BeanFactory的重要实现接口。
这个BeanFactory创建完成之后,里面是没有任何Bean的信息的,它是通过保Bean的定义,然后再有需要的时候创建Bean交给我们使用,这也是控制反转的原理。
这里面的Bean的定义信息其实也是一个类就是BeanDefinition,主要描述
Beanclassscope、初始化,销毁。
所以接下来我们来创建BeanFactory并创建Config的Bean的定义交给容器管理。

// 创建容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建 config 的定义信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
// 容器中注册bean的信息,第一个参数为bean的名称
beanFactory.registerBeanDefinition("config", beanDefinition);

接下来我们来打印一下容器中所有的Bean的定义信息。

for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

输出:

config

我们发现其实我们Config类中还通过@Bean注解定义了两个Bean但是为什么这里并没有加入到容器当中呢?

这是因为现在这个容器并没有解析@Bean注解的能力,这个能力是通过一个 bean工厂后处理器来实现的,这个处理器就是internalConfigurationAnnotationProcessor

这个后处理器怎么加呢?

我们可以通过Spring提供的 AnnotationConfigUtils工具类的 registerAnnotationConfigProcessors 方法来将这个类注册到容器中,所以这个方法的参数就填入beanfactory就可以了。

// 添加 BeanFactory 后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

bean 工厂后处理器添加到容器中之后,我们打印一下容器中的BeanDefinition查看一下。

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

我们发现加入了很多Bean的定义,这些后面再逐渐介绍,其中第一个就是我们需要的。

但是我们虽然加入到了容器中,就像我们之前说的,容器一开始只是保存这些Bean的定义,只有当我们需要的时候才创建。所以这里我们需要启动这个后处理器。

首先我们需要获取到这个后处理器,像这些bean工厂的后处理器都有一个共同的类型,那就是BeanFactoryPostProcessor,我们可以通过类型获取到这些处理器,然后再依次的在容器中添加这些功能。

// 启动 BeanFactory 后处理器
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}

这时我们在打印容器中Bean的信息

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2

发现 bean1、bean2 也加入了容器。

既然bean1、bean2已经加入到了容器,并且bean1也依赖注入了bean2,那么我们应该也可以获取到bean2。

我们来尝试一下:

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出:

[DEBUG] 13:12:16.725 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
null

我们发现 bean1 通过构造方法被创建了,但是 bean 2 为 null

这是因为当前的beanFactory并没有解析@AutoWired@Resource这两个注解的能力,这两个注解也是后处理对容器增强的功能,这种后处理器名为 bean 后处理器。而解析这两个的后处理器就是上面我们加入到容器中的一堆后处理器中的第二个和第三个:internalAutowiredAnnotationProcessorinternalCommonAnnotationProcessor,分别用来解析@AutoWired@Resource的。

他们也是属于同一个类型BeanPostProcessor,我们可以通过像启动 bean工厂后处理器的方式一样,启动 bean 后处理器

// 启动 Bean 后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

输出:

[DEBUG] 13:20:50.173 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:20:50.187 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
com.sahuid.a02.TestBeanFactory$Bean2@63355449

这里我们可以发现,目前的容器都是我们在我们使用的时候才帮我们创建出来对象。

for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}System.out.println("=======");System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出:

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
=======
[DEBUG] 13:21:22.509 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:21:22.524 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
com.sahuid.a02.TestBeanFactory$Bean2@63355449

里我们也可以设置在加载 bean 定义的时候就帮我们创建好对象。
加入一个这个方法preInstantiateSingletons即可

beanFactory.preInstantiateSingletons();

输出:

[DEBUG] 13:24:37.564 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:24:37.578 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
=======
com.sahuid.a02.TestBeanFactory$Bean2@50b472aa

这里我们就可以总结一下:
我们可以 发现beanFactory不会做的事情:

  • 不会主动调用 beanFactory 后处理器
  • 不会主动添加 bean 后处理器
  • 不会主动初始化单例
  • 还不会解析 ${} 和 #{}

ApplicationContext 实现

这里主要介绍4种ApplicationContext实现:

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext
  • AnnotationConfigApplicationContext
  • AnnotationConfigServletWebServerApplicationContext

ClassPathXmlApplicationContext 实现

较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建

首先创建相关的类

static class Bean1 {}static class Bean2 {private Bean1 bean1;public Bean1 getBean1() {return bean1;}public void setBean1(Bean1 bean1) {this.bean1 = bean1;}
}

这里我们在resource目录下创建一个配置 bean 的 xml 文件

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bean1" class="com.sahuid.a02.A02Application.Bean1"/><bean id="bean2" class="com.sahuid.a02.A02Application.Bean2"><property name="bean1" ref="bean1"/></bean>
</beans>

然后我们就创建这个类读取配置文件即可

ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("b01.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

输出:

bean1
bean2

FileSystemXmlApplicationContext 实现

基于磁盘路线下 xml 格式的配置文件来创建

这里可以写相对路径也可以写相对路径

FileSystemXmlApplicationContext context
= new FileSystemXmlApplicationContext("D:\\Code\\spring-study\\show\\src\\main\\resources\\b01.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

输出:

bean1
bean2

这两种方式底层是如何实现的呢?

其实就是还是创建的DefaultListableBeanFactory,只不过增加了一个XmlBeanDefinitionReader类,用这个类来读取xml文件的bean的配置信息,然后写入到beanfactory中。

具体模拟实现:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
// 读取之后
System.out.println("读取之后");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

这两个方式的区别就在于读取方式
通过classPath的是:

reader.loadBeanDefinitions(new ClassPathResource());

过fileSystem的是:

reader.loadBeanDefinitions(new FileSystemResource());

AnnotationConfigApplicationContext

较为经典的容器, 基于 Java 配置类来创建

这里我们先创建一个配置类,然后创建bean1和bean2这两个bean

@Configuration
static class Config{@Beanpublic Bean1 bean1(){return new Bean1();}@Beanpublic Bean2 bean2(){return new Bean2();}
}

然后传入到AnnotationConfigServletWebServerApplicationContext的构造函数参数中

AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

输出:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
a02Application.Config
bean1
bean2

我们可以发现这个ApplicationContext的实现类,自动添加上了beanfactory 和 bean 的后处理器,所以可以自己解析@Bean注解

AnnotationConfigServletWebServerApplicationContext

较为经典的容器,基于 Java 配置类来创建,用于 web 环境

这是我们用于web环境的容器,这个容器也需要一个配置类,不过这个配置类配置的Bean 比较多。

@Configuration
static class WebConfig{@Beanpublic ServletWebServerFactory servletWebServerFactory(){return new TomcatServletWebServerFactory(10000);}@Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}@Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){return new DispatcherServletRegistrationBean(dispatcherServlet, "/");}@Bean("/hello")public Controller controller1(){return new Controller() {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {response.getWriter().println("hello");return null;}};}
}

ServletWebServerFactory:这个功能相当于是指定当前程序运行在什么服务上,我们都知道 Spring Boot 有内置的 Tomcat 服务器,所以我们在这里就创建一个 Tomcat 的服务,参数可以指定端口。

DispatcherServlet:这是在 Spring MVC 中非常重要的一个类,所有的请求第一步都是先进入到这个类中,相当于这个类是服务的一个入口,然后通过这个类再去匹配不同的请求。

DispatcherServletRegistrationBean:上面我们创建了服务和入口,我们需要将两个类进行绑定,而这个就是用来绑定两者的关系,第二个参数是个访问路径,带有/的都能访问进来

Controller:这个就相当于是一个我们的Controller的接口,只不过这里直接通过Bean管理了,然后访问@Bean后面的值就可以访问到接口。

接下来我们创建出来容器

AnnotationConfigServletWebServerApplicationContext context
= new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);
}

进行页面访问:
在这里插入图片描述

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

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

相关文章

【Linux】Shell 编程规范及检查工具推荐

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】&#xff08;全…

【RH124】解释Linux文件系统权限

RH124教材中控制对文件的访问一章中有一道解释Linux文件系统权限的测验题&#xff0c;可以一起来看看&#xff1a; 一、权限解释 这是通过 ls -l 命令查看的结果。它显示了文件或目录的权限、拥有者、所属组等信息。 1、长列表的第一个字符表示文件类型&#xff1a; -是常…

【C语言零基础入门篇 - 16】:栈和队列

文章目录 栈和队列栈栈功能的实现源代码 队列队列功能的实现源代码 栈和队列 栈 什么是栈&#xff1a;功能受限的线性数据结构 栈的特点&#xff1a;先进后出 。例如&#xff1a;仓库进货、出货。 栈只有一个开口&#xff0c;先进去的数据在栈底&#xff08;bottom&#xf…

STM32篇:STM32CubeMX的安装

一.介绍与安装 1.作用 通过界面的方式&#xff0c;快速生成工程文件。 2.下载 官网 https://www.st.com/zh/development-tools/stm32cubemx.html#overview 3.安装 一路下一步&#xff0c;建议不要安装在C盘 4.配置 更新固件包位置&#xff08;比较大&#xff0c;默认在…

LeetCode 257. 二叉树的所有路径(回溯详解)

文章目录 LeetCode 257. 二叉树的所有路径思路递归版本一:非常明确的回溯代码版本二&#xff1a;精简的回溯代码 LeetCode 257. 二叉树的所有路径 LeetCode 257. 二叉树的所有路径 给定一个二叉树&#xff0c;返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节…

全网最适合入门的面向对象编程教程:51 Python函数方法与接口-使用Zope实现接口

全网最适合入门的面向对象编程教程&#xff1a;51 Python 函数方法与接口-使用 Zope 实现接口 摘要&#xff1a; 在 Python 中&#xff0c;Zope 提供了一种机制来定义和实现接口。Zope 的接口模块通常用于创建可重用的组件&#xff0c;并确保组件遵循特定的接口规范。 原文链…

力扣 209.长度最小的子数组

一、长度最小的子数组 二、解题思路 采用滑动窗口的思路&#xff0c;详细见代码。 三、代码 class Solution {public int minSubArrayLen(int target, int[] nums) {int n nums.length, left 0, right 0, sum 0;int ans n 1; for (right 0; right < n; right ) { …

【二等奖论文】2024年华为杯研赛D题成品论文(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 点击链接获取【2024华为杯研赛资料汇总】&#xff1a; https://qm.qq.com/q/jTIeGzwkSchttps://qm.qq.com/q/jTIeGzwkSc 题 目&#xff1a; 大数据驱动的…

一劳永逸:用脚本实现夸克网盘内容自动更新

系统环境&#xff1a;debian/ubuntu 、 安装了python3 原作者项目&#xff1a;https://github.com/Cp0204/quark-auto-save 感谢 缘起 我喜欢看电影追剧&#xff0c;会经常转存一些资源到夸克网盘&#xff0c;电影还好&#xff0c;如果是电视剧&#xff0c;麻烦就来了。 对于一…

深度学习-卷积神经网络(CNN)

文章目录 一、网络构造1. 卷积层&#xff08;Convolutional Layer&#xff09;&#xff08;1&#xff09;卷积&#xff08;2&#xff09;特征图计算公式&#xff08;3&#xff09;三通道卷积 2. 激活函数&#xff08;Activation Function&#xff09;3. 池化层&#xff08;Pool…

【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)

文章目录 【JUC并发编程系列】深入理解Java并发机制&#xff1a;线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)1. 基本 API 介绍2. 简单用法3. 应用场景4. Threadlocal与Synchronized区别5. 内存溢出和内存泄漏5.2 内存溢出 (Memory Overflow)5.2 内存泄…

全栈项目小组【算法赛】题目及解题

题目&#xff1a;全栈项目小组【算法赛】 题目&#xff1a; 解题思路 1.遍历简历信息&#xff1a;我们需要读取所有简历&#xff0c;根据期望薪资和岗位类型进行分类和统计。 2.分类统计&#xff1a;使用哈希表来存储每个薪资下的前端&#xff08;F&#xff09;和后端&#…

【线程】线程的同步

本文重点&#xff1a;理解条件变量和生产者消费者模型 同步是在保证数据安全的情况下&#xff0c;让我们的线程访问资源具有一定的顺序性 条件变量cond 当一个线程互斥地访问某个变量时&#xff0c;它可能发现在其它线程改变状态之前&#xff0c;它什么也做不了&#xff0c;…

window系统DockerDesktop 部署windows容器

目录 参考文献1、安装Docker Desktop1.1 下载安装包1.2 安装教程1.3 异常解决 2、安装windows容器2.1 先启动DockerDesktop 软件界面2.2 检查docker版本2.3 拉取windows镜像 参考文献 windows容器docker中文官网 Docker: windows下跑windows镜像 1、安装Docker Desktop 1.1 …

SSM框架VUE电影售票管理系统开发mysql数据库redis设计java编程计算机网页源码maven项目

一、源码特点 smm VUE电影售票管理系统是一套完善的完整信息管理类型系统&#xff0c;结合SSM框架和VUE、redis完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用ssm框架&#xff08;MVC模式开发&#xff09;&#xff0c;系 统具有完整的源代码和数据库&#…

【C语言零基础入门篇 - 17】:排序算法

文章目录 排序算法排序的基本概念冒泡排序选择排序插入排序 排序算法 排序的基本概念 1、什么是排序&#xff1f; 排序是指把一组数据以某种关系&#xff08;递增或递减&#xff09;按顺序排列起来的一种算法。 例如&#xff1a;数列 8、3、5、6、2、9、1、0、4、7 递增排序…

深入浅出:Eclipse 中配置 Maven 与 Spark 应用开发全指南

Spark 安装配置 1.在 Eclipse 中配置 Maven Eclipse 中默认自带 Maven 插件&#xff0c;但是自带的 Maven 插件不能修改本地仓库&#xff0c;所 以通常我们不使用自带的 Maven &#xff0c;而是使用自己安装的&#xff0c;在 Eclipse 中配置 Maven 的 步骤如下&#xff1a;…

Nature Electronics |无感佩戴的纤维基电子皮肤(柔性半导体器件/柔性健康监测/电子皮肤/柔性传感/纤维器件)

英国剑桥大学Yan Yan Shery Huang课题组,在《Nature Electronics 》上发布了一篇题为“Imperceptible augmentation of living systems with organic bioelectronic fibres”的论文,第一作者为王文宇博士(Wenyu Wang),论文内容如下: 一、 摘要 利用电子技术对人类皮肤和…

0-PCIE串行高速接口架构介绍

随着计算机技术日新月异的发展&#xff0c;对于I/O传输速率的需求愈发提高&#xff0c;PCI总线由于是并行传输&#xff0c;在时钟频率提高之后会带来信号偏移和串扰的问题从而使信号衰减失真&#xff0c;同时在数据传输速率不断提高之后PCI总线还面临着管脚限制&#xff0c;传输…

哈电集团数智化转型新突破:浪潮信息SAP HANA驱动数智升级

浪潮信息SAP HANA一体化解决方案&#xff0c;鼎力推动哈尔滨电气集团有限公司&#xff08;哈电集团&#xff09;取得了数字化转型的非凡成就。该定制化方案不仅促使哈电集团业财一体化程度显著跃升&#xff0c;突破70%大关&#xff0c;更确保了库存管理的绝对精准&#xff0c;库…