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()方法。

	@Overridepublic 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 {@Autowiredprivate ApplicationEventPublisher publisher;public void register(){System.out.println("用户注册");//发布者发布一个UserRegisterEvent事件,从this这个对象发出publisher.publishEvent(new UserRegisterEvent(this));}
}
@Component
public class Component2 {@EventListenerpublic 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);}}@Configurationstatic class Config {@Beanpublic Bean1 bean1(){return new Bean1();}@Beanpublic Bean2 bean2(){return new Bean2();}}static class Bean1 {@Autowiredprivate 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());}@Configurationstatic class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}}static class Bean1 {@Autowiredprivate 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){//创建BeanFactoryDefaultListableBeanFactory 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{@Beanpublic Bean1 bean1(){return new Bean1();}//也可以通过添加@Autowired注解在Bean2中注入bean1对象@Beanpublic 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容器@Beanpublic ServletWebServerFactory servletWebServerFactory(){return new TomcatServletWebServerFactory();}//前端控制器,用来分发Servlet@Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}//将前端控制器注册到Web容器当中@Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){return new DispatcherServletRegistrationBean(dispatcherServlet,"/");}//创建一个控制器用来处理前端请求@Bean("/hello")//当Bean注解中以/开头时,后面的内容除了作为bean名称外,还要充当网络访问路径public Controller controller1(){return new Controller() {@Overridepublic 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.hqwc.cn/news/297464.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【项目管理】CMMI-需求跟踪矩阵模版

需求菜单/功能模块需求名称需求变更类型&#xff08;新增、修改&#xff09;需求状态&#xff08;已建议、已批准、已设计、已实现、已验证、已删除&#xff09;优先级&#xff08;高、中、低&#xff09;软件需求&#xff08;工作产品、章节号&#xff09;概要设计&#xff08…

听GPT 讲Rust源代码--src/tools(25)

File: rust/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs 在Rust源代码中&#xff0c;suspicious_command_arg_space.rs文件位于clippy_lints工具包的methods目录下&#xff0c;用于实现Clippy lint SUSPICIOUS_COMMAND_ARG_SPACE。 Clippy是Ru…

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

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

【C++】开源:FLTK图形界面库配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍FLTK图形界面库配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0…

[c]扫雷

题目描述 扫雷游戏是一款十分经典的单机小游戏。在n行m列的雷区中有一些格子含有地雷&#xff08;称之为地雷格&#xff09;&#xff0c;其他格子不含地雷&#xff08;称之为非地雷格&#xff09;。 玩家翻开一个非地雷格时&#xff0c;该格将会出现一个数字——提示周围格子中…

vmware安装中标麒麟高级服务器操作系统 V7.0

vmware安装中标麒麟高级服务器操作系统 V7.0 1、下载中标麒麟高级服务器操作系统 V7.0镜像2、安装中标麒麟高级服务器操作系统 V7.02.2、安装虚拟机 1、下载中标麒麟高级服务器操作系统 V7.0镜像 官方提供使用通道 访问官网 链接: https://www.kylinos.cn/ 下拉到页面最底部 点…

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

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

工资发放 C语言xdoj92

题目描述&#xff1a; 公司财务要发工资现金&#xff0c;需要提前换取100元、50元、20元、10元、5元和1元的人民币&#xff0c; 请输入工资数&#xff0c;计算张数最少情况下&#xff0c;各自需要多少张。 输入格式&#xff1a;共一行&#xff0c;输入一个正整数。 输出格式&am…

Linux:jumpserver介绍(1)

官方网站 JumpServer - 开源堡垒机 - 官网https://www.jumpserver.org/ JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpServer 帮助企业以更安全的方式管控和登录所有类型的资产&#xff0c;实现事前授权、事中监察、事后审计&…

百分百能遇到的接口自动化测试面试题,看完的现在已经在办理入职了...

1. 什么是接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试是指使用自动化工具对接口进行测试&#xff0c;验证接口的正确性、稳定性和性能等方面的指标。 2. 为什么要进行接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试可以提高测试效率&#xff0c;减…

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

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