【spring源码系列-01】spring底层源码整体概述

简介: 【spring源码系列-01】spring底层源码整体概述

一,spring源码整体概述

1,初步概述

在分析这个源码之前,首先需要去官网下载这个spring的源码包,建议下载5.x.x的版本。我这里安装的是:https://github.com/spring-projects/spring-framework/tree/5.2.x


spring框架在我们的认知中,是一个框架,也是一个生态。在spring中,最主要的就是两部分:一个是IOC,另一个是AOP。 IOC由被称为控制反转,是一种思想,就是将对象交给容器管理,而与其对应的是DI,被称为依赖注入,是IOC的一种具体实现。AOP是在IOC基础的一个升华,因此需要透彻的了解IOC之后,再来了解AOP。


IOC一般是作为bean对象的容器,其构建对象的基本流程如下,中间省略部分扩展点,如一些后置处理器,增强器等,后序会一一补上。下面这张图主要是针对于注解的方式获取bean的流程图。


fdef1aff0a22423490d150ec2f34dac7.png

1,获取上下文一般会通过两种方式获取,一种是通过XML的方式获取,一种是通过注解的方式获取,XML在现在流行的如日中天的springboot中很少使用,因此这里也选择通过注解的方式获取这个上下文ApplicationContext。

public class MainClass {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(MainCofig.class);
    User user = (User)context.getBean("user");
    System.out.println("user的类型:"+user.getClass());
  }
}
@Configuration
@ComponentScan(basePackages = {"com.zhs.study"})
public class MainCofig {
}

当然,也可以通过这个XML的方式来获取上下文,在最初接触spring的时候,就是将bean卸载xml文件中的。


ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("xxx.xml");

2,而不管是使用这个XML的配置文件还是使用这个注解的方式获取到上下文,他们在调用getBean之前,都得生成一个BeanDefinition的bean定义,有了具体的bean定义之后,才能通过这个bean工厂去生产或者获取这个bean对象。而在获取到一个统一的BeanDefinition之前,那么就需要通过实现这个BeanDefinitionReader接口来将不同的上下文配置信息转换成统一的BeanDefinition

public interface BeanDefinitionReader {...}

BeanDefinitionReader接口有着具体的实现类,如下图所示,有着上面所说的xml和注解生成bean定义的读取器等,相当于作为一个统一的解析器,



3b4eef0328bb41f2a33f9b29bf76b7bb.png

随后通过一个BeanDefinitionRegistry的注册器,将读取到的东西注册成一个BeanDefinition,这样就能统一的生成一个bean工厂可以识别的一个BeanDefinition对象了


protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
  try {
    Document doc = doLoadDocument(inputSource, resource);
    return registerBeanDefinitions(doc, resource);
  }
}

由于生成的beanDefinition的数量比较多,因此会先将这个bean定义先加入到这个beanDefinitionMap里面。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

3,随后会调用这个getBean()方法,从这个bean工厂中获取值,如果这个bean定义存在则直接获取,不存在则先创建再将值返回。

Student student = (Student) context.getBean("student");

在获取这个bean的过程中,主要会经历实例化,属性填充和初始化三个主要的过程,分别是实例化、属性填充、初始化 这三个主要的步骤。实例化就是在堆内存中开辟一块空间,并且给对象的属性值赋予默认值;属性填充就是往这个对象中set最终的值;最后再执行init初始化方法。并且这三个步骤的顺序不能改变。

da9498cf133b4ad385a9df8e45dd0017.png



在填充属性到这个初始化的过程中,可以设置一些Aware接口的属性,实现这些Aware接口可以获取上下文等。如某个对象想获取整个上下文的信息,就可以实现ApplicationContextAware,外部获取到这个实例之后,就可以直接通过getApplicationContext方法获取上下文。

//实现上下文的aware接口
@Component
public class First implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    //实现方法
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    //将值返回
    private ApplicationContext getApplicationContext(){
        return applicationContext;
    }
}

Aware的作用就是:当Spring容器创建的bean对象在进行具体操作的时候,如果需要容器中的其他对象,此时可以将对象实现这个Aware接口,从而满足当前的需要。


除了设置Aware,还可以设置一些BeanPostProcessor 处理器,首先会调用BeanPostProcessor.before()方法,最后会调用这个BeanPostProcessor.after() 方法,在这两个处理器中间,执行的就是上面说的init初始化方法。而这两个处理器的方法如下

//前置处理器
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  return bean;
}
//后置处理器
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  return bean;
}

4,随后生成一个完整的bean对象,存储在这个concurrentHashMap一级缓存中,后续直接在map中获取实例即可。这就是一个粗略的的ioc流程。


5,在spring容器中,又将spring bean对象分为普通对象和容器对象,容器对象相当于一个内置的对象,而普通对象相当于自定义对象。内置对象就是类似于一些需要提前加载的类,并没有显式的自定义一些对象,自定义对象就是一些自定义在xml中或者注解中的一些实例对象。


2,扩展点机制

在上述粗略的描述了一下IOC的整体流程,除了上面这些组件之外,还存在的一些扩展点机制,如一些PostProcessor 后置处理器,主要有BeanFactoryPostProcessor和BeanPostProcessor处理器,前者主要是用来增强beanDefinition信息的,后者可以用来增强bean信息的,如aop等。


BeanFactoryPostProcessor的接口信息如下,并且该接口是一个函数式接口,在spring容器中,该接口是以集合的形式存在,就是会有多个这种bean工厂的后置处理器。

//函数式接口
@FunctionalInterface
public interface BeanFactoryPostProcessor {
  void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanPostProcessor的接口信息如下,里面有两个方法,被称为后置处理器的前置方法和后置处理器的后置方法

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;
  }
}

如下面这段BeanFactoryPostProcessor的举例,在实现这个接口之后,重写里面的postProcessBeanFactory方法就可以获取到beanDefination,并且对这个bean定义做一个增强的功能。里面可以设置一些bean的描述,加一些描述信息,是否设置成类加载等等。

/**
 * 自定义bean工厂的后置处理器类
 * 对beanDefinition做增强功能
 * @author zhenghuisheng
 * @date : 2023/5/25
 */
@Component
public class BeanFactoryTest implements BeanFactoryPostProcessor {
    /**
     * @param beanFactory : beanDefinition
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("taskTestController");
        taskTestController.setDescription("hhhhha");
        System.out.println("=================taskTestController增强完成!");
        System.out.println(taskTestController.getDescription());
    }
}

3,核心方法refresh

除此之外,xml方式的构造方法和注解的构造方法里面,里面都有一个重要的方法refresh ,他们的构造方法如下

//XML的方式获取到上下文
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
  super(parent);   // 初始化父类 ,获得xml路径资源解析器
  setConfigLocations(configLocations);  // 通过环境变量解析 xml路径
  if (refresh) {
    refresh();   // 这个方法时spring是最终要的一个方法,甚至体系整个ioc的声明周期
  }
}
//注解的方式获取上下文
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
  //调用构造函数
  this();
  //注册我们的配置类
  register(annotatedClasses);
  //IOC容器刷新接口
  refresh();
}

而在这个refresh方法中,可以说是整个spring底层的核心,其代码如下,因此后文的spring源码的继续分析,基本就是围绕下面的这些方法。

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    //1:准备刷新上下文环境
    prepareRefresh();
    //2:获取告诉子类初始化Bean工厂  不同工厂不同实现
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    //3:对bean工厂进行填充属性
    prepareBeanFactory(beanFactory);
    try {
      // 第四:留个子类去实现该接口,bean工厂的后置处理器
      postProcessBeanFactory(beanFactory);
      // 调用我们的bean工厂的后置处理器. 
          //1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
      invokeBeanFactoryPostProcessors(beanFactory);
      // 注册我们bean的后置处理器
      registerBeanPostProcessors(beanFactory);
      // 初始化国际化资源处理器.
      initMessageSource();
      // 创建事件多播器
      initApplicationEventMulticaster();
      // 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
      onRefresh();
      //把我们的事件监听器注册到多播器上
      registerListeners();
      // 实例化我们剩余的单实例bean.
      finishBeanFactoryInitialization(beanFactory);
      // 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
      finishrefresh();
    }
    }
}

4,BeanFactory和FactoryBean的区别

BeanFactoy是一个大的容器接口,ApplicationContext是其具体的实现接口,主要用于创建和获取对象,使用这个BeanFacory的时候必须遵循完整的创建的过程,这个过程是由spring来控制管理的。


FactoryBean只需要调用getObject就可以返回具体的对象,整个对象的创建过程是由用户自己来控制的,更加灵活,也可以用来创建一些特殊的bean,更加复杂的bean。在这个FactoryBean对象中,主要有三个方法,分别是:IsSingleton、getObject、getObjectType 这三个方法,使用的最频繁的就是getObject这个方法。


在preInstantiateSingletons 的这个方法中,有一段判断逻辑如下,判断这个bean是不是工厂bean

//是不是工厂bean
if (isFactoryBean(beanName)) {...}

如果不获取该对象,那么该对象具体的值是看不见的,因为该对象没有提前创建。接下来再看一段代码,一个实体类实现这个bean工厂,再创建一个老师对象和学生对象的实体类

/**
 * @author zhenghuisheng
 * @date : 2023/5/29
 */
@Component
public class TeacherFactory implements FactoryBean<Teacher> {
    @Override
    public Teacher getObject() throws Exception {
        return new Teacher();
    }
    @Override
    public Class<?> getObjectType() {
        return Teacher.class;
    }
}

接下来通过上下文获取这个工厂中的实体类,查看结果如下

//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");
//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");

也就是说可以通过这个FactoryBean返回的对象不是当前对象,也可以通过这个& 获取原来的值,可以使得获取值的方式更加的灵活。而使用这个FactoryBean的意义就是:只需要通过重写这个getObject方法就可以获取到对应的bean对象,而不需要完全遵守这个bean的生命周期


相关文章
|
1天前
|
设计模式 开发框架 Java
Spring及工厂模式概述
Spring及工厂模式概述
29 8
|
1天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
37 0
|
1天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
1天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
20 6
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
25 3
|
1天前
|
前端开发 Java 应用服务中间件
Spring MVC框架概述
Spring MVC 是一个基于Java的轻量级Web框架,采用MVC设计模型实现请求驱动的松耦合应用开发。框架包括DispatcherServlet、HandlerMapping、Handler、HandlerAdapter、ViewResolver核心组件。DispatcherServlet协调这些组件处理HTTP请求和响应,Controller处理业务逻辑,Model封装数据,View负责渲染。通过注解@Controller、@RequestMapping等简化开发,支持RESTful请求。Spring MVC具有清晰的角色分配、Spring框架集成、多种视图技术支持以及异常处理等优点。
16 1
|
1天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
15 1
|
1天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
102 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
1天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
1天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
http://www.vxiaotou.com