Spring源码分析

简介: Spring源码分析


第一章:Spring引言

一:当前Spring的一个地位

Spring准确的来讲是一套解决方案,是一个技术栈,是一个全家桶

从起初的Spring,到因为要进行web开发引入了SpringMVC,到为了后来的快捷开发,引入了Springboot,在到后来的微服务,也就是SpringCloud。

Spring能有以上这么优秀的离不开Spring核心当中的两大特性,IOC和AOP,Spring能完成这么复杂的生态的构建离不开这两大伟大的特性。

SpringMVC本质上就是由Spring+MVC这两大部分组成,任何一个MVC框架都离不开MVC的内容和影子。

二:SpringBoot的真正的价值

SpringBoot不要把他妖魔化,SpringBoot真正的价值是:简化了Spring和SpringMVC的开发。
他本质上没有什么新的东西,他本质上就是对Spring和SpringMVC的简化:

1:方便导入Jar包

我们现在都是基于maven导入依赖的,maven我们都是自己引入依赖,但是boot觉得这样太麻烦了,他引入了starter,方便引入导入jar包

2:简化冗余配置

Spring从原始的xml到后续的注解,已经简化了,但是还是太麻烦,Spring

boot再次基础上再次进行了封装,简化冗余配置。

3:内嵌了Tomcat

内嵌Tomcat对我们来讲是一个非常重要的特性,两大好处

1:内嵌了Tomcat好处1

好处的背景:

解决了SpringMVC父子容器的问题。这是原有springMVC开发需要面对和解决的问题

在SpringMVC开发中,是如何解决父子容器的呢?

首先知道,SpringMVC开发过程中,是有两个Spring工厂的。回忆一下SpringMVC的配置:

DispatcherServlet:SpringMVC当中的前端控制器

init-param:我们还需要执行Spring的配置文件,指定spring的配置文件

DispatcherServlet本质上就是一个Spring的工厂,他是servlet但是内部封装了一个Spring的工厂

ContextLoaderListerner这个spring的一个监听器。这个listener本质上就是一个创建了spring工厂的listener,有这两个工厂并存,ContextLoaderListerner是父工厂,dispatcherServlet是子工厂

带来的问题:

Controller当中获取Service的时候,在Service当中配置了事务,但是事务不生效。

父工厂当中创建了Service,添加了事务,但是在子工厂当中,也同样创建了Service,但是没有事务,Controller是子工厂帮我们创建的,这个时候,就会获取子工厂的service,这样就是没有事务了。

问题的解决方案:

子工厂不要创建Service,去调用父工厂的Service。这是MVC开发过程中注明的父子容器的问题。

SpringBoot没有父子容器的问题,SpringBoot是内嵌Tomcat,他自己不需要解决了这个问题。不需要你程序员外部激活。

2:内嵌了Tomcat好处2

Web开发的启动进程,是通过Tomcat为载体启动进程的,换句话说一个Tomcat就是一个进程,但是SpringBoot内嵌了Tomcat之后,就可以通过运行jar包的方式去启动web服务,也就是启动一个服务进程,这样就可以多个Tomcat进程。轻松将一个大的web服务,轻松启动提供服务。对外发送请求,接收相应。也就提供了将一个web服务轻松进行拆分的条件,这也就解释了,一个SpringCloud必须嫁接于SpringBoot的原因。

除了这玩意,之后的好处跟SpringBoot毛关系都没有。

3:但是,如果没有SpringBoot,我们可以做微服务开发么?

当然是可以的。

没有SpringBoot,我们依然可以有类似于SpringCloud的微服务方案,我们完全可以使用Spring+SpringMVC去搭建微服务方案。

微服务是一个架构思想,他解决了分布式的很多问题,SpringCloud只是其中的一个解决方案

甚至我们可以脱离Java的体系,使用go来进行微服务编程,当前的go,Java都能干这事。

SpringBoot和SpringCloud都是停留在应用层,真正的核心体系,还是在Spring和SpringMVC当中。

4:Spring源码特点

1.枯燥,mybatis顶多算是spring的一个模块。
2.方方面面都要掌握,
3.课程源码学习的内容:
Spring容器技术:IOC
xml读取,存储,对象创建,加工,缓存,分析
AOP代理设计:Spring真正把代理设计融入到Spring体系当中。
事务:Spring是如何进行事务处理的,Spring与mybatis源码整合,现有持久化方案,在来讨论事务处理

5:Spring源码学习方案

在源码中,编写自己的测试代码,研究Spring源码。

1搭建不但见源码环境是不重要的,我们当前搭建环境只是为了加注释。

源码环境:从运行的角度来讲,我们在Spring源码当中写点自己的测试代码进行运行,会把自己的自己和Spring的代码都编译掉,这是要给很漫长而又蛋疼的过程。

什么叫基于源码运行?

我们从idea当中启动的时候,就是基于源码来跑,这样就会把所有的源码重新编译一遍。

为什么我们选择这个版本

我们的Spring学习是基于Spring5.1.x,Spring核心部分在Spring4.0之后,改动量很小很小,基本是一致的,Spring的5.2版本中引入了Groovy脚本和Kotinj脚本的支持,反而不利于我们源码的学习。

6:Spring源码环境搭建

1.获取Spring源码Github获取即可
2.Spring从5.x之后构建工具就由Maven转到了Gradle
Spring下载下来的源码中,有一个gradle的包,其中有一个gradle-wrapper.properties的配置文件,
其中的url指定了这个Spring的版本对应的gradle的版本。
我们开发的时候gradle在中国用的很少,没有必要单独去搭建gradle环境,我们可以让Spring源码自动帮我们搭建环境时下载Gradle 80mb。
gradle这个东西,引入aliyun的镜像。在源码根路径下有build.gradle配置文件,这个就相当于maven的配置文件。
3.idea当中导入Spring项目,当我们导入在之后,就会进入这个buildoutput窗口,idea会自动帮我们下载gradle环境。
4.当我们构建完成之后,我们还需要做最后一件事,我们去点击那个build小斧头,跟他构建成class文件。
repositories {
    maven { url "https://repo.spring.io/libs-spring-framework-build" }
    maven { url 'https://maven.aliyun.com/repository/public/' }
        maven { url 'https://maven.aliyun.com/repository/spring/'}
    mavenCentral()
  }

三:Spring容器

1:Spring容器概述

1):工厂or容器?

Spring工厂,也就是Spring的容器。作为Spring的工厂来讲,Spring工厂是为了创建对象,创建完对象之后,不仅仅把这个对象创建出来,同时还会把这个对象存储在工厂当中宫其他调用者进行调用。创建和存储是他的作用,能存储就像是一个容器。当前这得有得有一个前提,对象必须得是单实例对象,Spring的scope必须得是sigleton才会进行存储,否则之给创建,每次使用每次建。

2):Spring容器是怎样存储单实例

思路很简单,Java的世界中,存储多种对象有多种数据结构,还得方便取用,最方便的数据结构就是Map,但凡是容器的概念,一定是有存储对象的作用。所以,使用容器或者工厂或者Ioc容器来称呼spring工厂都是没毛病的

为什么有的人把Tomcat也称为容器

因为tomcat不仅仅创建servlet还存储servlet供其他人进行使用。

2:Spring基本工厂

ApplicationContext是Spring最常用的工厂,又叫应用上下文。实际上是Spring提供的较为高级的一个工厂,这个工厂是复合了很多个其他工厂的作用,他的角色被很多的其他Spring工厂所分担了,所以,这个工厂可以视为Spring工厂的一个门面。就像是Mybatis的门面是sqlSession,用sqlSession进行访问和操作,它为我们提供了很多的工具和方法,使用方便,接口单一。但是实际上,他的是线上是很复杂的。

对应ApplicationContext来讲,他是一个高级的工厂,但是他集齐了其他高级工厂的能力,应用的时候,使用这个高级工厂很方便,但是我们真正分析研究的时候,就不能用这个东西了,因为功能太多。

Spring工厂的最为核心和底层的工厂是BeanFactory,BeanFactory是一个接口。这个容器的最基本的接口,Spring工厂的最底层,就是这个接口了。SpringBean容器的根接口,Spring的真个设计过程中,严格遵守类型单一职责的概念,因此他的工厂为了实现这么复杂的功能,演变成了一个非常复杂的体系。

底层是接口,在Java认知体系当中我们知道,子接口一定是在父接口的基础上提供了一些个性化的东西。经过层层继承,我们就可以看到一个更为高级的ApplicationContext接口。

1):BeanFactory

首先Spring工厂最底层的类就是这个BeanFactory接口,他定义了工厂的基本操作,也就是存取,也就是getBean这些操作都是这个接口而生。

2):HierarchicalBeanFactory

这个接口让Spring容器具有了继承的能力,也就是让Spring中引入了父子容器的概念。

3):AutowireCapableBeanFactory

这个接口提供了自动注入的能力。自动注入的能力是怎么体现的呢?我们在Spring的Bean标签当中,配置的Autowired标签,提供了这个标签之后就可以实现注入,这个注入的能力是这个容器提供的。除此之外,它还帮我们做了,initialBean和DisposBean,这个是什么意思呢?就是对Bean来进行初始化和销毁。工厂为啥会具有初始化和销毁Bean的操作呢,就是这个工厂提供了这样的能力,而当我们在我们创建的Bean实现上述的两个接口时候,工厂的这两样能力就可以在这个Bean上体现。

4):SimpleJndiBeanFactory

这个基本没啥屌用

5):ListableBeanFactory

这个工厂可以去获取一些配置信息。比如说:constrainsBeanDefinition或着getBeanNameForType,我们可以通过这个方法来获取Spring创建的所有的Bean的信息(名字等)。

我们讲解这么多内容干什么,就是因为我们的Spring高级工厂为什么具有这么丰富的功能,就是以为你Spring工厂是由这一系列的工厂能力堆砌而来的。每一个的工厂都是功能单一的,但是他们把共鞥都堆砌到ApplicationContext上的时候,这个ApplicationContext就很牛逼了。

6):ConfigurableBeanFactory

这个工厂被称为可配置工厂,我们创建一个对象是单实例多实例,这个事就是由这个工厂来实现的,当然话还有类型转换器,类型转换器和后置处理Bean都是由这个工厂提供的能力。

3:XmlBeanFactory

以上这么多工厂,有两个工厂是将上述工厂的能力集大成的。(以上都是基础工厂)

ApplicationContext和DefaultListableBeanFactory

DefaultListableBeanFactory是集成了上述工厂类型的最集大成者,这两个是成熟的工厂。当前我们使用的是Xml进行配置的,这样的话我们使用的是DefaultListableBeanFactory这个工厂的一个子类XmlBeanFactory他能基于Xml配置来完成对象的创建和管理。这里边多亏了他一个强大的工具类:

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
这个玩意是用来读取xml的配置信息的。

基于此:我们就得到了一个分析的核心就是 XmlBeanFactory

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {
  private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
  /**
   * Create a new XmlBeanFactory with the given resource,
   * which must be parsable using DOM.
   * @param resource the XML resource to load bean definitions from
   * @throws BeansException in case of loading or parsing errors
   */
  public XmlBeanFactory(Resource resource) throws BeansException {
    this(resource, null);
  }
  /**
   * Create a new XmlBeanFactory with the given input stream,
   * which must be parsable using DOM.
   * @param resource the XML resource to load bean definitions from
   * @param parentBeanFactory parent bean factory
   * @throws BeansException in case of loading or parsing errors
   */
  public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);
  }
}

初看,这里边的功能也太少了,他自己的功能大部分都是从父类继承来的。这个类已经过期了:

Deprecated as of Spring 3.1 in favor of DefaultListableBeanFactory and XmlBeanDefinitionReader
自Spring3.1起已弃用,取而代之的是{link DefaultListableBeanFactory}和{XmlBeanDefinitionReader}

当前我们先按他学习,后期在进行替换。

1.读取配置文件,读取到JVM内存中,封装成Java对象。 
2.创建工厂生产的对象。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource('applicationContext.xml'));
3.工厂容器中获取对象。
User user = ("User")beanFactory.getBean("u");

1):Spring读取Xml的配置文件

new ClassPathResource(‘applicationContext.xml’)指定了Spring配置文件的类路径。

ClassPathResource是Spring定义的一个类型,实现了Resource接口通过类加载器获取数据流。

Resource

Spring当中一个非常核心的接口,读取相关资源文件的内容,获取数据流,不仅仅是xml文件,可以读取网络中的资源。实现不同的功能就体现在不同的实现类里边了。Resource继承了一个接口,InputStreamSource这里边有一个方法InputStream getInputStream() throws IOException;那么我们就知道,读取方式一定是引入一个输入流的这种形式,当我们有了输入流,一切的问题迎刃而解。

Alt+7调出类的全部方法内容,点击对应方法:

ctrl+h查看了类的继承关系。

@Override
  public InputStream getInputStream() throws IOException {
    InputStream is;
    if (this.clazz != null) {
      is = this.clazz.getResourceAsStream(this.path);
    }
    else if (this.classLoader != null) {
      is = this.classLoader.getResourceAsStream(this.path);
    }
    else {
      is = ClassLoader.getSystemResourceAsStream(this.path);
    }
    if (is == null) {
      throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
    }
    return is;
  }

Resource实现了继承了InputStreamSource接口,所有的内容读取都会以输入流的形式进行。

任何一个框架在开发和实现的过程中都会对常用的功能进行封装,Mybatis对反射进行了封装,Spring对输入流进行了封装。

FileSystemResource:从文件系统中获取输入流

UrlResource:从网络中获取输入流

ClassPathResource:从类路径中获取数据流,Resource的实现类,通过类加载器的方式获取输入流。

ByteArrayResource从字节数组中获取输入流

我们的xml都会基于这些类读取到我们的Jvm内存中,他是通过什么形式体现的呀?Spring配置文件的内容是以对象的形式存在,最终被封装成了一个BeanDefination,这些一个一个的Bean标签,

XML的解析,Mybatis的Xml解析是通过xpath,Spring的Xml解析方式是用的SAX

Mybatis当中对反射封装的很到位,Spring对流的使用,封装的也很到位。

Spring配置文件的内容,时以对象的形式,存储在JVM当中,Mybatis当中,Mybatis-Config.xml被解析封装成一个Configuration对象,Mapper.xml会被封装成一个MapperStatement这样的对象。在我们的Spring当中最终被封装成了一个BeanDefination这样的一个对象,这里的Bean对应着配置文件中的一个个Bean标签。

我们如何把Spring核心配置文件经过XML解析(SAX)之后,封装成BeanDefination对象。Mybatis用的是xpath这个解析工具比较新。

Spring当中的Resource接口继承了InputStreamSource接口(接口与接口之间的关系也叫做继承)

复习:

整体的SPRING工厂,BeanFactory是Spring当中最为底层的工厂。几个核心工厂:

XmlBeanFactory最大的特点:可以帮我们读取Xml配置文件来帮我们创建对应的对象,如何使用:

1.怎么读取配置文件?
2.读取了配置文件之后,我们如何在Spring当中以对象的形式进行封装?
3。如何获取导配置信息之后,根据配置信息创建对象?
4.所创建对象的生命周期如何整理?
以上几句话以及涵盖了SpringIOC的全部的核心内容。包括我们所说的Spring中对象的注入是从哪个
位置完成注入的?这个是从对象的生命周期这个阶段当中进行注入的。包括我们的回环应用,或者叫循
环依赖,也是在创建对象和生命周期当中进行体现的。
BeanFactory beanFactory = new XmlBeanFactory("Resource...");
BeanFactory.getBean
我们在研究一个框架的源码研究的时候,不可能研究明白他的放方面面,抓大放小,抓主去次。

1.怎么读取配置文件?

spring当中做了很好的封装,基于各种的Resouce的实现类,最终以InputStream的形式读取进来。

2.读取了配置文件之后,我们如何在Spring当中以对象的形式进行封装?

Resource接口实现类通过这个接口的某一个实现类,就是打开输入流,可读取资源导虚拟机当中。我们都进来之后,我们必须以对象的形式来存储。所有的框架都是这个尿性,Spring当中式搞成了一个BeanDefination对象,他是一个对象,他的是实现类,后续的我们使用最多的实现类是GenericBeanDefination继承关系如下:

BeanDefination实现了Cloneable接口,实现类为GenericBeanDefination,从输入流到GenericBeanDefination对象的过程当中是由XmlBeanDefinitionReader作的。XmlBeanDefinitionReader这个类型是在我们的XMLBeanFactory这个对象当中来进行应用的。所以我们为啥从XMLBeanFactory开始学习,XMLBeanFactory具有Spring工厂完成的功能,而且是通过解析xml来进行对象的创建。这样就得通过XmlBeanDefinitionReader这个组件来操作。如果哪天XMLBeanFactory这个东西过期了,我们可能需要自己去手动调用解析。

2):XmlBeanFactory替换

尝试替换的思路:我们用DefaultlistableBeanFactory替换XMLBeanFactory

@Test
    public void test() {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext_tx.xml"));
        Product product = (Product) beanFactory.getBean("p");
        //DefaultlistableBeanFactory 替换 XmlBeanFactory
        //DefaultListableBeanFactory + XmlBeanDefinitionReader
      //过期之后的代码我们只能通过这个写法来进行替换了。
        DefaultListableBeanFactory beanFactory1 = new DefaultListableBeanFactory();
        Resource resource = new ClassPathResource("applicationContext_tx.xml");
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory1);
        xmlBeanDefinitionReader.loadBeanDefinitions(resource);
        Object product1 = beanFactory1.getBean("product");
        System.out.println("product1 = " + product1);
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("u");
//        System.out.println("user.getName() = " + user.getName());
//        System.out.println("user.getPassword() = " + user.getPassword());
    }

如果或者Bean的时候没有:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'u' available
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:773)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
  at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
  at com.baizhiedu.SpringTest.testshit(SpringTest.java:161)

我们为什么不直接在Spring源码环境当中去写测试类,因为编译一坨坨Spring源码太特么耗时间了。新建一个Maven工程,使用Spring的依赖,然后只编译自己那几行代码他不香么。

Spring底层是如何通过BeanDefinitionReader组件将XMl封装成GenericBeanDefination对象的呢?

在Spring当中允许在一个工程当中有多个Spring工厂同时存在,

这种情况非常少见SpringMVC中的父子容器。

第二个视频看了半个小时

相关文章
|
19天前
|
Java Spring
spring 源码分析——第二篇(ioc 注解方式)
spring 源码分析——第二篇(ioc 注解方式)
43 0
|
9月前
|
缓存 Java Spring
详解Spring自定义消息格式转换器及底层源码分析
详解Spring自定义消息格式转换器及底层源码分析
|
19天前
|
安全 前端开发 Java
Spring Security 自定义异常失效?从源码分析到解决方案
Spring Security 自定义异常失效?从源码分析到解决方案
|
7月前
|
Java 测试技术 API
源码分析系列教程(02) - 手写Spring事务框架
源码分析系列教程(02) - 手写Spring事务框架
53 0
|
19天前
|
Dubbo Java 应用服务中间件
Dubbo 第四节: Spring与Dubbo整合原理与源码分析
DubboConfigConfigurationRegistrar的主要作⽤就是对propties⽂件进⾏解析并根据不同的配置项项⽣成对应类型的Bean对象。
|
19天前
|
安全 Java 数据安全/隐私保护
【Spring Security】Spring Security 认证过程源码分析
【Spring Security】Spring Security 认证过程源码分析
43 0
|
9月前
|
缓存 Java Spring
Spring中Bean创建过程之源码分析
Spring中Bean创建过程之源码分析
55 0
|
19天前
|
缓存 Java uml
SpringBoot2 | Spring IOC 流程中核心扩展接口的12个扩展点源码分析(十一)
SpringBoot2 | Spring IOC 流程中核心扩展接口的12个扩展点源码分析(十一)
52 0
|
19天前
|
缓存 Java uml
SpringBoot2 | Spring AOP 原理深度源码分析(八)
SpringBoot2 | Spring AOP 原理深度源码分析(八)
69 0
|
6月前
|
Java Spring 容器
Spring Boot启动命令参数详解及源码分析
Spring Boot启动命令参数详解及源码分析
170 1