Spring原理(1)——容器

Spring,容器,原理 · 浏览次数 : 100

小编点评

文章内容主要介绍以下几个主题: 1. Spring容器的配置 2. Bean的定义 3. Annotations的使用 4. Spring容器的加载 5. 注解的处理 文章内容使用简单的排版和注释,能够帮助读者理解Spring容器的配置和使用。文章内容主要通过以下几个方面进行介绍: 1. Spring容器的配置 a. 使用DefaultListableBeanFactory创建容器 b. 使用XmlBeanDefinitionReader加载xml配置 2. Bean的定义 a. 使用@Configuration注解定义Bean b. 使用@Autowired注解进行注入 3. Annotations的使用 a. 使用@ContextAnnotationConfig注解配置 b. 使用@Configuration注解定义事件监听器 4. Spring容器的加载 a. 使用FileSystemXmlApplicationContext加载配置文件 b. 使用AnnotationConfigApplicationContext加载配置 5. 注解的处理 a. 使用@Autowired注解进行注入 b. 使用@Configuration注解定义事件监听器 文章内容通过简单的说明能够帮助读者理解Spring容器的配置和使用。

正文

容器接口

image

BeanFactory

  • 是ApplicationContext的父接口,所有ApplicationContext的实现都组合了BeanFactory。

  • BeanFactory才是Spring的核心容器。

image

从BeanFactory提供的方法来看,主要是从容器中获取Bean。实际上控制反转,依赖注入以及Bean的生命周期管理,都由它的实现类提供。如下展示了BeanFactory其中一个实现类DefaultListableBeanFactory的继承关系。

image

可以看到,它的继承路线上有一个DefaultSingletonBeanRegistry类,这个类我们打开可以看到如下代码段,其中singletonObjects对象正是容器中所有单例对象被保存的地方。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);  // 保存了所有的单例对象

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
	
	...
}

ApplicationContext

image

ApplicationContext继承自多个接口,因此提供了多种能力,具体提供的能力如下代码所示。

@SpringBootApplication
public class SsmpApplication {

    public static void main(String[] args) throws IOException {
        final ConfigurableApplicationContext context = SpringApplication.run(SsmpApplication.class, args);

        // MessageSource接口提供的功能,实现国际化能力
        context.getMessage("hi", null, Locale.CHINA);
        context.getMessage("hi", null, Locale.US);

        // ResourcePatternResolver接口提供的能力,实现读取资源文件的能力
        final Resource[] resources = context.getResources("classpath*:spring.factories");

        // EnvironmentCapable接口提供的能力,实现获取环境参数的能力,可以获取环境变量、配置等
        final String java_home = context.getEnvironment().getProperty("java_home");
        final String port = context.getEnvironment().getProperty("server.port");

        // ApplicationEventPublisher提供的能力,可以发送事件,实现解耦
        context.publishEvent(new MyEvent(context));
    }

    public static class MyEvent extends ApplicationEvent {

        public MyEvent(Object source) {
            super(source);
        }
    }
}

容器实现

BeanFactory实现

以下代码的注释中展示了BeanFactory是如何注册Bean的,以及注册后如何进行后处理的。

点击查看代码
package com.leo.ssmp;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;

@SpringBootApplication
@Slf4j
public class SsmpApplication {

    public static void main(String[] args) {
        // 将MyConfig类注册到BeanFactory中,由BeanFactory管理Bean的生成,销毁
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        final BeanDefinition configDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyConfig.class)
            .setScope("singleton")
            .getBeanDefinition();
        beanFactory.registerBeanDefinition("config", configDefinition);

        // 向BeanFactory上注册一些后处理器,这些后处理器可以解析@Configuration和@Bean这些注解
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory后处理器,执行后处理操作,处理@Configuration注解,将bean1和bean2生成出来
        final Collection<BeanFactoryPostProcessor> beanFactoryPostProcessors = beanFactory.getBeansOfType(
            BeanFactoryPostProcessor.class).values();
        log.info("---------------BeanFactory后处理器------------------");
        beanFactoryPostProcessors.forEach(beanFactoryPostProcessor -> log.info(beanFactoryPostProcessor.toString()));
        beanFactoryPostProcessors.forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });
        log.info("---------------BeanFactory后处理器------------------");

        // Bean后处理器,针对Bean的生命周期的各个阶段做扩展,例如@Autowired @Resource,
        // 这样才能在生成bean1后,向bean1中注入bean2
        final Collection<BeanPostProcessor> beanPostProcessors = beanFactory.getBeansOfType(BeanPostProcessor.class)
            .values();
        log.info("----------------Bean后处理器-----------------");
        beanPostProcessors.forEach(beanPostProcessor -> log.info(beanPostProcessor.toString()));
        beanPostProcessors.forEach(beanFactory::addBeanPostProcessor);
        log.info("----------------Bean后处理器-----------------");

        log.info("------------------注册的所有Bean定义信息------------------");
        final String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            log.info(beanDefinitionName);
        }
        log.info("------------------注册的所有Bean定义信息------------------");

        final Bean1 bean1 = beanFactory.getBean(Bean1.class);
        log.info(bean1.getBean2().toString());
    }

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

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

    static class Bean1 {

        @Autowired
        private Bean2 bean2;

        public Bean1() {
            log.info("Bean1 construct...");
        }

        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        public Bean2() {
            log.info("Bean2 construct");
        }
    }
}

以上代码的运行结果如下:

点击查看代码
10:56:43.379 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
10:56:43.395 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
10:56:43.398 [main] INFO com.leo.ssmp.SsmpApplication - ---------------BeanFactory后处理器------------------
10:56:43.398 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.annotation.ConfigurationClassPostProcessor@724af044
10:56:43.398 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.event.EventListenerMethodProcessor@4678c730
10:56:43.512 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:56:43.513 [main] INFO com.leo.ssmp.SsmpApplication - ---------------BeanFactory后处理器------------------
10:56:43.513 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:56:43.513 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
10:56:43.516 [main] INFO com.leo.ssmp.SsmpApplication - ----------------Bean后处理器-----------------
10:56:43.516 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6385cb26
10:56:43.516 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@38364841
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - ----------------Bean后处理器-----------------
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - ------------------注册的所有Bean定义信息------------------
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - config
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.annotation.internalConfigurationAnnotationProcessor
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.annotation.internalAutowiredAnnotationProcessor
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.annotation.internalCommonAnnotationProcessor
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.event.internalEventListenerProcessor
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - org.springframework.context.event.internalEventListenerFactory
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - bean1
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - bean2
10:56:43.517 [main] INFO com.leo.ssmp.SsmpApplication - ------------------注册的所有Bean定义信息------------------
10:56:43.517 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
10:56:43.518 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
10:56:43.529 [main] INFO com.leo.ssmp.SsmpApplication - Bean1 construct...
10:56:43.535 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
10:56:43.536 [main] INFO com.leo.ssmp.SsmpApplication - Bean2 construct
10:56:43.537 [main] INFO com.leo.ssmp.SsmpApplication - com.leo.ssmp.SsmpApplication$Bean2@55040f2f

Process finished with exit code 0

后处理器的执行顺序跟它们之间的排序策略有关,先执行的后处理器会生效。

ApplicationContext实现

ClassPathXmlApplicationContext

首先我们定义一个实体类

package com.leo.domain;

import lombok.Data;

@Data
public class Book {
    private Integer id;

    private String type;

    private String name;

    private String description;
}

resources目录下创建b01.xml文件作为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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="bean1" class="com.leo.domain.Book"/>
</beans>

通常我们会使用如下方式来启动和加载Spring容器

package com.leo;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

@SpringBootApplication
public class SpringApplication {
    public static void main(String[] args) {
        final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
            "b01.xml");
    }
}

其内部实现如果简单的拆开来,可以用如下步骤表示,BeanFactory是真正的容器,我们使用XmlBeanDefinitionReader将xml中定义的bean转化成BeanDefinition并注册到BeanFactory中。

package com.leo;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;

@SpringBootApplication
public class SpringApplication {
    public static void main(String[] args) {
        testClasspathXmlApplicationContext();
    }

    private static void testClasspathXmlApplicationContext() {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println("=======================>before");
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
        System.out.println("=======================>after");
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }
}

以上代码的运行结果如下:

=======================>before
14:14:13.483 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [b01.xml]
=======================>after
bean1

我们会发现这是容器里只有一个Bean,但是当我们用springboot自动配置来启动容器时,会发现即使我们不定义Bean,默认也会加载一些内置的bean。那么Spring是如何加载这些内置的Bean的呢。我们可以在xml文件中加入如下配置:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="bean1" class="com.leo.ssmp.domain.Book"/>

    <!--这里开启了各种注解,例如Autowired-->
    <context:annotation-config/>
</beans>

这样我们运行上面的代码时打印出来的结果如下:

=======================>before
14:14:13.483 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [b01.xml]
=======================>after
bean1
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

可以看到这时已经加载了很多处理注解和事件监听的Bean。

FileSystemXmlApplicationContext

基本原理与ClassPathXmlApplicationContext相似,只不过这里改为从文件系统直接读取xml配置。

AnnotationConfigApplicationContext

直接通过@Configuration注解的类来加载Bean,这种方法加载Bean时,会默认加载注解处理器这些内置的Bean。同时还会将config类也加载为Bean。

与Spring原理(1)——容器相似的内容:

Spring原理(1)——容器

容器接口 BeanFactory 是ApplicationContext的父接口,所有ApplicationContext的实现都组合了BeanFactory。 BeanFactory才是Spring的核心容器。 从BeanFactory提供的方法来看,主要是从容器中获取Bean。实际上控制反转,依

源码学习之Spring容器创建原理

1 前言 众所周知,Spring可以帮我们管理我们需要的bean。在我们需要用到这些bean的时候,可以很方便的获取到它,然后进行一系列的操作。比如,我们定义一个bean MyTestBean public class MyTestBean { private String testStr = "t

手把手带你开发starter,点对点带你讲解原理

在2012 年 10 月,一个叫 Mike Youngstrom 的人在 Spring Jira 中创建了一个功能请求,要求在 Spring Framework 中支持无容器 Web 应用程序体系结构,提出了在主容器引导 Spring 容器内配置 Web 容器服务;这件事情对 SpringBoot 的诞生应该说是起到了一定的推动作用。 所以SpringBoot 设计的目标就是简化繁琐配置,快速建

聊聊Spring IOC容器的注入方式

为什么要说这个? 对于Spring体系而言,我个人认为最重要的就是IOC容器,其次才是AOP、Context等模块;因为这些模块功能是或搭建或集成在IOC容器这个基础设施之上的。 直接基于Spring框架体系做开发时,可以通过常用的JavaConfig或XML方式将对象的生命周期及装配由容器原生的接

Docker化Spring Boot应用

本文翻译自国外论坛 medium,原文地址:https://medium.com/@bubu.tripathy/dockerizing-your-spring-boot-application-75bf2c6568d0 Docker 是一个强大的工具,允许开发人员将他们的应用程序打包在容器中可以在任

Spring源码:bean的生命周期(一)

Spring的Bean定义环节是Spring IoC容器中的核心流程之一。在这个过程中,Spring会扫描指定的包路径,找到符合条件的Bean,并将其转换为Bean定义。在这个过程中,Spring使用了ASM技术来解析类的注解信息,判断当前类是否符合要求。然后,Spring将符合条件的Bean定义加入到候选集合中,并对其进行唯一标识命名、默认值赋值、常见定义注解的解析等操作。最后,Spring使用合并的Bean定义来包装原始的Bean定义,以便在Bean实例化的过程中进行更好的管理和控制。

在线程中使用Spring的Bean的方法、不推荐把“线程”注入到Spring

一、不推荐把“线程”注入到spring 将线程注入到Spring容器中并不是一个常见的做法,而且通常也不推荐这样做,原因如下: 生命周期管理困难: Spring管理的Bean生命周期由Spring容器管理,而线程的生命周期由JVM管理。将线程注入到Spring容器中会导致线程的生命周期与Spring

使用 Spring 实现控制反转和依赖注入

使用 Spring 实现控制反转和依赖注入 概述 在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,以及如何在Spring框架中实现它们。 什么是控制反转? 控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。 与传

聊聊Mybatis集成Spring的原理

一般都是研究框架源码,我为什么要反过来研究集成原理呢? 在我自己看来,集成虽然比较简单,但要求的细节比较多,需要掌握根本性的东西才能做到集成。 Mybatis集成Spring用到了FactoryBean以及BeanDefinition注册的原理,从这两个维度来实现集成,而我们单独学习Spring时,

手写模拟Spring底层原理-Bean的创建与获取

相信大家对Spring都有一定的了解,本篇文章我们会针对Spring底层原理,在海量的Spring源代码中进行抽丝剥茧手动实现一个Spring简易版本,对Spring的常用功能进行手写模拟实现。