Spring学习笔记(一):Spring源码的整体脉络

上一篇把Spring学习的大纲大体列了下,几天过去了,不敢说自己忙(因为一说忙就是借口,时间都是挤出来的),今天趁双节休息时间,把我们Spring源码的整体脉络先补充下,也借此篇文章祝大家中秋国庆快乐。

一、从哪里开始入手学习

1、先知道的几个概念

        每当在讲到什么是Spring时,我们一般会想到Spring是IOC和AOP的容器。
那么问题又来了,什么是IOC呢?
IOC是控制反转的意思,它是一种理念,用来解决层与层或者类与类之间的耦合问题,DI才是它的实现。
又冒出了一个概念,DI又是个啥呀?
DI顾名思义,依赖注入。
下面介绍一个例子,来理解下IOC和DI的原理。
  在没有使用SpringIOC时,如果A类需要调用B类对象,一般我们会在A类里

ClassB classB = new ClassB()


  对应这样的代码引用,当我们需要将ClassB换成ClassC时,需要改动这行代码,如果这样的引用有数十上百个,那么一来每个地方都去改工作量大,更重要的是不能保证不出问题,我们要知道任何小的代码改动都有可能引入BUG。
  因此,通过SpringIOC的将控制权反转便能解决这个问题

@Autowried
IClassB classB;


一般将B类去实现一个接口,当我们需要将ClassB换成ClassC时,让C类也去实现这个接口,将C类设置为注入的类,这样的C类需要依赖其他类来注入,即依赖注入。

2、看源码入口

SpringIOC的加载是在Spring应用的上下文ApplicationContext开始的,所以我们在看源码前可以先新建一个入口类

public class testSpring {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring.xml");
    //ApplicationContext ioc = new AnnotationConfigApplicationContext(IoCJacaConfig.class);

	User user = (User)ioc.getBean("userProperty");//user
	System.out.println(user);
}

二、SpringIOC的加载过程分析

1、从类到Bean

Ioc容器承载的是一堆bean,从类注入到容器成为Bean不是一步到位的,中间涉及到很多SpringIOC的加载环节,对于使用Spring的用户来说可以分为以下3个步骤。

  • 配置类(xml、xml+@注解、配置类<@纯注解>)
  • 加载Spring应用上下文
    new ClassPathXmlApplicationContext(“xx.xml”);
    new AnnotationConfigApplicationContext(xx.class);
  • 获取bean:getBean();

用流程图来大体表示下SpringIoc的加载流程,如下:
在这里插入图片描述

2、BeanFactory

先看一段代码

public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}

这是AbstractApplication类实现了BeanFactory接口的一段代码,当然这可以不关心,这段代码主要想说明,我们调用getBean时是调用BeanFatory去获取的bean,因此这里引入一个Spring最核心、最重要的组件beanFactory,我们称之为bean工厂。beanFactory是Spring最核心的顶层接口,它只有一个唯一的职责生产bean
  加载Spring应用上下文时就是通过bean工厂来将类注入到容器中成为一个bean,上面我们提到bean工厂的职责是负责生产bean,那么它到底是怎么运作的呢?
  beanFatory使用的是简单工厂模式,根据每个类封装好的一个“东西”(这个东西接下来会讲到)生成相应的bean。
  讲到这里有个很重要的问题:BeanFactory和ApplicationContext的区别?

  • 他们都有生产bean的能力,但Spring应用上下文它实际上不生产bean,它最终也是调用bean工厂来生产bean。另外,上下文可以通过扫描包或配置类一次同事装载多个bean的功能(更好地服务类),而bean工厂只能一个一个来生产。
  • 他们都可以作为容器,但BeanFactory是简单的容器,只提供装载和获取bean的功能,ApplicationContext提供更强大的容器功能,例如国际化、AOP等;
  • 区别:BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
  • 选择:一般web应用都选择ApplicationContext,对资源要求较高的应用选择BeanFactory。

3、BeanDefinition

还记得上面在讲BeanFactory时提到的一个封装好的“东西”吗,这个东西就是BeanDefinition,我们称之为Bean定义,因为BeanFactory是只提供装载(生产)bean和获取bean的功能,它是一台没有感情的机器,对于xml、@注解或javaConfig的不同方式的类配置,Spring必须提前转化为一个统一的东西给BeanFactory他才能工作,所以就引出了BeanDefinition,它也是Spring顶层的核心接口,封装了生产bean的一切原料(懒加载、单例/多例等)。
  每个类对应一个BeanDefinition,所有的BeanDefinition放在一个BeanDefinitionMap里,BeanFactory循环这个Map去实例化为bean对象。
  类注册为BeanDefinition需要经过3个步骤:

  • BeanDefinitionReader:读取配置类配置的扫描包
  • BeanDefinitionScanner:在这些包内扫描所有的类
  • BeanDefinitionRegistry:将扫描到的类注册为bean定义

BeanFatory调用getBean去生产bean,getBean既有生产功能又有获取功能,如果存在就返回,如果不存在就生产。




到此,Spring应用的上下文加载已大概完成,现在我们把上面的SpringIoc的加载流程稍作补充
在这里插入图片描述

三、Bean的生命周期

bean的生成不是一步到位的,需要经历3个过程:

  • 实例化
  • 填充属性
  • 初始化
    1、初始化的方式有两种,反射和工厂方法。反射是由Spring控制的,工厂方法可以自己控制new的过程(相对比较灵活);
    2、实例化化后的bean只是一个空壳,需要经过属性填充,解析注解(如@Autowired @Value)
    3、初始化,如initMetho、初始化结束方法。
    在bean的生命周期过程中要提到2个很重要的知识点

3.1、循环依赖

循环依赖的出现是在填充属性阶段。

class BeanA {
	@Autowired
	private BeanB b;
}
class BeanB {
	@Autowired
	private BeanA a;
}

对于这样的两个类,在beanA属性填充时,由于依赖beanB,则需要先生成beanB,而在beanB的属性填充时发现依赖beanA,由此变出现了循环依赖。Spring解决循环依赖的方式是使用三级缓存(后面文章会详细讲到)。

3.2、Aware

在bean的初始化阶段会调用一堆的aware,例如ApplicationContextAware ,它的用法是当实现了这个接口,必须实现一个方法,通过这个方法可以获得Spring应用上下文。

@Component
public class AppUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        applicationContext = arg0;
    }
}

至此,bean的生命周期完成,最终将这些成熟的bean存放至一级缓存,Map<beanName,bean的实例>。

四、Spring扩展点

4.1、bean工厂的后置处理器

  1. BeanFactoryPostProcessor
    修改:从bean工厂中获取beanDefinition去修改。
  2. BeanFactoryRegistryPostProcessor
    注册:获取BeanDifinitionRegistry去添加bean定义。
  • 在Spring集成Mybatis时就用到了这些扩展接口,因为Mapper是接口,没法注册为bean,因此通过BeanFactoryRegistryPostProcessor可以新增beanDefinition注册为bean;
    bean工厂的后置处理器的扩展节点是属于ApplicationContext这个过程中的提供的,它也是ApplicationContext和BeanFatory的区别之一;
    事实上BeanDefinitionReader\BeanDefinitionScanner\BeanDefinitionRegistry严格来说都是通过bean工厂的后置处理器集成的;
    Spring生态除了IOC,其他都是通过扩展节点集成进来的。

4.2、bean的后置处理器

BeanPostProcessor是用来控制bean的生产过程,在整个bean的生命周期中共会调用9次。
猜想AOP是在哪个扩展点集成的?
事实上在实例化后就可以集成AOP,但是在倒数第二个(最后一个是销毁bean)才是真正的bean创建完成,为了实现彻底解耦,在这个扩展节点集成了AOP。

4.3、还有很多其他扩展点


小节: BeanFactoryPostProcessor和BeanPostProcessor都是ApplicationContext集成的(xxxApplicationContext继承了BeanFatory),没有这两个扩展点就没办法读取扫描配置类和解析@Autowried以及AOP等 到此为止,Spring源码的整体脉络基本差不多了,最后将流程图补充完整来总结下这个脉络。

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值