1.SpringBoot的源码CommandLineRunner和ApplicationRunner源码分析
2.Spring源码系列-BeanPostProcessor与BeanFactoryPostProcessor
3.23. Spring源码篇之环境变量Environment
4.Spring源码Autowired注入流程
5.6. Spring源码篇之FactoryBean
6.25. Spring源码篇之SpEL表达式
SpringBoot的CommandLineRunner和ApplicationRunner源码分析
深入探究SpringBoot中的ApplicationRunner和CommandLineRunner接口。这两个接口在启动SpringBoot应用时起到关键作用,示例下面将对它们进行源码分析。源码
首先,示例让我们聚焦于ApplicationRunner接口,源码其内部定义了一个名为run的示例盲盒商城抽奖系统源码方法,无需额外参数,源码源码如下所示,示例展示了接口的源码基本框架。
接着,示例审视CommandLineRunner接口,源码同样地,示例它也仅定义了一个run方法,源码同样没有额外参数,示例源码内容在此。源码接口设计简洁,旨在支持特定逻辑的执行。
为了更直观地理解这些接口的运行,让我们通过实际项目进行演示。具体操作是将SpringBoot项目打包为JAR文件并执行。
在项目执行过程中,观察并分析代码,可以揭示更多关于ApplicationRunner和CommandLineRunner接口如何在实际应用中运作的细节。
接下来,以ApplicationRunnerDemo和CommandLineRunnerDemo为例,深入探讨接口的使用。首先,审视ApplicationRunnerDemo类,了解如何定义实现ApplicationRunner接口的实例并注入应用上下文。然后,通过CommandLineRunnerDemo类,进一步探索实现CommandLineRunner接口的实例,关注参数传递的机制以及接口执行的时机。
至此,参数传递、参数解析以及获取参数的过程已经清晰呈现。此外,ApplicationRunner和CommandLineRunnerDemo的执行时机也已明确阐述,为理解SpringBoot启动过程中的关键逻辑提供了深入洞察。
Spring源码系列-BeanPostProcessor与BeanFactoryPostProcessor
在Spring框架中,BeanPostProcessor与BeanFactoryPostProcessor各自承担着不同的职责,它们在IoC容器的工作流程中起着关键作用。
BeanFactoryPostProcessor作用于BeanDefinition阶段,对容器中Bean的定义进行处理。这个过程发生在BeanFactory初始化时,对BeanDefinition进行修改或增强,提供了一种在不修改源代码的情况下定制Bean的机制。相比之下,知了源码BeanPostProcessor则在Bean实例化之后生效,对已经创建的Bean对象进行进一步处理或替换,提供了更晚、更灵活的扩展点。
以制造杯子为例,BeanFactoryPostProcessor相当于在选择材料和形状阶段进行定制,而BeanPostProcessor则在杯子制造完成后,进行诸如加花纹、抛光等深加工。
在Spring框架中,BeanPostProcessor的使用场景较为广泛,尤其在实现AOP(面向切面编程)时,通过使用代理类替换原始Bean,实现如日志记录、事务管理等功能。
此外,容器在启动后,还会进行消息源初始化、广播器初始化及监听器初始化,为Bean实例化做好准备。完成这些准备工作后,容器会调用registerBeanPostProcessors方法注册BeanPostProcessor,对已创建的Bean进行进一步处理。同时,初始化消息源、广播器和监听器,为后续事件处理做好基础。
总结,BeanFactoryPostProcessor与BeanPostProcessor在Spring IoC容器中的作用各有侧重。前者侧重于对BeanDefinition的定制,后者则是在Bean实例化后的进一步加工,两者共同为构建灵活、可扩展的IoC容器提供了强大的支持。
在深入分析Spring框架的源码时,我们发现refresh()方法的实现中包含了对BeanFactoryPostProcessor和BeanPostProcessor的注册与处理。这些处理步骤确保了容器能够在启动时对Bean进行正确的配置和初始化。
文章中通过一个例子展示了如何使用BeanFactoryPostProcessor替换已注册Bean的实现,以及对其源码的分析。通过例子和源码的结合,读者能够更直观地理解这些后置处理器在Spring框架中的应用和工作原理。
. Spring源码篇之环境变量Environment
在开发过程中,我们常将变动数据抽取出来,通过配置动态获取不同环境值,这包括配置文件或数据库等。
Spring中定义了统一的环境变量接口Environment,子类关系从StandardEnvironment、AbstractEnvironment、TubeHot源码ConfigurableEnvironment到Environment,其中StandardEnvironment是默认使用的。
源码解析揭示,StandardEnvironment通过继承AbstractEnvironment实现customizePropertySources,此接口允许添加系统变量,如jvm启动参数与操作系统变量。
默认使用StandardEnvironment,基于Spring的其他框架可设置特定值。
StandardEnvironment源码中,getSystemProperties与getSystemEnvironment已由父类AbstractEnvironment实现。
简化示例:通过重写customizePropertySources扩展配置信息,或直接继承StandardEnvironment获取启动参数。
扩展应用示例:以shura.properties内容为例,自定义环境变量ShuraEnvironment,添加配置文件并设置启动类的环境。
Spring环境变量介绍至此,下一节将探讨占位符的使用。
学习过程中的问题与疑问,欢迎交流,共同进步。
Spring源码Autowired注入流程
在Spring框架中,Autowired注解的注入流程是一个开发者常问的问题。本文将带你深入了解这一过程,基于jdk1.8和spring5.2.8.RELEASE环境。
首先,当Spring应用启动,通过SpringApplication的run方法调用refreshContext,进而执行refresh方法,初始化上下文容器。在这个过程中,非懒加载的bean实例化由finishBeanFactoryInitialization方法负责,特别是其内部的beanFactory.preInstantiateSingletons方法。
在默认非单例bean的getBean方法中,会调用AbstractAutowireCapableBeanFactory的createBean方法,这个方法会处理包括@Autowired在内的各种注解。特别关注AutowiredAnnotationBeanPostProcessor,它在获取元数据后,会进入beanFactory.resolveDependency来处理可能的多个依赖问题。
最后,DefaultListableBeanFactory的doResolveDependency方法通过反射机制,实现了属性注入。尽管这只是整个流程的概述,但深入源码可以帮助我们更好地理解Autowired的底层工作机制。
虽然这只是一个基本的梳理,但希望能为理解Spring的Autowired注入提供一些帮助。写这篇文章我投入了一周的时间,尽管过程艰辛,Meta源码但如果觉得有价值,请给予鼓励,如点赞、收藏或转发。期待您的宝贵意见,让我们共同进步!
6. Spring源码篇之FactoryBean
FactoryBean是Spring提供的一个功能强大的小型工厂,用于灵活创建所需Bean。在框架与Spring整合时,尤其是Mybatis-plus中,通过注解可以自动生成Spring Bean,而FactoryBean的功能正是实现批量动态生成Bean。下面详细介绍FactoryBean的源码解析。
首先,我们来看看如何判断一个对象是否为FactoryBean。在Spring的实例化过程中,如果类实现了FactoryBean接口,则会被识别为FactoryBean。而获取FactoryBean时,通常在Bean名称前加上"&"符号。
接下来,我们深入分析FactoryBean的接口。
FactoryBean接口定义了如何创建Bean,包含两个主要方法:getObject和isInstance。getObject用于返回创建的Bean实例,isInstance用于判断一个对象是否由FactoryBean创建。
SmartFactoryBean是FactoryBean的子接口,它提供了额外的特性,允许决定是否提前实例化对象。
在实际使用中,FactoryBean的实例化过程较为关键。如果不希望立即实例化某个非懒加载单例Bean,则需要确保它未被识别为FactoryBean。例如,UserBean的实例化代码在正常情况下不会打印任何输出,表明并未实例化。而通过将UserBean实现为SmartFactoryBean,并使isEagerInit返回true,就能在控制台中观察到UserBean的实例化过程。
获取FactoryBean创建的Bean有多种方式。通过在Bean名称前加"&",可以获取到由getObject方法生成的Bean。此外,若需要获取FactoryBean本身,则可以使用多个"&"符号,Spring会循环遍历,直至获取到实际的源码+投资Bean。
在Spring实例化完成后,通常会调用getObjectForBeanInstance方法来获取真正的Bean实例。这一过程包括了共享实例(sharedInstance)的引用和Bean名称的处理。最终,通过调用getObject方法,我们能够获取到由FactoryBean生成的实际Bean。
以Mybatis-plus中的MapperFactoryBean为例,说明了如何在实际项目中应用FactoryBean。MapperFactoryBean是Mybatis-plus提供的一个FactoryBean,用于自动注册Mapper接口为Spring Bean。
总结而言,FactoryBean在Spring中扮演着灵活创建和管理Bean的重要角色,尤其在需要动态生成或自定义Bean创建逻辑的场景中。通过理解其源码和使用方法,开发者可以更高效地整合各类框架与Spring,实现更为灵活和高效的系统构建。
. Spring源码篇之SpEL表达式
Spring的SpEL表达式,即Spring Expression Language,是Spring框架中实现复杂功能的关键组件。在Spring中,独立的spring-expression模块用于支持这一功能。本文将提供对SpEL表达式源码的简要分析,以帮助理解其基本用法。 在AbstractBeanFactory中,有一个名为beanExpressionResolver的属性,用于配置默认的表达式解析器。在初始化BeanFactory时,通过AbstractApplicationContext#prepareBeanFactory设置默认值,该值默认为开启状态,可通过配置参数spring.spel.ignore=false来关闭表达式功能。 核心解析组件是BeanExpressionResolver,它提供了evaluate方法,用于解析传入的表达式并返回结果。作为实现类,StandardBeanExpressionResolver具体实现evaluate方法,执行解析任务。 解析SpEL表达式的接口是ExpressionParser,它接收表达式和ParserContext,后者定义了解析规则。关键子类包括SpelExpressionParser、InternalSpelExpressionParser和TemplateAwareExpressionParser。在解析过程中,会调用TemplateAwareExpressionParser#parseExpressions方法,该方法进一步调用InternalSpelExpressionParser#doParseExpression,实现表达式的详细解析。解析流程的关键步骤是tokenizer.process和eatExpression方法,它们负责识别和处理特殊字符以及逻辑运算。 SpEL表达式本质上是一个语法树结构,涉及复杂的运算、对象访问和方法调用。它支持的字符规范包括括号、逻辑运算符(如or、and)、比较运算符(如>、<)、点号(用于访问对象属性)、问号(用于条件判断)、美元符号(用于访问变量)等。 以下是使用SpEL表达式的简单示例:案例一
输出特定值或表达式的结果。案例二
对数据集进行处理,例如筛选、排序或计算。案例三
执行对象方法,如调用实例方法或访问静态方法。案例四
使用SpEL获取Spring容器中的Bean实例,包括使用@和&注解来分别获取普通Bean和FactoryBean。 通过以上分析,我们大致了解了SpEL表达式的功能和基本用法。理解这些关键类及其功能有助于在实际开发中灵活运用SpEL,提高代码的可维护性和可读性。尽管SpEL的实现细节复杂,掌握其核心概念和用法足以应对常见的应用场景。张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!
本文将简要介绍AOP(面向切面编程)的基础知识与使用方法,并深入剖析Spring AOP源码。首先,我们需要理解AOP的基本概念。
1. **基础知识
**1.1 **什么是AOP?
**AOP全称为Aspect Oriented Programming,即面向切面编程。AOP的思想中,周边功能(如性能统计、日志记录、事务管理等)被定义为切面,核心功能与切面功能独立开发,然后将两者“编织”在一起,这就是AOP的核心。
AOP能够将与业务无关、却为业务模块共同调用的逻辑封装,减少系统重复代码,降低模块间的耦合度,有利于系统的可扩展性和可维护性。
1.2 **AOP基础概念
**解释较为官方,以下用“方言”解释:AOP包括五种通知分类。
1.3 **AOP简单示例
**创建`Louzai`类,添加`LouzaiAspect`切面,并在`applicationContext.xml`中配置。程序入口处添加`"睡觉"`方法并添加前置和后置通知。接下来,我们将探讨Spring内部如何实现这一过程。
1.4 **Spring AOP工作流程
**为了便于理解后面的源码,我们将整体介绍源码执行流程。整个Spring AOP源码分为三块,结合示例进行讲解。
第一块是前置处理,创建`Louzai`Bean前,遍历所有切面信息并存储在缓存中。第二块是后置处理,创建`Louzai`Bean时,主要处理两件事。第三块是执行切面,通过“责任链+递归”执行切面。
2. **源码解读
**注意:Spring版本为5.2..RELEASE,否则代码可能不同!这里,我们将从原理部分开始,逐步深入源码。
2.1 **代码入口
**从`getBean()`函数开始,进入创建Bean的逻辑。
2.2 **前置处理
**主要任务是遍历切面信息并存储。
这是重点!请务必注意!获取切面信息流程结束,后续操作都从缓存`advisorsCache`获取。
2.2.1 **判断是否为切面
**执行逻辑为:判断是否包含切面信息。
2.2.2 **获取切面列表
**进入`getAdvice()`,生成切面信息。
2.3 **后置处理
**主要从缓存拿切面,与`Louzai`方法匹配,创建AOP代理对象。
进入`doCreateBean()`,执行后续逻辑。
2.3.1 **获取切面
**首先,查看如何获取`Louzai`的切面列表。
进入`buildAspectJAdvisors()`,方法用于存储切面信息至缓存`advisorsCache`。随后回到`findEligibleAdvisors()`,从缓存获取所有切面信息。
2.3.2 **创建代理对象
**有了`Louzai`的切面列表,开始创建AOP代理对象。
这是重点!请仔细阅读!这里有两种创建AOP代理对象方式,我们选择使用Cglib。
2.4 **切面执行
**通过“责任链+递归”执行切面与方法。
这部分逻辑非常复杂!接下来是“执行切面”最核心的逻辑,简述设计思路。
2.4.1 **第一次递归
**数组第一个对象执行`invoke()`,参数为`CglibMethodInvocation`。
执行完毕后,继续执行`CglibMethodInvocation`的`process()`。
2.4.2 **第二次递归
**数组第二个对象执行`invoke()`。
2.4.3 **第三次递归
**数组第三个对象执行`invoke()`。
执行完毕,退出递归,查看`invokeJoinpoint()`执行逻辑,即执行主方法。回到第三次递归入口,继续执行后续切面。
切面执行逻辑已演示,直接查看执行方法。
流程结束时,依次退出递归。
2.4.4 **设计思路
**这部分代码研究了大半天,因为这里不是纯粹的责任链模式。
纯粹的责任链模式中,对象内部有一个自身的`next`对象,执行当前对象方法后,启动`next`对象执行,直至最后一个`next`对象执行完毕,或中途因条件中断执行,责任链退出。
这里`CglibMethodInvocation`对象内部无`next`对象,通过`interceptorsAndDynamicMethodMatchers`数组控制执行顺序,依次执行数组中的对象,直至最后一个对象执行完毕,责任链退出。
这属于责任链,实现方式不同,后续会详细剖析。下面讨论类之间的关系。
主对象为`CglibMethodInvocation`,继承于`ReflectiveMethodInvocation`,`process()`的核心逻辑在`ReflectiveMethodInvocation`中。
`ReflectiveMethodInvocation`的`process()`控制整个责任链的执行。
`ReflectiveMethodInvocation`的`process()`方法中,包含一个长度为3的数组`interceptorsAndDynamicMethodMatchers`,存储了3个对象,分别为`ExposeInvocationInterceptor`、`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`。
注意!这3个对象都继承了`MethodInterceptor`接口。
每次`invoke()`调用时,都会执行`CglibMethodInvocation`的`process()`。
是否有些困惑?别着急,我将再次帮你梳理。
对象与方法的关系:
可能有同学疑惑,`invoke()`的参数为`MethodInvocation`,没错!但`CglibMethodInvocation`也继承了`MethodInvocation`,可自行查看。
执行逻辑:
设计巧妙之处在于,纯粹的责任链模式中,`next`对象需要保证类型一致。但这里3个对象内部没有`next`成员,不能直接使用责任链模式。怎么办呢?就单独设计了`CglibMethodInvocation.process()`,通过无限递归`process()`实现责任链逻辑。
这就是我们为什么要研究源码,学习优秀的设计思路!
3. **总结
**本文首先介绍了AOP的基本概念与原理,通过示例展示了AOP的应用。之后深入剖析了Spring AOP源码,分为三部分。
本文是Spring源码解析的第三篇,感觉是难度较大的一篇。图解代码花费了6个小时,整个过程都沉浸在代码的解析中。
难度不在于抠图,而是“切面执行”的设计思路,即使流程能走通,将设计思想总结并清晰表达给读者,需要极大的耐心与理解能力。
今天的源码解析到此结束,有关Spring源码的学习,大家还想了解哪些内容,欢迎留言给楼仔。
Spring源码从入门到精通---@Import(五)
深入解析如何给容器注册bean
通过ComponentScan+注解如@Controller,@Service,@Compoment,@Repository实现自动扫描bean
@Bean+@Configuration定义导入第三方bean
利用@Import快速批量导入组件,优势在于简化配置
文章重点解析@Import的三种用法:直接导入容器、自定义importSelector实现、自定义ImportBeanDefinitionRegistrar手动注册
1)@import注解直接导入容器,id默认为全类名
2) 自定义importSelector类,返回需要注册的全类名数组
3) 实现ImportBeanDefinitionRegistrar接口,自定义组件注册和id
通过@Import源码,导入的实质是一个数组,允许批量导入多个类
演示通过import将组件如color和red导入容器,并展示容器中组件的打印
提供JUnit测试类,重复利用方法提取getDefinitionNames(),简化测试步骤
新增1)@Import基础使用部分,删除原有代码,便于理解@Import
运行示例,展示导入组件后的容器打印结果,突出import的优势
详细步骤:
2)自定义myImportSelector类实现ImportSelector,返回新增组件路径,结合扫描自定义类
结果展示:blue和yellow组件成功注册容器,验证自定义importSelect功能
3)实现ImportBeanDefinitionRegistrar接口,自定义组件名注册到容器
junit测试不变,运行结果:验证容器中包含red、yellow组件,满足自定义id需求
Spring 源码学习 :initMessageSource
前言
阅读完registerBeanPostProcessors源码后,接下来就是initMessageSource这一步骤,其主要功能是初始化国际化文件。
按照惯例,首先通过官网了解国际化的用法,然后深入研究源码。
官网1..1. Internationalization using MessageSource[1]中提到,MessageSource的主要作用是使用国际化,定制不同的消息。
需要注意的是,MessageSource定义的Bean名称必须为messageSource,如果找不到则会默认注册DelegatingMessageSource作为messageSource的Bean。
1. 创建国际化文件
2. 声明MessageSource
在JavaConfig中声明MessageSource,记得名字一定要叫做messageSource!
3. 测试结果
执行后输出结果如下:
了解了国际化是如何使用的之后,再看看这一步的源码,就知道其作用了!
initMessageSource源码
这块源码唯一值得关注的地方就是,Bean的名称必须要是messageSource。
总结
本文通过官网,了解到什么是国际化,以及国际化的使用,并结合代码和源码,知其然,知其所以然。
当然本文需要注意的地方就是国际化MessageSource的Bean名称要必须为messageSource。