3

Spring 学习总结

 1 year ago
source link: https://www.r9it.com/20221204/spring-summary.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring 学习总结


RockYang 2022-12-04 0 Spring 源码系列

学习小贴士

一个人在下水之前,无论他看多少游泳指导手册,学习多少游泳技巧都是没有用的,因为他无法真实地感受到水压和漂浮感。

或许,对于实践类的学习内容来说,从来不存在什么入门指导,只有进阶指导。因为入门不是靠技巧,而是靠自己先动手“趟”出来的。

一个在刚开始学习一个新东西之前,应该少想多做,因为想得太多的话,很容易感觉压力铺面而来,以致于没有勇气开始。

还是那句话:想,都是问题;做,才有答案。

作为一个十年的 PHPer(虽然现在用 Java 和 Golang 更多一些),习惯了 PHP 的轻盈,灵活,一直对于 Spring 这种重型框架心存芥蒂。 自从决定做一个修行者之后,感觉自己看事物的角度丰富了一些,因为放弃了身份认同,愿意接受更多的身份和可能性。 最近因为工作需要,又从 Golang 转到 Java 开发了,想到既然工作中避不开它,不妨怀着谦卑的态度去看一看 Spring 到底是何方神圣,它到底有何魅力以至于众多 Java 开发者趋之若鹜。 结果看完源码之后我很庆幸没有错过它,Spring 的设计确实有大学问。

TIPS 分享一个自己看源码的心得:

  1. 先在脑子里面过一下,如果自己是设计者,会怎么设计这个功能,如果可能的话先按照自己的思路把这个项目写一个能跑通流程的 1.0 版本,这非常重要,能给你后面看源码的时候带来很大的自信。 如果你在后续读源码的时候发现某些地方作者和你的实现方法居然是一样的,那种愉悦的感觉会激励你继续把源码读下去。
  2. 去网上找一下作者的设计原理,对照一下自己的设计,找找差距,一般官方文档上面都会有大概的描述,即使没有,广大网友肯定已经有人已经整理了。
  3. 对照官方的原理和流程图,在源码中打断点来一步一步验证自己的猜测。
  4. 遇到不理解的问题,记得要向作者或者社区(GitHub)提问,这个很重要,千万不要怕麻烦

# 01 - Spring 框架体系

当我们在谈论 Spring 的时候,我们在谈论什么呢?

Spring 框架是由很多模块组成的,应用程序可以选择他们需要的模块。容器模块整个 Spring 框架的核心(spring-bean, spring-context),我们熟知的配置模型和依赖注入就是在这个模块实现的。 除此之外,Spring 还为不同的应用程序架构提供基础支持,包括国际化、事务库事务和持久化,以及 Web。它还包括基于 Servlet 的 Spring MVC Web 框架,以及 Spring WebFlux 响应式 Web 框架。

除此之外,Spring 官方在 Spring 框架的基础上开发了很多其他项目,比如 SpringBoot, Spring Cloud 等。大多数情况下,当人们说 Spring 时,他们指的是整个项目系列,也叫 Spring 全家桶

本文仅仅讨论 Spring 框架本身。

# 02 - 设计理念

想要深入学习一个框架,那么你就不能只停留在【术】的层面,也就是说你不仅要知道她有那些功能,如何使用她,你还需要了解她背后的【道】-- 只有了解她的设计理念之后你对她的使用才能得心应手。

Spring 官方文档上有阐述自己的设计理念 (opens new window)

  1. Provide choice at every level. Spring lets you defer design decisions as late as possible. For example, you can switch persistence providers through configuration without changing your code. The same is true for many other infrastructure concerns and integration with third-party APIs.

    在每个层次提供选择。Spring允许您尽可能延迟设计决策。例如,在不改变你的代码的情况下,通过配置来切换持久化提供程序。在许多其他基础设施问题和与第三方 API 的集成上也同样遵循这个原则。

  2. Accommodate diverse perspectives. Spring embraces flexibility and is not opinionated about how things should be done. It supports a wide range of application needs with different perspectives.

    容纳不同的观点。Spring拥抱灵活性并且对于事情应该如何处理并不会固执己见。它支持各种不同角度的应用程序需求。

  3. Maintain strong backward compatibility. Spring’s evolution has been carefully managed to force few breaking changes between versions. Spring supports a carefully chosen range of JDK versions and third-party libraries to facilitate maintenance of applications and libraries that depend on Spring.

    保持强大的向后兼容性。Spring的发展经过精心的管理,几乎没有在版本之间进行破坏性的更改。Spring 对与JDK版本和第三方库都是经过精心选择的,以方便依赖于 Spring 的应用程序和库的维护。

  4. Care about API design. The Spring team puts a lot of thought and time into making APIs that are intuitive and that hold up across many versions and many years.

    关心API设计。Spring 团推花费了大量的心思和时间在 API 设计上,这些 API 是直观的。这些API可以在多个版本中使用,并可以使用很多年。

  5. Set high standards for code quality. The Spring Framework puts a strong emphasis on meaningful, current, and accurate javadoc. It is one of very few projects that can claim clean code structure with no circular dependencies between packages.

    为代码质量设定高标准。Spring 框架非常强调有意义的、最新的和准确的 Java 文档。它是极少数可以声称代码结构清晰且包之间没有循环依赖关系的项目之一。

作为一个 PHP 的铁杆粉丝,我刚接触 Spring 的时候,感觉她非常臃肿,很多设计的华而不实,因为很多人根本用不到。估计很多从 PHP,Golang 等转 Java 开发的工程师都会有跟我类似的想法。

从 Spring 的设计原则中,我们不难找到答案。Spring 想要打造的是一个[好用的产品],而不是一个高性能的[工具框架]。

就拿第一条原则来说:在每个层次提供选择,简单来说就是 Spring 在各个层面都为开发者 “留了后门”,你随时都可以配置并插入自己的实现,这样给开发者很大的发挥空间。 比如在初始容器的时候你可以实现自己 BeanFactory,在初始化 BeanFactory 的时候你可以注册自己的 BeanDefinition,你也可以定义一个 FactoryBean 完完全全自己来创建 Bean, 甚至可以在 Bean 的任何生命周期进行拦截做一点自己的事情...

她有点像一辆带有自动驾驶功能的汽车:如果你自己会开,我可以完全把驾驶权交到你手上,如果你不会开,那也没有关系,切换到自动驾驶就好了。

可以说,Spring 真正做到了框架只是给你赋能,而不是“框”住你,你既可以随心所欲的发挥你的创意,又不用担心系统框架层面的问题。

再比第三条原则:保持强大的向后兼容性,一方面 Spring 要不断升级优化加入好用的 API,而另一方面 Spring 为降低使用旧版本的开发者维护成本,依然保留着旧的 API。 这样的设计必然会使得框架越来越臃肿,但是这就是 Spring 的风格。Spring 有点像这么一个中年大叔,他未必有什么过人的能力和英俊的外表,可能平时你还嫌弃他唠叨,但是关键时刻他能给你带来安全感, 任何时刻你都能在他那里得到确定性。对,就是确定性,通俗的说法叫做【靠谱】。

保持强大的向后兼容性,也许是 Spring 这么受开发者欢迎的一个重要原因,我前 4 年前因为转到区块链行业,基本都是在使用 Golang 和 Rust 语言,可以说是几乎没怎么接触 Spring 了, 但是现在一上手,还是感觉顺畅无比,还是当年的感觉。可以说 Spring 能让你有一种:出走半生,归来还是少年的感觉。

再说回到臃肿的问题,殊不知在 Spring 之前大家使用的 Java 企业级开发框架是 EJB,其复杂和臃肿程度几乎比 Spring 高出一个数量级。 当然近些年也不乏有轻量级的 Java Web 框架出现,比如 JFinal,Solon 等一些国产 Java 应用开发框架。但是个人认为这些框架在设计上跟 Spring 没有什么本质的区别。

所谓的高性能和轻量级本质上其实就是不同价值观下的取舍而已,那种既安全,又高效,扩展性好,使用方便的框架根本是不存在的。

所以,如果一个人根据自己的需求权衡之后决定不使用 Spring 这个完全没有任何问题,但是如果他硬要说某某 Java Web 框架甩 Spring 几条街,那我会认为他不仅缺乏理性且有王婆卖瓜的嫌疑。

# 03 - 从 Bean 工厂开始

对不起,我不是针对谁,我是说你们所有的对象在我眼里都是一个 Bean 而已。

在 Spring Framework 中,Bean 是一等公民,所有的这些 Bean 都由 BeanFactory 来管理,很明显,BeanFactory 会负责创建 Bean,并且提供获取 Bean 的 API。

而我们通常所说的 Spring 容器 ApplicationContext 也是 BeanFactory 的一种,在 Spring 源码中,是这么定义的:

ublic interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
		    ...
}
Copied!

这个定义暴露 ApplicationContext 的所有功能,首先 ApplicationContext 继承了 ListableBeanFactoryHierarchicalBeanFactory, 而 ListableBeanFactoryHierarchicalBeanFactory 都继承至 BeanFactory,所以 ApplicationContext 继承了 BeanFactory 的所有功能。

除此之外,还继承了 MessageSourceApplicationEventPublisherEnvironmentCapable 等接口,总体来说 ApplicationContext 拥有以下功能。

  1. HierarchicalBeanFactory: 拥有获取父 BeanFactory 的功能
  2. ListableBeanFactory: 拥有获取beanNames的功能
  3. ResourcePatternResolver: 资源加载器,可以一次性获取多个资源(文件资源等等)
  4. EnvironmentCapable: 可以获取运行时环境(没有设置运行时环境功能)
  5. ApplicationEventPublisher: 拥有广播事件的功能(没有添加事件监听器的功能)
  6. MessageSource: 拥有国际化功能

所以你可以把 ApplicationContext 理解为一个超级 Bean 工厂。ApplicationContext 有两个比较重要的实现类:

  1. AnnotationConfigApplicationContext 基于 Config 类注解来启动容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    
    Copied!
  2. ClassPathXmlApplicationContext 基于 Spring XML 配置文档来启动容器
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = context.getBean("userService", UserService.class);
    
    Copied!

Spring 中另一个重要的核心 Bean 工厂为 DefaultListableBeanFactory。事实上,Spring 容器中主要使用的也是这个 Bean 工厂实现。我们也可以直接使用它而不用使用 ApplicationContext:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanFactory.registerBeanDefinition("user", beanDefinition);
System.out.println(beanFactory.getBean("user"));
Copied!

DefaultListableBeanFactory 是非常强大的,支持很多功能,可以通 DefaultListableBeanFactory 的继承结构图来直观感受一下:

image-61297c54585c90dad57278325672d945.png

# 04 - Bean 生命周期

Spring 中通过 BeanDefinition 来定义 Bean。你可以把 BeanDefinition 当做 Bean 工厂中生产 Bean 的模具。 BeanDefinition 中定义了很多属性来描述一个 Bean 的特点,比如:

  • class:表示Bean类型
  • scope:表示Bean作用域,单例或原型等
  • lazyInit:表示Bean是否是懒加载
  • initMethodName:表示Bean初始化时要执行的方法
  • destroyMethodName:表示Bean销毁时要执行的方法

在 Spring 中,我们经常会通过以下几种方式来定义 Bean:

  1. <bean /> XML 节点
  2. @Bean 方法注解
  3. @Component(@Service,@Controller) 类注解

通过 <bean/>@Bean@Component 等申明式方式所定义的 Bean,最终都会被 Spring 解析为对应的 BeanDefinition 对象,并放入 Spring 容器中,这种称之为 声明式定义 Bean

我们还可以通过*编程式申明 Bean**,也就是自定义一个 BeanDefinition 来创建 Bean。

AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
// 生成一个 BeanDefinition 对象,并设置 beanClass 为 User.class,并注册到 ApplicationContext 中
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);

// 我们还可以通过BeanDefinition设置一个Bean的其他属性 
beanDefinition.setScope("prototype"); // 设置作用域
beanDefinition.setInitMethodName("init"); // 设置初始化方法
beanDefinition.setLazyInit(true); // 设置懒加载

context.registerBeanDefinition("user", beanDefinition);
System.out.println(context.getBean("user"));
Copied!

接下来看看 Bean 的具体生成过程,也就是我们常说的 Bean 的生命周期:

# 1. 生成BeanDefinition

Spring 启动的时候会扫描指定的包路径,生成 BeanDefinition 集合。值得一题的是,Spring 利用的 ASM 技术直接扫描解析 .class,并没有加载这个类到 JVM。

# 2. 合并 BeanDefinition

Spring 支持父子 BeanDefinition,子类 BeanDefinition 会继承父类 BeanDefinition 的属性:

<bean id="parent" class="com.spring.service.Parent" scope="prototype"/>
<bean id="child" class="com.spring.service.Child"/>
Copied!

Spring 默认的 Bean 都是单例 Bean 的,但是上述的 child Bean 确继承了 parent Bean 的 scope 属性,所以它也是原型 Bean。

# 3. Bean 实例化前

BeanDefinition 对应的类成功加载后,就可以实例化对象了,但是在 Spring 中,实例化对象之前,Spring 提供了一个扩展点,允许用户来控制是否在某个或某些 Bean 实例化之前做一些启动动作。 这个扩展点叫 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 比如:

@Component
public class TestBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException 
    {
        // 这里可以做一些自己的事情
        if ("userService".equals(beanName)) {
            System.out.println("实例化前");
            return new UserService();
        }
        return null;
    }
}
Copied!

如上代码会导致,在 userService 这个 Bean 实例化前,会进行打印。值得注意的是 postProcessBeforeInstantiation() 这个方法是有返回值的。如果返回值不是 null 的话,表示这个 Bean 不需要 Spring 来实例化了,Spring 也不会对这个 Bean 进行依赖注入了,会跳过一些步骤,直接进入执行后面的初始化后这个步骤。

# 4. Bean 实例化

Spring Bean 实例化分为几个步骤:

首先判断 BeanDefinition 中是否设置了 Supplier, 如果设置了则调用 Supplier 的 get() 方法得到对象:

AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {
	@Override
	public Object get() {
		return new UserService();
	}
}
);
context.registerBeanDefinition("userService", beanDefinition);
Copied!

如果没有设置 Supplier,则检查 BeanDefinition 中是否设置了factoryMethod,也就是工厂方法,比如:

<bean id="userService" class="com.spring.service.UserService" factory‐method="createUserService" />
Copied!

对应的 UserService 类为:

public class UserService {
	public static UserService createUserService() {
		System.out.println("执行createUserService()");
		UserService userService = new UserService();
		return userService;
	}
	public void test() {
		System.out.println("test");
	}
}
Copied!

如果既没有设置 Supplier 也没有设置 factoryMethod,那么 Spring 接下来将会找到当前类合适的构造方法来实例化对象。

# 6. BeanDefinition 的后置处理

Bean 对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring 又提供了一个扩展点 MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以对此时的 BeanDefinition 进行加工,比如给当前 Bean 的属性赋值:

@Component
public class TestMergedBeanDefinitionPostProcessor implements
MergedBeanDefinitionPostProcessor {
	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
	Class<?> beanType, String beanName) {
		if ("userService".equals(beanName)) {
		    // 初始化之后会自动调用实例的 setOrderService() 方法,并将参数传入。
		    // 注意,这个赋值是最终的赋值,意味着它的优先级最高,会覆盖 @Autowired 等注解注入的值
			beanDefinition.getPropertyValues().add("orderService", new OrderService());
		}
	}
}
Copied!

# 7. 实例化后

在处理完 BeanDefinition 后,Spring 又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),比如:

@Component
public class TestInstantiationAwareBeanPostProcessor implements
InstantiationAwareBeanPostProcessor {
	@Override
	public Boolean postProcessAfterInstantiation(Object bean, String beanName) throws
	BeansException {
		if ("userService".equals(beanName)) {
			UserService userService = (UserService) bean;
			userService.test();
		}
		return true;
	}
}
Copied!

在这个扩展中,你可以拿到实例化后的对象进行处理,但是貌似这个扩展没什么使用场景,因为刚刚实例化的 Bean 基本就是个半成品,都没有完成依赖注入,拿着基本也没有什么用。 但是按照 Spring 的设计哲学:能留出扩展点的尽量留出,没准后面有用呢。

# 8. 自动注入

这里应该算是 Spring Bean 生命周期的核心部分了。Spring 的自动注入主要分为下面几种形式:

  1. 通过构造方法传参的形式来注入参数。
  2. 通过给属性或者方法添加 @Autowire, @Resource@Value 注解来实现属性或者参数的自动注入。
  3. 通过自动调用 Bean 的 setXxx() 方法来注入。

# 9. 执行 Aware

完成了属性赋值之后,Spring 会执行一些回调,包括:

  1. BeanNameAware: 回传 beanName 给 bean 对象。
  2. BeanClassLoaderAware: 回传 classLoader 给 bean 对象。
  3. BeanFactoryAware: 回传 beanFactory 给 bean 对象。

通过这些回调你可以拿到 Bean 的名字,ClassLoader 和 BeanFactory,例如获取 Bean 名字:

@Component
public class UserService implements BeanNameAware {

	private String beanName;

	public void test()
	{
		System.out.println("test");
	}

	@Override
	public void setBeanName(String name)
	{
		System.out.println(name);
		this.beanName = name;
	}
}
Copied!

# 10. 初始化前

在执行 Bean 初始化前,Spring 又提供了一个扩展点:BeanPostProcessor.postProcessBeforeInitialization(),比如:

@Component
public class TestBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws
	BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化前");
		}
		return bean;
	}
}
Copied!

利用上面这个扩展点,你可以对 完成了依赖注入的 bean 进行处理。比如 Spring 源码中,InitDestroyAnnotationBeanPostProcessor 会在初始化前这个步骤中执行 添加了 @PostConstruct 注解的方法。

# 11. 初始化

  1. 查看当前 Bean 对象是否实现了 InitializingBean 接口,如果实现了就调用其 afterPropertiesSet() 方法。
  2. 执行 BeanDefinition 中指定的初始化方法(initMethodName)。

# 12. 初始化后

这是 Bean 创建生命周期中的最后一个步骤,也是 Spring 提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization(),比如:

@Component
public class TestBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws
	BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化后");
		}
		return bean;
	}
}
Copied!

可以在这个步骤中,对 Bean 最终进行处理,Spring 中的 AOP 就是在这个步骤中实现的,如果 AOP 拦截成功则返回一个当前 Bean 的代理对象。

总结一下,整个 Bean 创建过程中的扩展点基本都是通过 BeanPostProcessor 实现的,他们把 Bean 的创建流程切成一个个切面。简化版的 Bean 创建流程如下:

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
  3. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  4. InstantiationAwareBeanPostProcessor.postProcessProperties()
  5. Aware对象
  6. BeanPostProcessor.postProcessBeforeInitialization()
  7. BeanPostProcessor.postProcessAfterInitialization()

# 05 - 依赖注入

如果问 Spring 最大的贡献是什么,也许就是她是第一个使得 IOC(控制反转) 这种设计原则如此的深入人心。这也使得依赖注入成为 Spring 最显著的功能特性。

Spring 依赖注入主要两种实现形式,XML 配置 Bean 的方式和注解方式,考虑到现在基本很少有人用 XML 的方式配置 Bean 了,这里直接略过。

Spring 在匹配 Bean 类型的时候主要分 2 种方式:找 Bean 类型查找(byType)和按 Bean 名称查找(byName),再具体使用的时候通常都是两种结合。

实现自动注入的注解类主要有 4 个:@Autowired@Resource@Inject@Value

  1. @Autowired 注解:先通过类型去查找 Bean,找到多个话再通过名称去筛选。
  2. @Resource 注解:先通过名称去查找,如果找到则立即返回,没有找到则继续通过类型查找。
  3. @Inject 注解: 同 @Resource 注解
  4. @Value 注解: 注入配置文档,环境变量等。
    @Component
    class User {
        @Value("${key}") // 注入配置值 
        private String name;
        
        @Value("#{orderService}") // 注入名字为 orderService 的 Bean
        private OrderService orderService;
        
        @Value("#{admin[a-z]{1,5}}") // 解析 SPEL 表达式
        private Admin admin;
    }
    
    Copied!

依赖注入的具体实现过程如下:

# 1. 扫描注入点,加入列表

Spring 会利用 AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition() 找出注入点并缓存, 找注入点的流程为:

  1. 遍历当前类的所有的属性字段 Field
  2. 查看字段上是否存在 @Autowired@Value@Inject 中的其中任意一个,存在则认为该字段是一个注入点。
  3. 如果字段是 static 的,则不进行注入。
  4. 获取 @Autowired 中的 required 属性的值,如果为 false 则不进行注入。
  5. 将字段信息构造成一个 AutowiredFieldElement 对象,作为一个注入点对象添加到 currElements 集合中。
  6. 用同样的方法遍历所有的方法。将字段信息构造成一个 AutowiredMethodElement 对象,作为一个注入点对象添加到 currElements 集合中。
  7. 最后将 currElements 集合封装成一个 InjectionMetadata 对象,作为当前Bean对于的注入点集合对象,并缓存。

# 2. 遍历注入点,进行依赖注入

Spring 在 AutowiredAnnotationBeanPostProcessor.postProcessProperties() 方法中,会遍历所找到的注入点依次进行注入。

字段注入:

  1. 遍历所有的 AutowiredFieldElement 对象。
  2. 将对应的字段封装为 DependencyDescriptor 对象。
  3. 调用 BeanFactoryresolveDependency() 方法,传入 DependencyDescriptor 对象,进行依赖查找,找到当前字段所匹配的 Bean 对象。
  4. DependencyDescriptor 对象和所找到的结果对象 beanName 封装成一个 ShortcutDependencyDescriptor 对象作为缓存,比如如果当前 Bean是原型 Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象 beanName 去 BeanFactory 中去拿 bean 对象了,不用再次进行查找了。
  5. 利用反射将结果对象赋值给字段。

Set方法注入:

  1. 遍历所有的 AutowiredMethodElement 对象。
  2. 遍历将对应的方法的参数,将每个参数封装成 MethodParameter 对象。
  3. MethodParameter 对象封装为 DependencyDescriptor 对象
  4. 调用 BeanFactoryresolveDependency() 方法,传入 DependencyDescriptor 对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。
  5. DependencyDescriptor 对象和所找到的结果对象 beanName 封装成一个 ShortcutDependencyDescriptor 对象作为缓存,比如如果当前 Bean 是原型 Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象beanName去 BeanFactory 中去拿 bean 对象了,不用再次进行查找了。
  6. 利用反射将找到的所有结果对象传给当前方法,并执行。

# 06 - 循环依赖

大概凡是涉及到依赖注入都绕不开一个问题:循环依赖。这应该是依赖注入中最让人头疼的一个问题,而 Spring 为此提供了一个教科书级别的解决方案 -- 那就是缓存。

在注入对象的时候,如果发现有循环依赖,Spring 会先注入一个半成品的对象,然后再进行后面的填充属性,初始化等操作。具体来说,Spring 是使用三级缓存来解决 Bean 之间的循环依赖问题的:

  1. singletonObjects 一级缓存,缓存 经过了完整生命周期 的 Bean。
  2. earlySingletonObjects 二级缓存, 缓存未经过完整生命周期的 Bean,表示缓存的是早期的 Bean 对象。
  3. SingletonFactories, 三级缓存,缓存的是 lambda 表达式,执行之后会生成会得到一个 Bean,如果当前 Bean 需要 AOP,那么执行 lambda 表达式,得到就是对应的代理对象,如果无需 AOP,则直接得到一个原始对象。
class AService {
@Autowired
private BService beanB
}
class BService {
@Autowired
private ASerice beanA
}
Copied!

以上面的依赖注入为例,AService 和 BService 互相依赖,Spring 的具体解决流程如下:

  1. 实例化 AService 对象 -> 得到一个原始对象 BeanA。
  2. 基于原始对象 BeanA 生成一个 Lambda 表达式,保存在三级缓存 singletonFactories 中,如果 BeanA 需要 AOP 则会提前进行 AOP, 将进行 AOP 之后原始代理对象保存在 singletonFactories 中。
  3. 填充 BService 属性,先去单例池中寻找,没有找到则创建 BService。

    创建 BService

    1. 实例化 BService -> 得到一个原始对象 BeanB。
    2. 基于原始 BeanB 生成一个 Lambda 表达式,保存在三级缓存 singletonFactories
    3. 填充 AService 属性 -> 先去单例池(一级缓存)中找 -> AService Bean正在创建 -> 出现了循环依赖 -> 去二级缓存 earlySingletonObjects 寻找
    4. 二级缓存中没有找到,则二级缓存会从三级缓存 SingletonFactories 获取 AService 创建原始对象的 Lambda 表达式,然后执行 Lambda 表达式并将结果保存到二级缓存 earlySingletonObjects 中。
    5. 填充其他属性
    6. 放入单例池
  4. 填充其他属性
  5. 初始化后(这几进行 AOP 操作,如果存在循环依赖的话,AOP 会提前)
  6. 放入单例池

解决思路流程图如下:

image-3b8ec22d0b3735eaa21d190bfae37768.png

# 07 - Spring 手写实现

在正式开始阅读源码之前,我先根据自己的使用 Spring 的时候情况,把 Spring 的核心功能做了一个最精简版的实现。虽然实现的很粗糙,只有几百行代码,但是写完之后对 Spring 整体主要做的事情大致清楚了。同时自己在实现过程中遇到一些问题,激起了自己阅读 Spring 源码的兴趣, 想看看 Spring 是如何实现这些功能的。

源码地址:https://gitee.com/blackfox/spring-samples/tree/master/spring-custom-impl (opens new window)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK