第二讲-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请求来访问一下:

image-20240721001243298

我们发现访问成功了!

posted @   LilyFlower  阅读(6)  评论(0编辑  收藏  举报
编辑推荐:
· .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语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示