spring整体架构分析

一,前言

我使用spring的时间并不长,也没有深入阅读过源码,但因为最近了解了以下spring的源码,想用自己的理解来描述spring,所以可能会有不恰当的地方,若有人读到及发现错误,希望提醒!!

我们知道,spring最重要的两个概念是 Ioc 和 Aop,这贯穿着整个spring的使用。
spring框架有一个很重要的角色,那就是bean。

一切东西都是由bean引申出来

  1. bean来源哪里呢?
  2. 如果创建bean呢?
  3. 用什么结构来存储bean呢?
  4. 用什么来管理所有的bean呢?

我们先说 bean 来源

使用过 spring 的人多多少少写过这样的代码

<bean id=? class=? scope init-method abstract>
    <propety name=? value=?/>
	<propety name=? ref=?/>
</bean>

<bean id=? class=? scope init-method abstract>
    <constructor-arg name=? value=?/>
	<constructor-arg name=? ref=?/>
</bean>

也就是你在xml文件定义的那些对象,这是spring的来源之一
你或许也有使用过这几个注解

  • @Bean
  • @Controller
  • @Component

这也是bean的一个来源

甚至我们可以在代码种手动装入bean,不过呢,可以,但没必要。(springboot)

/**
@Configuration
或
@Component
或
@Bean定义
**/
@Configuration
public class DefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) 
throws BeansException {
    
    

    }

    /**
     * 先执行postProcessBeanDefinitionRegistry方法
     * 在执行postProcessBeanFactory方法
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 
        throws BeansException {
    
    
        // 第一种 : 手动注入
        // 注册bean
        registerBean(registry, "hello", HelloWord.class);
        registerBean(registry, "helloWord", HelloWord.class);
    }
/**
注册bean
**/
 private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
    
    
        RootBeanDefinition bean = new RootBeanDefinition(beanClass);
        registry.registerBeanDefinition(name, bean);
    }
}

当然,我们也可以自己设置bean的来源,不过呢,需要实现相关的接口,重写里面的方法。(beanDefinitionReader)

BeanDefinitionReader 的作用是读取 Spring 配置文件中的内容,将其转换为 IoC 容器内部的数据结构:BeanDefinition。

https://zhuanlan.zhihu.com/p/107839916

那么我们知道bean有哪些来源之后,问题又来了,spring是如何帮我们创建这个对象的呢

我们回顾以下,创建对象有几种方式?

  1. new
  2. 通过工厂
  3. 通过反射

很显然,spring是通过反射的方式来为我们创建对象的,大致的思路就是读取bean来源信息,然后通过反射来创建对象。创建对象的核心代码其实就几条

//三种或Class对象的的方式
Class clazz = Class.forName("完全限定名");
Class clazz = 对象.getClass();
class clazz =.class;

//clazz.newInstance(); 官方不推荐我们使用这种方式创建对象
Constructor con = clazz.getDeclareConstructor();
Object object = con.newInstance();

我们知道了如果创建bean,的确是通过反射,但是spring肯定不止这么简单就创建出来对象的,中间还有很多过程,而且我们创建的bean是用什么数据结构来保存的呢?

其实最适合的就是 Map 结构了。在spring中,并没有直接的把Class对象存储起来,而是通过beandefinition对象,保存着我们在配置文件…中定义的那些bean的信息。那么为什么spring不能用Class来建立bean呢?很简单,因为Class无法完成bean的抽象,比如bean的作用域,bean的注入模型,bean是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个BeanDefinition类来抽象这些信息,以便于spring能够完美的实例化一个bean。

在这里插入图片描述

在spring源码中,有着多个数据结构来缓存着与bean有关的数据。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
private final Set<String> registeredSingletons = new LinkedHashSet(256);
  1. singletonObjects(ConcurrentHashMap)

保存BeanName和创建的bean实例之间的关系,bean name->bean instance。

  1. singletonFactories(HashMap)

保存BeanName和创建bean的工厂之间的关系,bean name -> ObjectsFatory

  1. earlySingletonObjects(HashMap)

也是保存BeanName和创建bean实例之间的关系,与singletonObjects之间的区别在于,当一个单例bean被放到earlySingletonObjects里面之后,该bean就可以通过getBean()方法获取到了(虽然只是早期对象,即还在创建过程中。目的是解决循环依赖的问题)

  1. registeredSingletons(LinkedHashSet)

保存当前所有已注册的bean

由什么来管理众多的bean呢?

我们知道,在spring中,有许多容器。我们知道,容器是一个空间的概念,一般理解为可盛放物体的地方。在Spring容器通常理解为BeanFactory或者ApplicationContext。我们知道spring的IOC容器能够帮我们创建对象,对象交给spring管理之后我们就不用手动去new对象。

其中有BeanFactory与ApplicationContext两种方式可以创建对象。

  • BeanFactory

BeanFactory采用了工厂设计模式,负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的生命周期。BeanFactory在解析配置文件时并不会初始化对象,只有在使用对象getBean()才会对该对象进行初始化

  • ApplicationContext(接口)

ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:国际化支持、aop、事务等。而ApplicationContext在解析配置文件时对配置文件中的所有对象都初始化了,getBean()方法只是获取对象的过程,这样可以确保应用不需要等待他们被创建。

理解Spring容器、BeanFactory和ApplicationContext

最后,Spring如何管理bean

注意:这里我们是以应用上下文管理bean为例的。

<bean id="airplane" class="spring.Airplane"/>为例,spring在启动的时候,会创建应用上下文容器,而所有的bean都是在创建应用上下文容器的时候进行加载的,大致流程就是,应用上下文对象会根据我们传入的配置文件路径去加载这个配置文件,然后解析配置文件的< beans>标签下的< bean>标签,然后会对每个bean标签进行解析,这时会根据我们在bean标签中配置的属性(这里我们只定义了id和class)给每一个bean实例化一个BeanDefinition,同时会把这些BeanDefinition对象放入到应用上下文中的一个List< BeanDefinition>集合中,接着就是对List< BeanDefinition>进行循环并且通过class的值通过反射,实例化bean,最后将实例化的bean维护到一个map中,map的key就是bean的id,map的value就是bean的实例化对象,最后我们就可以通过id来获取我们想要的bean了,但是这里只是简单的介绍了bean的加载,应用上下文所做的事情远不止这些,还有对懒加载bean的维护,对bean之间依赖关系的维护(就是我们常说的依赖关系,其实也是通过一个Map<String, Set< String>>类型ConcurrentHashMap来维护的)等等。

二,通过图来描述上述过程

通过上面的解释,我们大致可以描绘出spring的一部分核心了。

在这里插入图片描述
有没有觉得在BeanDefintion到BeanFactory过程,有很多空白,觉得不“美观”?

是的,在这条路上还有其他类其中作用,它们有一个共同的接口BeanFactoryPostProcessor,增强器,可修改bean定义的信息。

或许你在使用spring的时候写过类似这样的代码

 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">   
 <property name="driverClassName">
      <value>${jdbc.driverClassName}</value>
 </property>   
 <property name="url">
     <value>${jdbc.url}</value>
 </property>   
  <property name="username">
     <value>${jdbc.username}</value>
 </property>   
 <property name="password">
     <value>${jdbc.password}</value>
 </property>   
</bean> 

你有没有好奇这个变量是在什么时候替换的?
其实就是在刚才提到的那条上及逆行替换的,把数据替换成properties中的数据。现在你明白BeanFactoryPostProcessor的作用了吧,通过它可以修改BeanDefintion中信息。你可以查看PlaceholderConfigurerSupport,这个类实现了上述接口,它的作用就是解析${}替换成我们设置的数据。

public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware {
    
    
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
    public static final String DEFAULT_VALUE_SEPARATOR = ":";
    protected String placeholderPrefix = "${";
    protected String placeholderSuffix = "}";
 	// ...
 }

所以,补充上图:(当然了,不止一个BeanFactoryPostProcessor

在这里插入图片描述

或许你会疑问,为什么创建一个对象这么复杂,要先实例化,再初始化,最后才能变成一个完整的bean对象呢?

我们先说,实例化和初始化过程中,spring做了什么

  • 实例化 : 在堆中开辟一块空间,属性都是默认值(因为我们使用Ioc容器,不用自己手动new,所以引用类型多数是null)
  • 初始化 - 给属性完成赋值操作
  1. 填充属性,赋值
  2. 调用具体的初始化方法(例如,在xml文件中,<bean></bean>标签中的init-method)

spring是一个框架,而且有着极为强大的生态,这也说明这它的扩展性很强。所以它在实例化bean 和 初始化bean 过程中,提供了一些接口,以至于我们能在这过程中做点什么。其实这就与Aop有点关系了。

spring 为我们提供了一个接口BeanPostProcessor。可以让我们在初始化bean前后执行一些操作。我们称它为后置处理器

public interface BeanPostProcessor {
    
    

	//实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        return bean;
    }

	//实例化、依赖注入、初始化完毕时执行
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        return bean;
    }
}
  • 后置处理器的postProcessorBeforeInitailization方法是在bean实例化,依赖注入之后及自定义初始化方法(例如:配置文件中bean标签添加init-method属性指定Java类中初始化方法、@PostConstruct注解指定初始化方法,Java类实现InitailztingBean接口)之前调用
  • 后置处理器的postProcessorAfterInitailization方法是在bean实例化、依赖注入及自定义初始化方法之后调用

你可以理解为 BeanPostProcessor是连接Ioc 与 Aop 的桥梁。当然了,BeanPostProcessor 也可以有多个。

我们继续完善上图:

在这里插入图片描述
spring肯定不止这点东西,他还给我们扩展了很多。例如,监听器
监听器的作用:监听器是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或者属性改变,当被监听对象发生上述事件后,监听器某个方法将立即执行。
可以通过监听器来监听bean的生命周期等等…

有关bean的生命周期可以查看下面几个博客:

  1. https://blog.csdn.net/qq_35634181/article/details/104473308
  2. https://blog.csdn.net/lisongjia123/article/details/52091013?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-6.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-6.control
  3. https://www.cnblogs.com/zrtqsk/p/3735273.html

继续完善上图

在这里插入图片描述

当然,这依然不够完整,还有一些类,比如Environment

Environment:为了方便使用,在容器创建的时候提前将系统相关属性加载到StandarEnvironment对象中,方便后续使用

可以查看以下深入了解

最后一步了,完善我们的最终图片

在这里插入图片描述

补充:

在上面提及spring容器的时候,说过BeanFactory和ApplicationContext,显示在上面的图没有ApplicationContext出现,我的理解是,ApplicationContext只不过是在BeanFactory外再套了一层,并且提供了更加丰富的功能,其实它是一直在通过BeanFactory来操作bean的。(这只是我的理解)

总结:想要成为一个框架,首先要考虑的一定是扩展性
Spring 提供了什么扩展性?

  1. 在对象创建之间添加某些功能
  2. 在容器初始化之前添加某些功能
  3. 在不同的阶段发出不同的事件,完成某些功能
  4. 抽象出一堆的接口来帮助扩展
  5. 面向接口编程

三,不太深入的了解

当你尝试阅读源码时,很多人都告诉你,要从AbstractApplicationContext类的refresh方法看起,只要了解了其中的方法,边了解了spring的大部分。先初略的了解里面每个方法的大致功能

    public void refresh() throws BeansException, IllegalStateException {
    
    
        synchronized(this.startupShutdownMonitor) {
    
    
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
    
    
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
    
    
                if (this.logger.isWarnEnabled()) {
    
    
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }

                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
    
    
                this.resetCommonCaches();
                contextRefresh.end();
            }

        }
    }
  • this.prepareRefresh();

    1. 设置spring容器的启动时间
    2. 撤销关闭状态
    3. 开启活跃状态
    4. 初始化属性元信息initPropertySource()
    5. 验证环境中必须存在的属性
  • ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

    • 获取BeanFactory实例
  • this.prepareBeanFactory(beanFactory);

    • 对BeanFactory进行相关的设置,为后续的使用做准备
    1. 设置ClassLoader用来加载Bean
    2. 设置表达式解析器等等
  • this.postProcessBeanFactory(beanFactory);

    • 模板方法,让不同的spring容器自定义扩展自己的加载方法,方法体为空
  • this.invokeBeanFactoryPostProcessors(beanFactory);

    • (会跳转到ConfigurationClassParser.doProcessConfigurationClass解析各种标签比如@Bean等)调用工厂后处理器处理解析各类Bean标签(@Configuration/@Import/@Bean/@SpringbootApplication),扫描Bean文件,并解析成一个个的Bean,这里的Bean只是被加载到spring容器当中,由于spring容器的懒加载,这些Bean仅仅只是加载到容器,并没有连接和初始化,当程序需要使用到该Bean的时候,才会将bean连接和初始化。
  • this.registerBeanPostProcessors(beanFactory);

    • 在容器中找出实现BeanPostProcessors接口的bean,设置到BeanFactory的属性之中,最后bean被实例化的时候会调用BeanPostProcessors(bean的后置处理器)
  • this.initMessageSource();

    • 加载国际化信息
  • this.initApplicationEventMulticaster();

    • 初始化事件的广播器,用于事件的发布
  • this.onRefresh();

    • 模板方法:让不同的spring容器自定义扩展自己的加载方法,方法体为空
  • this.registerListeners();

    • 注册监听器
  • this.finishBeanFactoryInitialization(beanFactory);

    • 实例化BeanFactory中已经被注册但是没有实例化的所有bean(懒加载不需要被实例化)
  • this.finishRefresh();

    • 初始化生命周期处理器等相关的事情

四,再聊聊spring的整体架构和核心技术

  • 以下是我找到的一篇不错的博客

https://blog.csdn.net/u010209217/article/details/80617310

五,最后,有关spring面试常问的知识

作者还没深入了解spring,所以以下问题并不全了解,再以后会继续学习源码,找出以下问题的答案:

  1. 什么是Spring框架,Spring框架包含哪些模块
  2. Spring框架的优势
  3. Ioc 和 DI 是什么?
  4. 描述以下Spring Ioc 容器的初始化过程
  5. BeanFactory 和 FactoryBean的区别?(这个问题我已经在之前的博客回答过了)
  6. BeanFactory 和 ApplicationContext的异同
  7. Spring Bean 的生命周期?
  8. Spring Aop的实现原理
  9. Spring 是如何管理事务的
  10. Spring的不同事务传播行为有哪些,有什么作用?(这个也有过记录)
  11. Spring 中用到了哪些设计模式?
  12. Spring 如何解决循环依赖?
  13. Bean的作用域
  14. Spring框架中有哪些不同类似的事件?
  15. Spring通知有哪些类型?
  16. Spring的自动装配

提一嘴:Spring 和 后面要学的Spring boot ,Spring Cloud 是息息相关的,Spring是核心是基础。

猜你喜欢

转载自blog.csdn.net/saienenen/article/details/112861280