Spring5底层原理之BeanFactory与ApplicationContext

news2024/11/24 12:54:26

目录

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路径结果如下 

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

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

相关文章

驱动开发-1

一、驱动课程大纲 内核模块字符设备驱动中断 二、ARM裸机代码和驱动有什么区别&#xff1f; 1、共同点&#xff1a; 都能够操作硬件 2、不同点&#xff1a; 1&#xff09;裸机就是用C语言给对应的寄存器里面写值&#xff0c;驱动是按照一定的套路往寄存器里面写值 2&#xff09…

为什么有的开关电源需要加自举电容?

一、什么是自举电路&#xff1f; 1.1 自举的概念 首先&#xff0c;自举电路也叫升压电路&#xff0c;是利用自举升压二极管&#xff0c;自举升压电容等电子元件&#xff0c;使电容放电电压和电源电压叠加&#xff0c;从而使电压升高。有的电路升高的电压能达到数倍电源电压。…

阶段十-物业项目

可能遇到的错误&#xff1a; 解决jdk17javax.xml.bind.DatatypeConverter错误 <!--解决jdk17javax.xml.bind.DatatypeConverter错误--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>…

git 使用方法自用(勿进)本地开发分支推上线上开发分支

一、//查看状态 1.git status 二、//查看改了哪个文件夹 1.git diff 2.//会出现改了哪个文件夹src/components/partials/Slider.js 三、//查看改了的文件夹里面具体改了啥内容 1.git diff src/components/partials/Slider.js 四、提交所有 1. git add . 五、写备注…

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

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

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

为什么c++的开源库那么少?

为什么c的开源库那么少&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「 C的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&#xff01;…

Skywalking 中 Agent 自动同步配置源码解析

文章目录 前言正文实现架构实现模型OAP 同步 ApolloConfigWatcherRegisterConfigChangeWatcher Agent 侧 前言 本文代码 OAP 基于 v9.7&#xff0c;Java Agent 基于 v9.1&#xff0c;配置中心使用 apollo。 看本文需要配合代码“食用”。 正文 Skywalking 中就使用这种模型…

基于SSM在线协同过滤汽车推荐销售系统

SSM毕设分享 基于SSM在线协同过滤汽车推荐销售系统 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【】 师兄根据实现的难度和等级对项目进行评分(最低0分&#xff0c;满分5分) 难度系数&#xff1a;3分 工作…

Springsecurty【2】认证连接MySQL

1.前期准备 基于Spring Initializr创建SpringBoot项目&#xff08;基于SpringBoot 2.7.12版本&#xff09;&#xff0c;实现与MyBatisPlus的项目整合。分别导入&#xff1a;CodeGenerator和MyBatisPlusConfig。 CodeGenerator&#xff1a;用于MybatisPlus代码生成&#xff1b;…

JavaScript原型,原型链 ? 有什么特点?

一、原型 JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象 当试图访问一个对象的属性时&#xff0c;它不仅仅在该对象上搜寻&#xff0c;还会搜寻该对象的原型&#xff0c;以及该对象的原型的原型&#xff0c;依次层层向上搜索&#xff0c;直到找到一个…

<各国地图轮廓app>技术支持

如在app使用过程中遇到任何问题&#xff0c;请与开发者联系caohechunhotmail.com

贴片晶振无源石英谐振器直插晶振

贴片晶振 贴片晶振3.579M~25MHz无源石英谐振器直插晶振 文章目录 贴片晶振前言一、贴片晶振3.579M~25MHz无源石英谐振器直插晶振二、属性三、技术参数总结前言 贴片晶振(Surface Mount Crystal Oscillator)是一种采用表面贴装技术进行安装的晶振。它的主要特点是封装小巧、安…

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

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

SpringBoot增删改查接口实例

前言 增删改查是后端最基本的技能。下面我就带领小伙伴们通过一个简单的示例来讲解SpringBoot的增删改查。Spring Boot框架层次从上至下可分为5层&#xff1a;分别为View层&#xff0c;Controller层&#xff0c;Service层&#xff0c;Mapper层&#xff0c;Model层 1. View层&a…

机械臂快速接触刚性环境阻抗对相互作用力的影响

当机械臂快速接触刚性环境时&#xff0c;阻抗对相互作用力的影响尤为显著。由于刚性环境对机械臂产生的阻力&#xff0c;机械臂在接触时会受到一个与运动方向相反的作用力&#xff0c;即接触力。阻抗参数的设置对接触力的大小具有重要影响。 一方面&#xff0c;阻尼参数决定了…

求三角形面积 C语言xdoj91

题目描述&#xff1a; 输入三角形的三边长&#xff0c;计算三角形的面积&#xff0c;结果保留两位小数。 边长分别为a,b,c&#xff0c;三角形的面积公式为ssqrt(p(p-a)(p-b)(p-c)),其中p(abc)/2。 输入格式&#xff1a;共一行&#xff0c;输入三个数&#xff0c;保留两位小数&a…

SQL注入【sqli靶场第23-28关】(七)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、总体思路 先确认是否可以SQL注入&#xff0c;使用单…

Apache Commons IO: 简化文件和IO操作

第1章&#xff1a;引言 咱们在做Java编程的时候&#xff0c;经常会遇到各种文件操作和输入输出&#xff08;IO&#xff09;的问题。不论是读取一个配置文件&#xff0c;还是把数据写入日志&#xff0c;这些看似简单的任务有时候会让人头疼。传统的Java IO操作&#xff0c;虽然…

多维时序 | MATLAB实CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测预测效果基本介…