【Spring基础系列2】很全的Sping IOC基础知识(二)

简介: 这两天一直在学习Spring,我把Spring分成两部分来学,分别为IOC和AOP,然后IOC我又分为纯理论知识和常用注解的使用姿势,这篇文章主要讲IOC的纯理论部分,所以看起来可能会有点枯燥,但是仔细体味一下,感觉里面还是有点意思。这些纯理论知识,基本都是来自于网络博客,我也不能再去看代码,然后重复造轮子,但是网上的示例,我肯定会自己亲自运行一遍才会贴出来。然后注解的使用,我也会自己使用完后,才会去讲解使用的方法。

Spring IOC原理总结


依赖注入流程

这个博主通过磨咖啡的故事,生动讲解了注入流程,将生产咖啡总结为两个阶段:

  1. 采摘和收集“咖啡豆”(bean)
  2. 研磨和烹饪咖啡


阶段一:收集和注册

第一阶段可以认为是构建和收集bean定义的阶段,在这个阶段中,我们可以通过XML或者Java代码的方式定义一些bean,然后通过手动组装或者让容器基于某些机制自动扫描的形式,将这些bean定义收集到IOC容器中。

假设我们以XML配置的形式来收集并注册单一bean,一般形式如下:

<bean id="mockService" class="..MockServiceImpl">
    ...
</bean>

如果嫌逐个收集bean定义麻烦,想批量地收集并注册到IOC容器中,我们也可以通过XML Schema形式的配置进行批量扫描并采集和注册:

<context:component-scan base-package="com.keevol">


阶段二:分析和组装

当第一阶段工作完成后,我们可以先暂且认为IOC容器中充斥着一个个独立的bean,它们之间没有任何关系。但实际上,它们之间是有依赖关系的,所以,IOC容器在第二阶段要干的事情就是分析这些已经在IOC容器之中的bean,然后根据它们之间的依赖关系先后组装它们。如果IOC容器发现某个bean依赖另一个bean,它就会将这另一个bean注入给依赖它的那个bean,直到所有到bean的依赖都注入完成,所有bean都“整装待发”,整个IOC容器都工作即算完成。

至于分析和组装的依据,Spring框架最早是通过XML配置文件的形式来描述bean与bean之间的关系,随着Java业界研发技术和理念都转变,基于Java代码和Annotation元信息的描述方式也日渐兴盛(比如@Autowired和@Inject),但不管使用哪种方式,都只是为了简化绑定逻辑描述的各种“表象”,最终都是为本阶段都最终目的服务。

这个讲解只是给大家对IOC有一个初步的认识,下面才是核心的部分。


IOC容器的原理

IOC容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系。

  • 原理就是通过Java的反射技术来实现的!通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
  • 再通过配置文件(xml)或者注解来描述类与类之间的关系
  • 我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了!

上面描述的技术只要学过点Java的都能说出来,我们简单来看看实际Spring IOC容器是怎么实现对象的创建和依赖的:

  1. 根据Bean配置信息在容器内部创建Bean定义注册表
  2. 根据注册表加载、实例化bean、建立Bean与Bean之间的依赖关系
  3. 将这些准备就绪的Bean放到Map缓存池中,等待应用程序调用

image.gif6ZM`E[R$Y{I~)I2PQ8L$LGR.png

Spring容器(Bean工厂)可简单分成两种:

  • BeanFactory:这是最基础、面向Spring的
  • ApplicationContext:这是在BeanFactory基础之上,面向使用Spring框架的开发者。提供了一系列的功能!

几乎所有的应用场合都是使用ApplicationContext!


BeanFactory vs ApplicationContext

BeanFactory

BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean。

BeanFactory:是IOC容器的核心接口, 它定义了IOC的基本功能,我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean。

BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。

BeanFactory有着庞大的继承、实现体系,有众多的子接口、实现类。来看一下BeanFactory的基本类体系结构(接口为主):

6D9HG]NQ(``G4[5GUON()QC.png

下面写了一大堆,我觉得仅供了解即可:

  • BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口。
  • 有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口。
  • ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry
  • ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口。  (这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类:)
  • AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
  • AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory
  • DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  • 最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。


再来看一下BeanFactory的源码:

public interface BeanFactory {
    /**
     * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
     */
    String FACTORY_BEAN_PREFIX = "&";
    /*
     * 四个不同形式的getBean方法,获取实例
     */
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    boolean containsBean(String name); // 是否存在
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型(多实例)
    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;// 名称、类型是否匹配
    Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型
    String[] getAliases(String name);// 根据实例的名字获取实例的别名
}


具体:

  • 4个获取实例的方法。getBean的重载方法。
  • 4个判断的方法。判断是否存在,是否为单例、原型,名称类型是否匹配。
  • 1个获取类型的方法、一个获取别名的方法。根据名称获取类型、根据名称获取别名。一目了然!

总结:这10个方法,很明显,这是一个典型的工厂模式的工厂接口。


看一个简单的示例:

@Data
public class Cat implements Animal {
    private String catName = "罗小黑";
}
@Data
public class Pets {
    @Resource
    private Cat cat;
    public static void main(String args[]) {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        org.springframework.core.io.Resource res = resolver.getResource("classpath:applicationContext.xml");
        BeanFactory factory = new XmlBeanFactory(res);
        Pets pets=factory.getBean("pets", Pets.class);
        System.out.println(pets.toString());
    }
}
// 输出:
// Pets(cat=Cat(catName=罗小黑))


这里需要到applicationContext.xml添加一下配置:

<bean id="pets" class="com.java.annotation.spring.bean.test4.Pets" >
    <property name="cat" ref="cat" />
</bean>
<bean id="cat" class="com.java.annotation.spring.bean.test4.Cat" />


解读一下:

  • XmlBeanFactory通过Resource装载Spring配置信息冰启动IoC容器,然后就可以通过factory.getBean从IoC容器中获取Bean了。
  • 通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。
  • 对于单实例(singleton)的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean时直接从IoC容器缓存中获取Bean。

这种方式非常不建议使用,因为我用这种方式,发现添加的@Service注解不能生效.


ApplicationContext

如果说BeanFactory是Spring的心脏,那么ApplicationContext就是完整的躯体了,ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。

BeanFactorty接口提供了配置框架及基本功能,但是无法支持spring的aop功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactorty,ApplicationContext还提供了以下的功能:

  1. MessageSource, 提供国际化的消息访问
  2. 资源访问,如URL和文件
  3. 事件传播特性,即支持aop特性
  4. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

ApplicationContext:是IOC容器另一个重要接口, 它继承了BeanFactory的基本功能, 同时也继承了容器的高级功能,如:MessageSource(国际化资源接口)、ResourceLoader(资源加载接口)、ApplicationEventPublisher(应用事件发布接口)等。

image.gif4D5(F]_O(]Z)FI1O`MY[SAP.jpg

ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能:

下面都是八股文,也是仅作了解即可:

  • ClassPathXmlApplicationContext:默认从类路径加载配置文件
  • FileSystemXmlApplicationContext:默认从文件系统中装载配置文件 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、任务调度等目的。
  • ConfigurableApplicationContext 扩展于 ApplicationContext,它新增加了两个主要的方法:refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用close()则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利,但作为开发者,我们并不需要过多关心这些方法。


还是看一下ClassPathXmlApplicationContext的使用姿势:

ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//ApplicationContext context =new ClassPathXmlApplicationContext("file:/Users/mengloulv/java-workspace/Demo5/src/main/resources/applicationContext.xml");
Pets pets=context.getBean("pets", Pets.class);
System.out.println(pets.toString());


再看一下FileSystemXmlApplicationContext的使用姿势:

ApplicationContext context =new FileSystemXmlApplicationContext("file:/Users/mengloulv/java-workspace/Demo5/src/main/resources/applicationContext.xml");
Pets pets=context.getBean("pets", Pets.class);
System.out.println(pets.toString());

个人还是偏向使用ClassPathXmlApplicationContext,可以使用相对路径。

后面还有个WebApplicationContext,反正我是用的少,就不介绍了。


BeanFactory和ApplicationContext的区别

1.BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

BeanFacotry延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 beanFactory 的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean , Applicationcontext 直接配置在配置文件即可而 beanFactory 这要在代码中显示的写出来才可以被容器识别。)

3.beanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 beanFactory 。

相关文章
|
3天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
93 69
|
2天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
33 21
|
8天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
7天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
5月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
28天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
46 2
|
2月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
4月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
266 18
|
4月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
2月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
45 0