第二讲-BeanFactory和ApplicationContext实现的特点
第二讲 容器的实现
1. BeanFactory 实现的特点
首先创建一个BeanFactory的实例,在这个示例中田间一些BeanDfinition对象,该对象主要包含bean的定义信息,例如bean的初始化方法,销毁方法,类型,作用域等等
package com.cherry.chapter1.a02;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
public class TestBeanFactory {
public static void main(String[] args) {
// 创建一个 beanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 添加一些bean的定义(描述信息)(类型,作用域,初始化方法。销毁方法等等)
}
}
接下来我们编写一个类Config.java
,这个类包含获取Bean1和Bean2的方法:
class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
Bean1和Bean2代码如下:
class Bean1{
private Logger logger = LoggerFactory.getLogger(Bean1.class);
public Bean1(){
logger.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2(){
return bean2;
}
}
class Bean2{
private Logger logger = LoggerFactory.getLogger(Bean2.class);
public Bean2(){
logger.debug("构造 Bean2()");
}
}
为Config生成BeanDefinition对象,然后加载到beanFactory中:
// 创建一个 beanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 添加一些bean的定义(描述信息)(类型,作用域,初始化方法。销毁方法等等)
// 创建一个 BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
// 讲beanDefinition注册到beanFactory中
beanFactory.registerBeanDefinition("config",beanDefinition);
// 此时beanFactory中存在了一个 bean:config
// 从容器中获取该Bean
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
运行结果如下:
config
我们发现注入成功。但是我们发现,我们并没有获取到Bean1和Bean2,这是为什么呢?这是因为此时的BeanFactory此时缺少了解析@Configuration
注解的能力,为了让BeanFactory能够支持解析@Configuration注解的能力,我们可以这样做:
给beanFactory添加一些常用的后处理器后处理器,这些常用的后处理器就是对BeanFactory功能的扩展。
// 给beanFactory添加一些常用的后处理器后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
运行如下:
config
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor # 解析@Configuration注解
org.springframework.context.annotation.internalAutowiredAnnotationProcessor # 解析@Autowired注解
org.springframework.context.annotation.internalCommonAnnotationProcessor # 解析@Resource注解
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
我们此时发现了一些后处理器,其中,internalConfigurationAnnotationProcessor
专门用于解析@Configuration注解。可是此时为什么还没有解析出其它的两个Bean对象呢?这是因为我们此时只是获取到了这些常见的BeanFactory后处理器,并没有将这些处理器加入到加入到BeanFactory中,因此我们需要将这些BeanFactory后处理器加入到BeanFactory中:
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
System.out.println("=============");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
运行结果如下:
bean1
bean2
我们发现已经加载出来了!BeanFactory后处理器的主要功能就是:补充了一些Bean的定义。
既然bean1和bean2已经拿到了,那我们能不能拿出来使用呢?
System.out.println("============");
System.out.println(beanFactory.getBean(Bean1.class));
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
运行结果如下:
============
21:05:05.610 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
21:05:05.612 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
21:05:05.614 [main] DEBUG com.cherry.chapter1.a02.Bean1 - 构造 Bean1()
com.cherry.chapter1.a02.Bean1@4abdb505
null
我们发现Bean1对象已经创建出来了,但是Bean1中依赖注入的Bean2对象并没有获取成功,这是为什么呢?这是因为@utowired
依赖注入的功能也是Spring的扩展功能,我们需要在BeanFactory中加入对@Autowired注解的解析:
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach((beanFactory::addBeanPostProcessor));
System.out.println("=================");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
运行结果如下:
=================
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
注意,这里解释一下bean后处理器和beanFactory后处理器的区别:BeanFactory处理器是对容器BeanFactory功能的扩展,如2Cinfiguration,而Bean后处理器是对的Bean生命周期的各个阶段提供扩展功能,例如@Autowired注解,@Resource注解
当然 BeanDefinietion实例化并不是在容器启动的时候就初始化好的,而是在第一次在被调用时才会被初始化,因此呢,Bean的初始化默认是懒汉模式,也就是懒加载模式,如果我们想在容器初始化的时候就初始化bean对象,而不是在我们调用时才初始化,我们可以调用beanFactory的preInstantiateSingletons()
方法提前完成初始化!
beanFactory.preInstantiateSingletons();
关于BeanFactory的一些总结:
- BeanFactory不会主动加载BeanFactory后处理器
- BeanFactory不会主动加载Bean后处理器
- BeanFactory不会主动完成单例Bean的初始化
- BeannFactory不会解析#{}EL表达式和${}占位符
后处理器排序
Bean后处理器有很多个,那么这些后处理器在加入BeanFactory中有什么顺序要求吗?或者说是哪一个该生效呢? 我们首先编写一段代码:
package com.cherry.chapter1.a02;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
public class Bean5 {
}
class Bean8{
@Autowired
@Resource(name = "bean7")
private Inter bean6;
public Inter getBean6() {
return bean6;
}
public void setBean6(Inter bean6) {
this.bean6 = bean6;
}
}
interface Inter{}
@Component
class Bean6 implements Inter{}
@Component
class Bean7 implements Inter{}
结果为:
bean6
我们都知道,@Autowired注解首先根据Bean的类型来注入该Bean,如果容器中没有该类型会根据Bean的名字注入Bean;而@Resource注解根据name属性注入
这是为什么呢,这就是因为后处理器的优先级有关,哪个处理器的优先级高解析就会使用哪个Bean后处理器!一般来说order数值越小,优先度就越高!
2. ApplicationContext的常见实现和用法
Application继承了BeanFactory,自然也扩展了beanFactory的功能,实现的功能就包括上面BeanFactory中不会主动做的事情!
首先我们看一下ApplicationContext常见的实现类:
2.1 较为经典的容器,基于classpath下xml格式的配置文件来创建
private static void testClasspathXmlApplication(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean22.class).getBean11());
}
编写一个Spring配置文件:
<?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="bean11" class="com.cherry.chapter1.a02.Bean11"/>
<bean id="bean22" class="com.cherry.chapter1.a02.Bean22">
<property name="bean11" ref="bean11"/>
</bean>
</beans>
接着来测试一下:
23:05:25.428 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@69222c14
23:05:25.652 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [bean01.xml]
23:05:25.706 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean11'
23:05:25.723 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean22'
bean11
bean22
com.cherry.chapter1.a02.Bean11@64f6106c
2.2 基于磁盘路径下的 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext(){
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\javaProject\\spring-analyse\\chapter1\\src\\main\\resources\\bean01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
测试如下:
23:27:34.680 [main] DEBUG org.springframework.context.support.FileSystemXmlApplicationContext - Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@69222c14
23:27:34.952 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from file [D:\javaProject\spring-analyse\chapter1\src\main\resources\bean01.xml]
23:27:35.009 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean11'
23:27:35.036 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean22'
bean11
bean22
那么xml文件解析完之后是如何将bean加入到beanFactory中的,这里以ClassPathXmlApplicationContext
为例:
public static void main(String[] args) {
//testFileSystemXmlApplicationContext();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前");
for (String beanName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanName);
}
System.out.println("读取之后...");
// xmlBean阅读器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 加载配置文件
reader.loadBeanDefinitions(new ClassPathResource("bean01.xml"));
for (String beanName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanName);
}
}
运行结果如下:
读取之前
读取之后...
23:37:01.271 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [bean01.xml]
bean11
bean22
2.3 较为经典的容器,基于java配置类来创建
private static void testAnnotationConfigApplicationContext(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String beanName : context.getBeanDefinitionNames()) {
System.out.println(beanName);
}
}
@Configuration
static class Config{
@Bean
public Bean11 bean11(){
return new Bean11();
}
@Bean
public Bean22 bean22(Bean11 bean11){
Bean22 bean22 = new Bean22();
bean22.setBean11(bean11);
return bean22;
}
}
}
测试结果如下:
23:53:31.203 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@123772c4
23:53:31.241 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
23:53:31.503 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
23:53:31.510 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
23:53:31.513 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
23:53:31.518 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
23:53:31.546 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a02Application.Config'
23:53:31.565 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean11'
23:53:31.605 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean22'
23:53:31.614 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'bean22' via factory method to bean named 'bean11'
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
bean11
bean22
我们发现除了加入我们定义的bean以外,还加入了一些常见的Bean Factory Post Processor,这些后处理器可以解析一些常见的注解,这在前面已经分析过,这里就不再分析了。
2.4 较为经典的实现,基于java配置类来创建,用于WEB环境中
private static void testAnnotationServletWebApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context
= new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
@Configuration
static class WebConfig{
// 提供一个WEB容器组件 tomcat
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
// 提供一个DispatcherServlet组件,
@Bean
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
// 将servlet组件注册到tomcat服务器中
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet){
return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
}
// 测试控制器
@Bean("/hello")
public Controller controller1(){
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
测试如下:
七月 21, 2024 12:07:49 上午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
00:07:49.522 [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
00:07:49.523 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Successfully started bean 'webServerStartStop'
00:07:49.524 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Starting beans in phase 2147483647
00:07:49.524 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Successfully started bean 'webServerGracefulShutdown'
我们发现,内置tomcat启动成功,接下来我们通过浏览器访问一下/hello
请求来访问一下:
我们发现访问成功了!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构