关于spring:Spring框架系列6-Spring-IOC实现原理详解之IOC体系结构设计

37次阅读

共计 9804 个字符,预计需要花费 25 分钟才能阅读完成。

在对 IoC 有了初步的认知后,咱们开始对 IOC 的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看 IOC 最顶层的结构设计。@pdai

  • Spring 框架系列(6) – Spring IOC 实现原理详解之 IOC 体系结构设计

    • 站在设计者的角度思考设计 IOC 容器
    • Spring IoC 的体系结构设计

      • BeanFactory 和 BeanRegistry:IOC 容器性能标准和 Bean 的注册

        • BeanFactory 定义了 IOC 容器基本功能标准?
        • BeanFactory 为何要定义这么多层次的接口?定义了哪些接口?
        • 如何将 Bean 注册到 BeanFactory 中?BeanRegistry
      • BeanDefinition:各种 Bean 对象及其互相的关系
      • ApplicationContext:IOC 接口设计和实现

        • ApplicationContext 接口的设计
        • ApplicationContext 接口的实现
    • 参考文章
    • 更多文章

站在设计者的角度思考设计 IOC 容器

如果让你来设计一个 IoC 容器,你会怎么设计?咱们初步的通过这个问题,来帮忙咱们更好的了解 IOC 的设计。

在设计时,首先须要思考的是 IOC 容器的性能(输出和输入), 承接后面的文章,咱们初步的画出 IOC 容器的整体性能。

在此基础上,咱们初步的去思考,如果作为一个 IOC 容器的设计者,主体上应该蕴含哪几个局部:

  • 加载 Bean 的配置(比方 xml 配置)

    • 比方不同类型资源的加载,解析成生成对立 Bean 的定义
  • 依据 Bean 的定义加载生成 Bean 的实例,并搁置在 Bean 容器中

    • 比方 Bean 的依赖注入,Bean 的嵌套,Bean 寄存(缓存)等
  • 除了根底 Bean 外,还有惯例针对企业级业务的特地 Bean

    • 比方国际化 Message,事件 Event 等生成非凡的类构造去撑持
  • 对容器中的 Bean 提供对立的治理和调用

    • 比方用工厂模式治理,提供办法依据名字 / 类的类型等从容器中获取 Bean

(pdai:这种思考的过程才是建设性的,常识体系的构建才是高效的)

Spring IoC 的体系结构设计

那么咱们来看下 Spring 设计者是如何设计 IoC 并解决这些问题的。

BeanFactory 和 BeanRegistry:IOC 容器性能标准和 Bean 的注册

Spring Bean 的创立是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者治理对象间的依赖关系提供了很多便当和根底服务,在 Spring 中有许多的 IOC 容器的实现供用户抉择和应用,这是 IOC 容器的根底;在顶层的结构设计次要围绕着 BeanFactory 和 xxxRegistry 进行:

  • BeanFactory:工厂模式定义了 IOC 容器的基本功能标准
  • BeanRegistry:向 IOC 容器手工注册 BeanDefinition 对象的办法

其互相关系如下:

咱们再通过几个问题来辅助了解。

BeanFactory 定义了 IOC 容器基本功能标准?

BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能标准,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。咱们看下 BeanFactory 接口:

public interface BeanFactory {    
      
    // 用于勾销援用实例并将其与 FactoryBean 创立的 bean 辨别开来。例如,如果命名的 bean 是 FactoryBean,则获取将返回 Factory,而不是 Factory 返回的实例。String FACTORY_BEAN_PREFIX = "&"; 
        
    // 依据 bean 的名字和 Class 类型等来失去 bean 实例    
    Object getBean(String name) throws BeansException;    
    Object getBean(String name, Class requiredType) throws BeansException;    
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    // 返回指定 bean 的 Provider
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    // 查看工厂中是否蕴含给定 name 的 bean,或者内部注册的 bean
    boolean containsBean(String name);

    // 查看所给定 name 的 bean 是否为单例 / 原型
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 判断所给 name 的类型与 type 是否匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    // 获取给定 name 的 bean 的类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    // 返回给定 name 的 bean 的别名
    String[] getAliases(String name);
     
}

BeanFactory 为何要定义这么多层次的接口?定义了哪些接口?

次要是为了 辨别在 Spring 外部在操作过程中对象的传递和转化过程中,对对象的数据拜访所做的限度

有哪些接口呢?

  • ListableBeanFactory:该接口定义了拜访容器中 Bean 根本信息的若干办法,如查看 Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包含某一 Bean 等办法;
  • HierarchicalBeanFactory:父子级联 IoC 容器的接口,子容器能够通过接口办法拜访父容器;通过 HierarchicalBeanFactory 接口,Spring 的 IoC 容器能够建设父子层级关联的容器体系,子容器能够拜访父容器中的 Bean,但父容器不能拜访子容器的 Bean。Spring 应用父子容器实现了很多性能,比方在 Spring MVC 中,展示层 Bean 位于一个子容器中,而业务层和长久层的 Bean 位于父容器中。这样,展示层 Bean 就能够援用业务层和长久层的 Bean,而业务层和长久层的 Bean 则看不到展示层的 Bean。
  • ConfigurableBeanFactory:是一个重要的接口,加强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等办法;
  • ConfigurableListableBeanFactory: ListableBeanFactory 和 ConfigurableBeanFactory 的交融;
  • AutowireCapableBeanFactory:定义了将容器中的 Bean 按某种规定(如按名字匹配、按类型匹配等)进行主动拆卸的办法;

如何将 Bean 注册到 BeanFactory 中?BeanRegistry

Spring 配置文件中每一个 <bean> 节点元素在 Spring 容器里都通过一个 BeanDefinition 对象示意,它形容了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的办法。

BeanDefinition:各种 Bean 对象及其互相的关系

Bean 对象存在依赖嵌套等关系,所以设计者设计了 BeanDefinition,它用来对 Bean 对象及关系定义;咱们在了解时只须要抓住如下三个要点:

  • BeanDefinition 定义了各种 Bean 对象及其互相的关系
  • BeanDefinitionReader 这是 BeanDefinition 的解析器
  • BeanDefinitionHolder 这是 BeanDefination 的包装类,用来存储 BeanDefinition,name 以及 aliases 等。
  • BeanDefinition

SpringIOC 容器治理了咱们定义的各种 Bean 对象及其互相的关系,Bean 对象在 Spring 实现中是以 BeanDefinition 来形容的,其继承体系如下

  • BeanDefinitionReader

Bean 的解析过程非常复杂,性能被分的很细,因为这里须要被扩大的中央很多,必须保障有足够的灵活性,以应答可能的变动。Bean 的解析次要就是对 Spring 配置文件的解析。这个解析过程次要通过下图中的类实现:

  • BeanDefinitionHolder

BeanDefinitionHolder 这是 BeanDefination 的包装类,用来存储 BeanDefinition,name 以及 aliases 等

ApplicationContext:IOC 接口设计和实现

IoC 容器的接口类是 ApplicationContext,很显然它必然继承 BeanFactory 对 Bean 标准(最根本的 ioc 容器的实现)进行定义。而 ApplicationContext 示意的是利用的上下文,除了对 Bean 的治理外,还至多应该蕴含了

  • 拜访资源:对不同形式的 Bean 配置(即资源)进行加载。(实现 ResourcePatternResolver 接口)
  • 国际化: 反对信息源,能够实现国际化。(实现 MessageSource 接口)
  • 利用事件: 反对利用事件。(实现 ApplicationEventPublisher 接口)

ApplicationContext 接口的设计

咱们来看下 ApplicationContext 整体构造

  • HierarchicalBeanFactory 和 ListableBeanFactory:ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其余的接口扩大了 BeanFactory 的性能:
  • ApplicationEventPublisher:让容器领有公布利用上下文事件的性能,包含容器启动事件、敞开事件等。实现了 ApplicationListener 事件监听接口的 Bean 能够接管到容器事件,并对事件进行响应解决。在 ApplicationContext 形象实现类 AbstractApplicationContext 中,咱们能够发现存在一个 ApplicationEventMulticaster,它负责保留所有监听器,以便在容器产生上下文事件时告诉这些事件监听者。
  • MessageSource:为利用提供 i18n 国际化音讯拜访的性能;
  • ResourcePatternResolver:所 有 ApplicationContext 实现类都实现了相似于 PathMatchingResourcePatternResolver 的性能,能够通过带前缀的 Ant 格调的资源文件门路装载 Spring 的配置文件。
  • LifeCycle:该接口是 Spring 2.0 退出的,该接口提供了 start()和 stop()两个办法,次要用于管制异步处理过程。在具体应用时,该接口同时被 ApplicationContext 实现及具体 Bean 实现,ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean,以达到治理和管制 JMX、任务调度等目标。

ApplicationContext 接口的实现

在思考 ApplicationContext 接口的实现时,要害的点在于,不同 Bean 的配置形式(比方 xml,groovy,annotation 等)有着不同的资源加载形式,这便衍生除了泛滥 ApplicationContext 的实现类。

第一,从类结构设计上看,围绕着是否须要 Refresh 容器衍生出两个抽象类

  • GenericApplicationContext:是初始化的时候就创立容器,往后的每次 refresh 都不会更改
  • AbstractRefreshableApplicationContext:AbstractRefreshableApplicationContext 及子类的每次 refresh 都是先革除已有 (如果不存在就创立) 的容器,而后再从新创立;AbstractRefreshableApplicationContext 及子类无奈做到 GenericApplicationContext混合搭配从不同源头获取 bean 的定义信息

第二,从加载的源来看(比方 xml,groovy,annotation 等),衍生出泛滥类型的 ApplicationContext, 典型比方:

  • FileSystemXmlApplicationContext:从文件系统下的一个或多个 xml 配置文件中加载上下文定义,也就是说零碎盘符中加载 xml 配置文件。
  • ClassPathXmlApplicationContext:从类门路下的一个或多个 xml 配置文件中加载上下文定义,实用于 xml 配置的形式。
  • AnnotationConfigApplicationContext:从一个或多个基于 java 的配置类中加载上下文定义,实用于 java 注解的形式。
  • ConfigurableApplicationContext:扩大于 ApplicationContext,它新减少了两个次要的办法:refresh()和 close(),让 ApplicationContext 具备启动、刷新和敞开利用上下文的能力。在利用上下文敞开的状况下调用 refresh()即可启动利用上下文,在曾经启动的状态下,调用 refresh()则革除缓存并从新装载配置信息,而调用 close()则可敞开利用上下文。这些接口办法为容器的管制治理带来了便当,但作为开发者,咱们并不需要过多关怀这些办法。

第三,更进一步了解

设计者在设计时 AnnotationConfigApplicationContext 为什么是继承 GenericApplicationContext?因为基于注解的配置,是不太会被运行时批改的,这意味着不须要进行动静 Bean 配置和刷新容器,所以只须要 GenericApplicationContext。

而基于 XML 这种配置文件,这种文件是容易批改的,须要动态性刷新 Bean 的反对,所以 XML 相干的配置必然继承 AbstractRefreshableApplicationContext;且存在多种 xml 的加载形式(地位不同的设计),所以必然会设计出 AbstractXmlApplicationContext, 其中蕴含对 XML 配置解析成 BeanDefination 的过程。

那么仔细的你从上图能够发现 AnnotationWebConfigApplicationContext 却是继承了 AbstractRefreshableApplicationContext 而不是 GenericApplicationContext,为什么 AnnotationWebConfigApplicationContext 继承自 AbstractRefreshableApplicationContext 呢?因为用户能够通过 ApplicationContextInitializer 来设置 contextInitializerClasses(context-param / init-param),在这种状况下用户偏向于刷新 Bean 的,所以设计者抉择让 AnnotationWebConfigApplicationContext 继承了 AbstractRefreshableApplicationContext。(如下是源码中 Spring 设计者对它的解释)

 * <p>As an alternative to setting the "contextConfigLocation" parameter, users may
 * implement an {@link org.springframework.context.ApplicationContextInitializer
 * ApplicationContextInitializer} and set the
 * {@linkplain ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM "contextInitializerClasses"}
 * context-param / init-param. In such cases, users should favor the {@link #refresh()}
 * and {@link #scan(String...)} methods over the {@link #setConfigLocation(String)}
 * method, which is primarily for use by {@code ContextLoader}.

咱们把之前的设计要点和设计构造联合起来看:

到此,根本能够 <mark> 帮忙你从顶层构建对 IoC 容器的设计了解,而不是过早沉溺于代码的细节 </mark>; 所以《Java 全栈常识体系》最大的指标是帮忙你构筑体系化的认知,如果你本人去看源码而不站在顶层设计角度登程,你多半会捡了芝麻丢了西瓜,工夫一长啥印象没有。@pdai

参考文章

https://www.cnblogs.com/ITtan…

更多文章

首先,从 Spring 框架的整体架构和组成对整体框架有个认知。

  • Spring 根底 – Spring 和 Spring 框架组成

    • Spring 是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑 Spring 和 Spring Framework 的整体认知。

其次,通过案例引出 Spring 的外围(IoC 和 AOP),同时对 IoC 和 AOP 进行案例应用剖析。

  • Spring 根底 – Spring 简略例子引入 Spring 的外围

    • 上文中咱们简略介绍了 Spring 和 Spring Framework 的组件,那么这些 Spring Framework 组件是如何配合工作的呢?本文次要承接上文,向你展现 Spring Framework 组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出 Spring 的外围要点,比方 IOC 和 AOP 等;在此基础上还引入了不同的配置形式,如 XML,Java 配置和注解形式的差别。
  • Spring 根底 – Spring 外围之管制反转(IOC)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 IoC 的根底含意,同时以此发散了一些 IoC 相干知识点; 本节将在此基础上进一步解读 IOC 的含意以及 IOC 的应用形式
  • Spring 根底 – Spring 外围之面向切面编程(AOP)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点; 本节将在此基础上进一步解读 AOP 的含意以及 AOP 的应用形式。

基于 Spring 框架和 IOC,AOP 的根底,为构建下层 web 利用,须要进一步学习 SpringMVC。

  • Spring 根底 – SpringMVC 申请流程和案例

    • 前文咱们介绍了 Spring 框架和 Spring 框架中最为重要的两个技术点(IOC 和 AOP),那咱们如何更好的构建下层的利用呢(比方 web 利用),这便是 SpringMVC;Spring MVC 是 Spring 在 Spring Container Core 和 AOP 等技术根底上,遵循上述 Web MVC 的标准推出的 web 开发框架,目标是为了简化 Java 栈的 web 开发。本文次要介绍 SpringMVC 的申请流程和根底案例的编写和运行。

Spring 进阶 – IoC,AOP 以及 SpringMVC 的源码剖析

  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 体系结构设计

    • 在对 IoC 有了初步的认知后,咱们开始对 IOC 的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看 IOC 最顶层的结构设计
  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 初始化流程

    • 上文,咱们看了 IOC 设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的
  • Spring 进阶 – Spring IOC 实现原理详解之 Bean 实例化(生命周期, 循环依赖等)

    • 上文,咱们看了 IOC 设计要点和设计构造;以及 Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的;容器中寄存的是 Bean 的定义即 BeanDefinition 放到 beanDefinitionMap 中,实质上是一个ConcurrentHashMap<String, Object>;并且 BeanDefinition 接口中蕴含了这个类的 Class 信息以及是否是单例等。那么如何从 BeanDefinition 中实例化 Bean 对象呢,这是本文次要钻研的内容?
  • Spring 进阶 – Spring AOP 实现原理详解之切面实现

    • 前文,咱们剖析了 Spring IOC 的初始化过程和 Bean 的生命周期等,而 Spring AOP 也是基于 IOC 的 Bean 加载来实现的。本文次要介绍 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor,为后续交给代理加强实现做筹备的过程)。
  • Spring 进阶 – Spring AOP 实现原理详解之 AOP 代理

    • 上文咱们介绍了 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor)。本文在此基础上持续介绍,代理(cglib 代理和 JDK 代理)的实现过程。
  • Spring 进阶 – Spring AOP 实现原理详解之 Cglib 代理实现

    • 咱们在前文中曾经介绍了 SpringAOP 的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍 Cglib 动静代理的案例和 SpringAOP 实现的原理。
  • Spring 进阶 – Spring AOP 实现原理详解之 JDK 代理实现

    • 上文咱们学习了 SpringAOP Cglib 动静代理的实现,本文次要是 SpringAOP JDK 动静代理的案例和实现局部。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 初始化的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第一篇:DispatcherServlet 的初始化过程的源码解析。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 解决申请的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第二篇:DispatcherServlet 解决申请的过程的源码解析。

正文完
 0