Spring5底层原理之BeanFactory与ApplicationContext

目录

BeanFactory与ApplicationContext

BeanFactory

ApplicationContext

容器实现

BeanFactory实现

ApplicationContext实现

ClassPathXmlApplicationContext的实现

AnnotationConfigApplicationContext的实现

AnnotationConfigServletWebServerApplicationContext的实现


BeanFactory与ApplicationContext

BeanFactory

BeanFactory是ApplicationContext的父接口。是Spring的核心容器,ApplicationContext的主要的方法均是调用BeanFactory的方法。比如说下面获取bean对象案例中

@SpringBootApplication
public class CodeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
        ctx.getBean("abc");
    }
}

通过Ctrl+alt+b键可以看到具体的实现方法如下,先获取BeanFactory对象后调用BeanFactory对象的getBean()方法。

	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

BeanFactory表面只能getBean(),实际上控制反转,基本的依赖注入、直至Bean的生命周期的各种功能,都由它的实现类体提供。

ApplicationContext

相比较BeanFactory,ApplicationContext多了哪些功能?

由类图可以看出,ApplicationContext除了继承BeanFactory的两个接口,也继承了四个其他接口。

接下来分别用代码查看具体做了什么。

首先是翻译。不过该翻译需要自己导入文本

首先在resource包下创建messages开头的properties文件。在里面编写key=value样式的翻译

在主程序中编写代码。可以正常输出翻译后的内容。

第二个就是路径查找资源

第三个是从环境中获取配置值

第四个是发布事件

这个可以用于解耦,比如用户注册成功后,可能需要发送短信通知,又或是邮件通知。不知道如何去通知时,不能随意更改注册部分的功能,这时可以通过发布事件来处理。

首先自定义事件,需要继承ApplicationEvent类。

//自定义事件,需要继承ApplicationEvent
public class UserRegisterEvent extends ApplicationEvent {
    public UserRegisterEvent(Object source) {
        super(source);
    }
}
@Component
public class Component1 {

    @Autowired
    private ApplicationEventPublisher publisher;

    public void register(){
        System.out.println("用户注册");
        //发布者发布一个UserRegisterEvent事件,从this这个对象发出
        publisher.publishEvent(new UserRegisterEvent(this));
    }
}
@Component
public class Component2 {
    @EventListener
    public void send(UserRegisterEvent event){
        System.out.println("发送信息"+event);
    }
}
@SpringBootApplication
public class CodeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
        Component1 publisher = ctx.getBean(Component1.class);
        publisher.register();
    }
}

运行结果如下 

用户注册

发送信息com.zmt.test.UserRegisterEvent[source=com.zmt.test.Component1@1d3ac898]

容器实现

BeanFactory实现

从空项目中从头实现BeanFactory

public class TestBeanFactory {
    public static void main(String[] args) {
        /**
         * 从头实现BeanFactory
         */
        //创建bean工厂
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config",bean);
        //接下来获取查看哪些bean被加载
        String[] names = beanFactory.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1(){
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(){
            return new Bean2();
        }
    }

    static class Bean1 {
        @Autowired
        private Bean2 bean2;

        public Bean1() {
            System.out.println("构造bean1");
        }

        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        public Bean2() {
            System.out.println("构造bean2");
        }
    }
}

运行结果如下 

config

由此可以看出,这些注解都没有被解析。只有初始时定义的bean被加载。想要解析注解,需要给beanFactory添加一个后处理器。

运行结果如下 

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

可以看到添加了五个处理器。接下来调用这些处理器去解析注解

运行结果如下 

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中的getBean2方法发现

bean2并没有被注入,很显然时@Autowried没有起作用。我们需要再写一个Bean后处理器(针对bean的生命周期各个阶段进行扩展)。

也从另一方面体现了,只有当只用bean对象时才会被创建,不使用时只会在beanFactory中保存bean信息。

如果需要提前加载bean,则需要调用方法beanFactory.preInstantiateSingletons()。

具体代码如下

public class TestBeanFactory {
    public static void main(String[] args) {
        /**
         * 从头实现BeanFactory
         */
        //创建bean工厂
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //将beanDefinition注册到beanFactory中
        beanFactory.registerBeanDefinition("config", beanDefinition);
        //为BeanFactory添加一些常用的Bean工厂后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        //调用bean工厂后处理器的方法
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(processor -> processor.postProcessBeanFactory(beanFactory));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        //将BeanDefinition中的Bean后处理器添加bean后处理器
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        //单例对象事先创建,而不是创建时才创建bean对象
        beanFactory.preInstantiateSingletons();
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {
        @Autowired
        private Bean2 bean2;

        public Bean1() {
            System.out.println("构造bean1");
        }

        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        public Bean2() {
            System.out.println("构造bean2");
        }
    }
}

从上述代码中我们可以得到以下结论

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

ApplicationContext实现

ClassPathXmlApplicationContext的实现

<?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.zmt.test3.Bean1"/>
  <bean id="bean2" class="com.zmt.test3.Bean2">
    <property name="bean1" ref="bean1"/>
  </bean>
</beans>
public static void testClassPathXmlApplication(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}

运行结果如下 

bean1

bean2

com.zmt.test3.Bean1@5f3a4b84

applicationContext读取xml中的配置具体操作如下

public static void main(String[] args){
    //创建BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    //创建xml读取器
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    System.out.println("读取配置文件前");
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    //读取xml文件
    reader.loadBeanDefinitions("application.xml");
    System.out.println("读取配置文件后");
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
}

运行结果如下  

读取配置文件前

20:52:54.539 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [application.xml]

读取配置文件后

bean1

bean2

读取xml文件的实现方式还有一种FileSystemXmlApplicationContext,这种实现方式与ClassPathXmlApplicationContext的区别在于前者可以指定配置文件在磁盘中的路径或是src下的路径,其他与后者无异。

AnnotationConfigApplicationContext的实现

@Configuration
public class Config{
    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }

    //也可以通过添加@Autowired注解在Bean2中注入bean1对象
    @Bean
    public Bean2 bean2(Bean1 bean1){
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }
}
public static void testAnnotationConfigApplicationContext(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}

运行结果如下  

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

codeApplication.Config

bean1

bean2

com.zmt.test3.Bean1@96def03

由此可以看出,相比于ClassPahtXmlApplicationContext中,AnnotationConfigApplicationContext会自动添加常用的后处理器并主动调用这些后处理器

AnnotationConfigServletWebServerApplicationContext的实现

这个是用于web环境下的容器实现,既支持Java配置类又支持servlet与servlet的web容器

public static void testAnnotationConfigServletWebServerApplicationContext(){
    AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
}
@Configuration
public class WebConfig{
    //创建一个web容器
    @Bean
    public ServletWebServerFactory servletWebServerFactory(){
        return new TomcatServletWebServerFactory();
    }
    //前端控制器,用来分发Servlet
    @Bean
    public DispatcherServlet dispatcherServlet(){
        return new DispatcherServlet();
    }
    //将前端控制器注册到Web容器当中
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
        return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
    }
    //创建一个控制器用来处理前端请求
    @Bean("/hello")//当Bean注解中以/开头时,后面的内容除了作为bean名称外,还要充当网络访问路径
    public Controller controller1(){
        return new Controller() {
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().print("hello");
                return null;
            }
        };
    }
}

运行结果如下  

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

codeApplication.WebConfig

servletWebServerFactory

dispatcherServlet

registrationBean

/hello

访问/hello路径结果如下 

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

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

相关文章

系列一、MQ简介

一、MQ简介 1.1、概述 MQ&#xff08;Message Queue&#xff09;&#xff0c;是一种提供消息队列服务的中间件&#xff0c;也称为消息中间件&#xff0c;是一套提供了消息&#xff08;消息即数据&#xff0c;一般消息的体量不会很大&#xff09;生产、存储、消费全过程的API软…

【计算机网络】网络层——IP协议

目录 一. 基本概念 二. 协议报文格式 三. 网段划分 1. 第一次划分 2. CIDR方案 3. 特殊的IP地址 四. IP地址不足 1. 私有IP和公网IP 2. DHCP协议 3. 路由器 4. NAT技术 内网穿透(NAT穿透) 五. 路由转发 路由表生成算法 结束语 一. 基本概念 IP指网络互连协议…

python时间处理方法和模块

在 Python 中&#xff0c;有一些内置的模块和库&#xff0c;可以帮助我们处理日期和时间的表示、计算和转换。 1. 时间模块&#xff08;time&#xff09; Python 的 time 模块提供了一系列函数来处理时间相关的操作。通过这个模块&#xff0c;可以获取当前时间、睡眠指定时间…

.halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

尊敬的读者&#xff1a; 网络安全是当今数字时代的一大挑战&#xff0c;各种勒索病毒如.halo病毒层出不穷&#xff0c;对用户和企业的数据安全构成了严重威胁。本文将介绍.halo勒索病毒&#xff0c;以及如何恢复被其加密的数据文件&#xff0c;同时提供预防措施。在面对被勒索…

计算机视觉基础(11)——语义分割和实例分割

前言 在这节课&#xff0c;我们将学习语义分割和实例分割。在语义分割中&#xff0c;我们需要重点掌握语义分割的概念、常用数据集、评价指标&#xff08;IoU&#xff09;以及经典的语义分割方法&#xff08;Deeplab系列&#xff09;&#xff1b;在实例分割中&#xff0c;需要知…

【黑马甄选离线数仓day10_会员主题域开发_DWS和ADS层】

day10_会员主题域开发 会员主题_DWS和ADS层 DWS层开发 门店会员分类天表: 维度指标: 指标&#xff1a;新增注册会员数、累计注册会员数、新增消费会员数、累计消费会员数、新增复购会员数、累计复购会员数、活跃会员数、沉睡会员数、会员消费金额 维度: 时间维度&#xff08…

Qt 多媒体音频模拟按钮发音(音视频启动)

## 项目演示 平台:windows或者ubuntu 要求:平台需要支持音频播放功能 文件格式:.wav 可以使用剪映生成,音频部分,我这里是简短的音乐 # Qt 多媒体简介 Qt QSound是Qt框架中的一个类,用于播放音频文件。它可以在Qt应用程序中实现简单的音频播放功能,包括播放、暂停和…

连锁便利店管理系统有什么用

连锁便利店管理系统对于连锁便利店的运营和管理非常有用。以下是一些常见的用途&#xff1a; 1. 库存管理&#xff1a;连锁便利店通常需要管理多个门店的库存&#xff0c;管理系统可以帮助实时掌握各个门店的库存情况&#xff0c;包括商品数量、进货记录、库存调拨等。这样可以…

Vue2从源码角度来回答一些常见的问题

1.请说一下Vue2响应式数据的理解&#xff08;先知道基本的问题在哪里&#xff0c;源码的角度来回答&#xff0c;用的时候会有哪些问题&#xff09; 可以监控一个数据的修改和获取操作。针对对象格式会给每个对象的属性进行劫持 Object.defineProperty 源码层面 initData ->…

设计模式--外观模式

实验12&#xff1a;外观模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解外观模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用外观模式解决实际问题。 [实验任务]&#xff1a;计算机开启 在计算机主机(Main…

VSCode中配置prettier和ESLint

文章目录 了解ESLint和Prettier的作用prettier配置ESLint配置常见问答ESLint 和Prettier 有什么区别&#xff1f;为什么我应该同时使用ESLint 和Prettier&#xff1f;在使用ESLint 和Prettier 时&#xff0c;有可能出现它们之间的规则冲突吗&#xff1f;我已经在项目中使用了ES…

微前端——无界wujie

B站课程视频 课程视频 课程课件笔记&#xff1a; 1.微前端 2.无界 现有的微前端框架&#xff1a;iframe、qiankun、Micro-app&#xff08;京东&#xff09;、EMP&#xff08;百度&#xff09;、无届 前置 初始化 新建一个文件夹 1.通过npm i typescript -g安装ts 2.然后可…

软件测试工程师简历项目经验怎么写?--9999个已成功入职的软件测试工程师真实简历

简历是我们求职的第一步&#xff0c;也是非常重要的一步。 青云叔叔看过太多简历&#xff0c;最快3秒就淘汰一份简历&#xff0c;因为其实我们每天要收到很多简历进行筛选&#xff0c;那么面试官其实也是会很快进行对简历进行判断的&#xff0c;如果你对简历写的一塌糊涂&…

8、SpringCloud高频面试题-版本1

1、SpringCloud组件有哪些 SpringCloud 是一系列框架的有序集合。它利用 SpringBoot 的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用 SpringBoot 的开发风格做到一键启…

第十三节TypeScript 元组

1、简介 我们知道数组中元素的数据类型一般都是相同的&#xff08;any[]类型的数组可以不同&#xff09;&#xff0c;如果存储的元素类型不同&#xff0c;则需要使用元组。 元组中允许存储不同类型的元素&#xff0c;元组可以作为参数传递给函数。2、创建元组的语法格式&#x…

MATLAB Mobile - 使用预训练网络对手机拍摄的图像进行分类

系列文章目录 前言 此示例说明如何使用深度学习对移动设备摄像头采集的图像进行分类。 在您的移动设备上安装和设置 MATLAB Mobile™。然后&#xff0c;从 MATLAB Mobile 的“设置”登录 MathWorks Cloud。 在您的设备上启动 MATLAB Mobile。 一、在您的设备上安装 MATLAB M…

IDEA控制台乱码

报错情况&#xff1a; 报错原因&#xff1a;Idea的vm用的编码格式不一致&#xff1a;需要修改为UTF-8 你看Tomcat我之前下在后修改果&#xff0c;就没有报错&#xff0c;新人刚下载也有乱码问题 问题解决&#xff1a; 按我步骤来一定对 下面这俩文件打开输入&#xff1a; -D…

医疗机构远程视频监控集中管理,贝锐蒲公英提供一站式解决方案

上海某企业专业致力于医疗软件、家居智能化研发、设计、销售、集成及实施&#xff0c;企业主营业务之一为医疗软件&#xff0c;涉及PACS/RIS/WEB/HIS、示教系统等方面的医院信息化建设。 在实际应用、部署过程中&#xff0c;需要实现各地区分院与总院间的数据库互相访问、视频数…

Portainer.io:让容器管理变得更加直观

在现代软件开发和部署中&#xff0c;容器化技术已经变得越来越流行。Docker 是其中一种领先的容器化平台&#xff0c;而 Portainer.io 则是一个优秀的管理工具&#xff0c;使得 Docker 的使用变得更加简单和可视化。本文将介绍 Portainer.io 的基本功能和如何在 Docker 上安装和…