Spring框架(三) Ben生命周期源码解析

简介: 到此为止 , Spring框架的生命周期完成 , 对于比较具体的细节还需要仔细的琢磨 , 谢谢大家的阅读 , 感觉文章不错的话来个一键三连吧

本篇文章主要说明的是一个Bean是在Spring中如何创建的 , 也就是Bean的生命周期 ,  在传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了。一旦bean不再被使用,则由Java自动进行垃圾回收。


相比之下,Spring管理Bean的生命周期就复杂多了,正确理解Bean 的生命周期非常重要,因为Spring对Bean的管理可扩展性非常强 , 接下来我们慢慢的分析


流程图

我们可以先来看一下Bean生命周期的流程图 , 也就是一个Bean在创建的过程中经历了这些步骤


51da6f0ba796092b0ef70b36cbfc895b_2944e058eb2749418b50131b2f9965b6.png

其实结合前面两篇文章的内容 , 我们也可以知道大概在Spring容器启动的时候 , 大概会做两件事情

1.扫描

2.创建非懒加载单例Bean

62e26d03664432409483618a0b15981b_fe4127ac94984370ba247ff07a665dcc.png


一.扫描

除了Spring启动等扫描可以说是Bean生命周期的第一步 , 扫描哪些类实现需要成为Bean的 , 我只有先扫描到哪些类是需要成为Bean的 , 我才能进行实例化 , 依赖注入 , 初始化等等步骤 , 而在构造方法的this()方法中会初始化读取器和扫描器

946c9038ec3073e233b3fa960589f40f_7bdb024f766b46dfbfaa215191bd17a9.png

那么扫描的逻辑就是在扫描器ClassPathBeanDefinitionScanner中的scan()方法 , scan()方法会调用doScan()真正的逻辑是在doScan()中

68d625081e4d41e3d135835422f32c04_be07043e18494b7ea8251da9d837f9a6.png

我们来看doScan()方法

protected Set<BeanDefinitionHolder> doScan(String... basePackages)

首先doScan()方法需要一个入参 , 也就是一个包路径 , 然后Spring会调用findCandidateComponents()方法然后进行扫描 ,然后解析并得到一个BeanDefinition的集合

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

我们再来看findCandidateComponents()方法

b02debea72c9a2057e40bffa3bad93e2_d3f456ac8baa43b5ab787b96f6989753.png


索引文件

上面的if的作用就是判断你是否定义了索引文件 , 因为这样扫描所有的class从而判断你是否能成为一个Bean的方式相对来说比较慢 , 那么Spring也提供了一种机制, 就是定义索引文件


比如这样定义一个配置文件


7daa475dedc7808c8945edc68c89dc3c_ea89dd5b9eb34cac9b4075ed6368e129.png

然后加入如下内容

com.lyh.service.UserService=org.springframework.stereotype.Component

这样的话就相当于直接定义我当前这个项目里边有哪些Bean , 就相当于直接告诉Spring , 我UserService这个类上有@Component注解 , 那么Spring会怎么做呢?


它会判断你有没有索引 , 也就是上面定义的spring.components文件 , 如果有调用addCandidateComponentsFromIndex()方法来解析 , 我们来看看这个方法

05f898366597d27c585b9f309b981c62_d36bffa655a043268f258ffc50d3041a.png

进来之后 , 我们会发现它首先循环你定义的过滤的内容 ,比如我定义的就是一个过滤component的一个过滤器

String stereotype = extractStereotype(filter);

然后它就会拿到你过滤的这个注解 , 也就是@Conponent

types.addAll(index.getCandidateTypes(basePackage, stereotype));

getCandidateTypes()方法就是获取配置文件的内容 ,然后过滤

a3aade12c258f3f40c3666b16775db95_119a9a24255b418ea1d284f8d313a8af.png

然后它会把符合的类型都放到一个集合 , 然后使用元数据读取器解析, 并使用ScannedGenericBeanDefinition封装成为BeanDefinition , 当然UserService也是要加@Component注解的

其实简单一点就是之前Spring不知道你那个类需要成为Bean , 所以对所有文件进行扫描, 现在你在配置文件直接告诉Spring , 然后Spring取到你配置文件的内容 , 然后解析封装成为BeanDefintion , 然后扫描完成了 , 不明白的话直接debug一波, 就清楚啦

默认情况下我们进的是下面的分析 , 也就是else的分支 , 我们来看scanCandidateComponents()方法:


1.构造一个LinkedHashSet , 用来存放最后扫描的结果

Set<BeanDefinition> candidates = new LinkedHashSet<>();

2.解析包路径

// 获取basePackage下所有的文件资源
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
      resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

CLASSPATH_ALL_URL_PREFIX

df1513fddee8c6ffd01804a62983c08f_b8ca75a1184f46a2b36849a2b09291aa.png


this.resourcePattern

10d98d7b1ac09bec26d9cc38c8d2078b_11d90e1a8e3549ccbd9fe01a235a85be.png


resolveBasePackage()方法的作用是将 . 替换为 /


比如说我们的包名是 : com.lyh.service

那么解析之后的包路径就是 : “classpath*:com/lyh/service/**/*.class”

然后根据解析得到的包路径并利用ResourcePatternResolver资源加载器来加载资源


加载资源的就是包路径下的class文件

得到的就是一个一个的class文件的file对象


3.将class解析为元数据读取器

第2步通过包路径然后加载class文件 , 并存放到一个数组中 , 也就是Resource[] , 然后遍历这个数组 , 如果当前资源可以读 , 那么就会把这个类解析为一个资源读取器

// 参数resource为当前正在遍历的资源
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

这样解析为一个元数据读取器之后 ,这个读取器就可以读取当前这个资源的信息 , 比如一些注解的信息 , 一些类的名称 , 是否是抽象类 , 包括这个类实现的接口等等一系列东西


它的底层使用的是ASM技术


4.过滤

这个过滤也就是过滤的我们通过以下这种方式排除或符合的Bean

@ComponentScan(value = "com.lyh",
  excludeFilters = {@ComponentScan.Filter(
             type = FilterType.ASSIGNABLE_TYPE, 
             classes = UserService.class)}.)
public class AppConfig {
}

也就是这个判断 , 我们点进去isCandidateComponent()方法看一下底层实现

@ComponentScan(value = "com.lyh",
  includeFilters = {@ComponentScan.Filter(
             type = FilterType.ASSIGNABLE_TYPE, 
             classes = UserService.class)})
public class AppConfig {
}
if (isCandidateComponent(metadataReader))

05d1c095fde816b75159a0d7e39eaac9_cf9e17d1e6274a0f8b700130951b41e6.png

他在这里会判断如果是否需要排除或者符合条件的Bean , 只有符合条件的Bean才会真正的成为Bean , 如果有@Component注解 , 那么就会进入到isConditionMatch()方法来判断 , 然后重要的就是这个shouldSkip()方法

c299f24f6f99d4ea074e6eb32fa76365_ac0d446f1936420983a6c42cfdb4e99f.png

它会判断你是否有@Conditional注解 , 如果没有就跳过

da58d9844b9b1dcf72fce2be9c41d600_ffe11776ae934151b8d873c9d05ffbf1.png

如果有@Conditional注解 , 它会解析注解的条件是否匹配 , 如果匹配 , 那么就不跳过, 如果不匹配 , 那么就跳过, 就表示不能成为一个Bean


5.判断是否是顶级类或接口等

if (isCandidateComponent(sbd))

f31e904bcd6212fc2523b7533a1b4205_b908bff7921d436eacda482d73173102.png


metadata.isIndependent() : 判断是否是顶级类或者是内部类

metadata.isConcrete() : 判断是否是接口或者是抽象类

metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))) : 如果是抽象类但是这个抽象类内部有随便一个方法加了@Lookup注解 , 那么它也可以成为一个Bean


@Lookup注解

我们通过一个demo来演示这个注解的作用

新建一个User类 , 并且使他的作用域为原型

@Component
@Scope(scopeName = "prototype")
public class User {
}
@Component
public class UserService {
  @Resource
  private User user;
  public void test1(){
  System.out.println("user : " + user);
  }
}

然后我们这样多调用几次 , 那么每次输出user的值会一样吗?


public class TestSpring {
  public static void main(String[] args){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    // 返回getObject()方法返回的Bean
    UserService userService = (UserService)applicationContext.getBean("userService");
    userService.test1();
    userService.test1();
    userService.test1();
    userService.test1();
  }
} 

测试发现, 它们的值相同 , 为什么呢?

5c877c58fef83e4d16f5632469fb088c_b85c9f515b0440d2898116dab2319cee.png

因为UserService是单例的 , 所以user也只会被赋一次值 , 那么如果想要体现出原型的效果 , 怎么做呢?


把UserService这样改造一下


@Component
public class UserService {
  @Resource
  private User user;
  public void test1(){
  System.out.println("user : " + a());
  }
  @Lookup("user")
  public User a(){
  return null;
  }

定义一个方法 , 然后加上@Lookup注解

12f918bdf08272dff161981512a85885_5e3992f72f9e4331823848913502af60.png

这个时候就体现除了原型的效果, 关于@Lookup注解的源码 , 我会在后续的文章来解释 , 此处只明白它的用法就行


6.添加扫描过滤结果到集合

最开始不是定义了一个BeanDefinition的集合吗, 如果第五步的判断也通过了 , 难么就添加到这个集合 , 并返回


到此为止 , 也扫描到了好多类 , 也生成了Beanfinition ,而现在的Beanfinition只有一个BeanClass属性 ,其他的属性还没有解析 , 然后我们在回到doScan()方法来看 , 刚刚我们说的是这个方法

dcd7c8c8637840d13fb35ad9f5214346_1eaacc3c32a440619f330259f4db9789.png

我们接着往下看


7.解析Scope属性

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());

然后我们进入到这个实现类

fbd07ac6b4af4a98da2d4683d65f75d8_05ddfdf19fd743639e65325d27bfb3ef.png

我们可以看到 , 它就会解析Scope注解 , 然后放到Meta中,

d8434260b458b544f1cf352867503683_ccb6ab93cd3847e88aca019945f6c566.png

然后设置到BeanDefinition中


8.生成Bean的名字

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

我们进入到generateBeanName()方法 , 点进入这个类

19dd52156c35a1cf742020ce8051437c_5dccdb3c82804993a228f2118be6f92e.png

它会获取你注解上指定的名称 , 就比如这样定义@Component(“user”)

3171bab2f17601bd5b80a8518650020b_6e2456afde7a4fd3a0cc4f18222223cd.png

接着看isStereotypeWithNameValue()方法

732e89771406ccaf9ad761a2e930b77c_5f9a5eca1d864eee9d0ccdb7aefa857d.png

它就会判断你是否定义了这些注解


COMPONENT_ANNOTATION_CLASSNAME : org.springframework.stereotype.Component

如果有的话 , 它就会取你这个注解的值

Object value = attributes.get("value");

那么我得到的这个value就是Bean的名称 , 如果写了@Component但是没有指定值 , 那么就会返回空 , 因为beanName属性默认是空 , 如果是空 , 那么它就会默认去生成

也就是下面这一段

4ac4c39be54c032e9d01f3dc56c6652c_e269dc6ec76543e7933c8e651bff2ad1.png

然后点进入buildDefaultBeanName()方法, 直到下面的方法 , 它就会把类的首字母小写 , 作为Bean的名称

c8bcccc2923eae87de79f8b3a35c96f8_d080e2893e524319a2e1a6e20cdc913f.png

e862c56429e4794c57c94b005411151d_3ebb7b0821b94574bac15c7f852483d6.png

如果长度大于1 , 并且类的第一个第二字字母都是大写 , 那么就直接返回, 就是比如我们定义了一个名为这样的类 : ABtest , 那么这个类的名称就是ABtest

否则的话就将首字母小写 , 然后返回


9.是否实现了AbstractBeanDefinition接口

if (candidate instanceof AbstractBeanDefinition) {
  postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}

如果实现了AbstractBeanDefinition接口, 那么它会给当前BeanDefinition赋一些默认值


f02c24fff4f7ae028b40b8d9e717f5db_d2f1155204ba4efc94f1c135027a14a9.png

eeec8b6c4a32655fcc9cd2121f284bf3_eea22cc16bec4e1d963471c024797b84.png

10.是否实现了AnnotatedBeanDefinition接口

if (candidate instanceof AnnotatedBeanDefinition) {
  // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
  AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

如果实现了AnnotatedBeanDefinition , 那么它就会解析@Lazy、@Primary、@DependsOn、@Role、@Description这些注解 ,我们来看看processCommonDefinitionAnnotations()方法


相关注解的作用我都会放在后面来说明


11.检查Spring容器中是否存在该Bean

if (checkCandidate(beanName, candidate)) {
  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
  definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  beanDefinitions.add(definitionHolder);
        // 注册
  registerBeanDefinition(definitionHolder, this.registry);
   }


那么注册会注册到哪呢?

我们点进来看一下registerBeanDefinition()方法 , 进入到下面这个方法

4c9118a32ebfed83625fe2e850ee093e_22808cf60678493e9b5ffce964a1c0a1.png

c455676eb3370651c85f85c07cc95407_b361b750ad344929a9449fa9ba956a99.png

我们会看到这样一行代码

90227be97fd9acc088729a0932f2f9ac_9928ccef28614474a5016f56ee784cbd.png


也就是说注册到了Map中 , 这个Map之前的文章中有提到过 , 可以把这个Map理解为Spring容器 , 也可以把单例池理解为Spring容器

this.beanDefinitionMap.put(beanName, beanDefinition);

所以它检查的时候就会判断根据beanName去beanDefinitionMap中判断

7212055e1ac4976cb66f4f0a4433ecd8_d6027578d19141a9901d443515faeea6.png

可以看到, 如果存在的话就抛了异常 , 也就是Bean的名称重复了

那isCompatible()方法又是什么意思呢?

我们把AppConfig复制一份 , 不需要改变任何内容

然后我们这样来测试

public class TestSpring {
  public static void main(String[] args){
  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  applicationContext.register(AppConfig.class);
  applicationContext.register(AppConfig1.class);
  applicationContext.refresh();
  }
}

它这里会扫描两次, 因为AppConfig和AppConfig1的内容是相同的, 那么扫描两次 , 按照上面的逻辑 , beanName冲突不是会报错吗 , 这里测试发现是不会报错的 , 那么为什么呢?就是因为isCompatible()方法它会判断这两个Bean的source是不是相同 , 如果相同 , 那么就不会注册第二次了, 所以就没有问题


到此为止 , 扫描的主要逻辑就讲完了 , 上面我们还遗留了一点小问题


二.实例化非懒加载单例Bean

我们直接来看refresh()的finishBeanFactoryInitialization()方法的beanFactory.preInstantiateSingletons();这个方法


1.合并BeanDefinition

在哪里体现是非懒加单例Bean呢?

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit())

它就会判断是不是单例, 是不是懒加载, 如果不是 , 就不会做任何操作

而 !bd.isAbstract() 这个并不是判断是否是抽象类 , 而是判断是否是抽象的BeanDefinition , 那么抽象的BeanDefinition一般是通过xml的方式来定义的 , 比如这样定义

4f87da1e583a07470601590c724a6b58_18386ca13d7e48a3a2d697bddf0335cc.png

也就是说抽象的BeanDefinition是不会创建Bean的


那么抽象的BeanDefinition有什么作用呢?比如我们在xml是这样定义的

0961c8556ceb3892bff701bac6397370_1103b3f7010e4163b8cabc31fb2e7cdd.png

这个时候虽然userService是单例的 , 但是由于它继承了id为member的 BeanDefinition(注意这里是继承BeanDefinition , 并不是继承类) , 所以userService也就成为了原型 , 虽然抽象BeanDefinition不能生成Bean , 但是继承它的BeanDefinition可以成为 Bean , 这就是它的作用 , 而这个userService成为原型的过程被称为合并BeanDefinition , 也就是这行代码

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

它会合并父子BeanDefinition , 如果子类自己没有定义 , 那么就会用父类的 , 如果子类定义了 , 那么就用自己的 , 而合并BeanDefinition 会产生第三个BeanDefinition , 而 并非改之前的BeanDefinition , 因为可能一个父BeanDefinition被好多个类继承 , 如果改了父BeanDefinition , 那么其他子类也就一块跟着改了 , 所以合并不会 修改原来的BeanDefinition , 只会产生新的 , 而新的BeanDefinition就称为RootBeanDefinition


2.创建非懒加载单例Bean

如果通过非懒加载的条件之后 ,它他会判断当前bean是否是一个FactoryBean , 如果不是一个FactoryBean , 那么就会调用getBean()创建非懒加载单例Bean

bca833997f6854ae86b2810e0f689cc7_c107a7556e974c8eb679e2b08dabe57e.png

那么他它是怎么判断是否是一个FactoryBean的呢?

11d1f4a9f6f94098b6ee98314ae772c8_d97b34b240f547e98e2bbc508dba4271.png

首先它会转换名字 , 就是截取&符号

逻辑大概分为三段

1.从单例池获取Bean的实例 , 如果实现了FactoryBean接口接口 , 那么就是一个FactoryBean

2.如果从单例池获取不到 , 就会判断父Bean工厂有没有

3.如果单例池没有 , 那么就根据BeanDefinition的类型去判断(获取BeanDefinition的beanClass属性) , 代码如下

7b731287e94fd2a646787d971d2ffd9c_509940afb82644c98bff6c62efa1a558.png

然后缓存一下这个属性

mbd.isFactoryBean = result;

下一次再来判断的时候就会

Boolean result = mbd.isFactoryBean;

直接获取


如果是一个FactoryBean , 那么就会去创建一个Bean , 这个对象就是实现了FactoryBean的这个Bean

// FACTORY_BEAN_PREFIX  : &
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
1
2

比如上篇文章我们讲的FactoryBean会创建两个对象


// 创建两个对象 , 一个是LyhFactoryBean  , 一个是getObject()方法返回的
public class LyhFactoryBean implements FactoryBean {

比如这里Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);创建的就是LyhFactoryBean ,并把这个创建好的Bean强转为FactoryBean

isEagerInit = (factory instanceof SmartFactoryBean &&
  ((SmartFactoryBean<?>) factory).isEagerInit());

如果实现了SmartFactoryBean , 并且isEagerInit()返回true , 才会调用getBean()去创建getObject()返回的Bean

@Component
public class LyhFactoryBean implements SmartFactoryBean {
  @Override
  public boolean isEagerInit() {
  return true;
  }
  @Override
  public Object getObject() throws Exception {
  return new UserService();
  }
  @Override
  public Class<?> getObjectType() {
  return UserService.class;
  }
}


说白了就是如果实现了SmartFactoryBean并且isEagerInit()返回true , 那么它才会调用getBean()去创建 getObject()对象


3.执行SmartInitializingSingleton

所有非懒加载单例Bean创建完毕之后 , 它会根据Bean名称获取到单例Bean的实例,

Object singletonInstance = getSingleton(beanName);

然后判断是否实现了SmartInitializingSingleton , 如果实现了 , 那么就执行afterSingletonsInstantiated()方法

39c34ae7a28866656a7729e429a4a304_51a3bb304b204f7fbf66e930643e5ca1.png


三.获取或创建Bean

上面说了扫描和解析以及实例化非懒加载单例Bean , 下面我们说一下Bean的获取以及获取不到时的创建


b049f2bdf9400b52cf8b901d13d7a3f8_7584d97dc9a642f099b3ffa605e54bbe.png

首先就是转换BeanName , 然后从单例池获取 ,能获取到就返回 , 获取不到呢? 也就是这段else的逻辑

90a4bfcf41bcb395f2b82128bf76c6ae_537febe82c6d484891841ff5ea81ca9b.png

它判断这个Bean是否有对应的BeanDefinition , 如果没有的话 , 就转换一下BeanName然后去父Bean工厂找

e82572667f1e768ac52552c6daf16a9c_c6ce88408cdc42fd89a24078516379b3.png

紧接它会获取合并之后的BeanDefinition , 然后检查这个BeanDefinition是不是抽象的 , 如果是的话是创建不了Bean的 , 然后就会通过getDependsOn()获取它所依赖的Bean


@DependsOn

39f19498702b30800a48a2652ffe06b5_78e062f9af1c4dddbfe68f8205bf1acd.png

这个注解表示依赖的意思 , 就比如MemberService加上@DependsOn注解之后 , 就表示在创建MemberService之前 , 要先把依赖的Bean创建好 , MemberService的这种写法是集合的形式, 而UserService的写法是单个的形式


拿到锁依赖的Bean之后就会把依赖的Bean注册到Map中

74980bc96c8aebd0ec8fedc7f9f3ae73_ef5df9be70704af2abeaab4c41945444.png

对应的就是这两个Map

da924a96b5ebc6131c274cd0b036a473_baf73b234b0545a78e307f050bd333ee.png

但是有一种情况会抛异常的

d0ada4997946aeb70b4fb8ae4f730fa7_78b5a2bab611495082625d345d2e4e97.png

如果是这样声明的话就会产生循环依赖 , 通过@DependsOn注解产生的循环依赖是无法解决的


我们接着来看的getBean()源码然后紧接着会调用getBean()创建所依赖的Bean , 然后创建锁依赖的Bean之后 , 才去真正的创建这个当前需要创建的Bean


简单回想一下Bean的生命周期 , 扫描, 接下来就要去创建实例化 , 属性注入…等等操作

, 它就会先判断当前需要创建的Bean的作用域是原型还是单例还是其他的作用域 ,

26ebe9b9459d97a4aa264e0aa1ab626f_935721a3dafb4f15ae52c5f17cdcd13a.png

我们先来看一下原型的逻辑

eaf6619abff70063237952782bbc68d7_d5a82a7ce25a4953b543b0ea13b5794f.png


原型的话就是直接调用createBean()方法创建 , 也没有放到单例池等操作

再来看单例Bean

这一块其实是一个lambda表达式 , 然后lambda执行的时候就会调用createBean()去创建Bean

462e6ba7e77a50d14983ac34d2e5e2b1_05c8c7da9a4b4bc3bd045dfcde86d43c.png

我们接着来看getSingleton()方法

0e6e7ae11e2774b5726efd8d5106074d_7de3fbff042b418c801ac8c6653a5b94.png

先从单例池获取 , 如果获取不到 , 那么就会执行刚刚的lanbda表达式 , 就会调用createBean()方法去创建一个对象

aa4e09bbe47aae1d47e5d023cdf2c021_2b1f3faf440b40ee8a1daa5d25c68c8d.png

然后会移除这个正在创建的标志并添加到单例池

原型和单例大概都讲完了 , createBean()的逻辑我们之后再看 , 再来看看第三种情况 , Bean的作用域除去原型和单例的话 , 如果考虑SpringMVC的话 , 还有一些其他的作用域 ,如request , application , session , 也就是说一次请求 , 拿到的Bean肯定是同一个Bean ,那么要怎样保证呢?其实实现原理和创建单例对象时候的逻辑差不多

1d987420308d5413db5de3ca25dba899_897319b1413d459ea7f5f48fef1692d2.png

这里会传一个lambda表达式 , 如果可以getAttribute()获取不为空 , 那么就返回 , 否则就创建

d97bc050b889869dad7fb55c4a664656_d8423a7c979e4ed7af2bdb5c26f06e91.png

在set设置的时候就会有区分

32470cf1c84c3d466ba8086c8c6eda13_4a68ed3e06ba4f70b57fef6883248cac.png

不管Bean的作用域是什么总归还是要去创建的 , 所以接下来我们就看看createBean()方法


四.加载类

再创建Bean的时候肯定是要加载类的 , 所以createBean()方法第一步就是加载类

040fe82f7e8889b8139f601d33754984_d3c981efe4894a2483b85f9c7982b90b.png

f3b93fce0121422110b7dbcb7dc06810_568b18d809b149548b280e6a6c931b55.png

如果加载过的话就直接返回 , 否则的话就加载类 , 就会先去获取类加载器

f117a7f6407c9d77224cf1d2eaa7c74e_a423b5e1c52143818e0f0cfc80e36222.png

a01781597b63964b7a1916a5b69e7701_f1266152dc0f4888adb92de35d5f0e0a.png

63bfbe4c041ba92544739245d7a49c0f_45dee5a9a47c420a9dc2cd72fab3c29b.png

当然 ,我们也可以这样自定义

200f162a3ae5b7a33d37164ca9043f11_d034642670034359a90b6bd419c0ee08.png

然后就会获取类的名称 , 然后如果有Spring表达式 , 那么就解析

50c3305a91a29fce95a25c950eea9f72_3d077e9f3a5a48a9bb4024db6175d800.png

这种使用方法的Spring表达式时很少使用的 , 比如是这样定义的(语法错误 , 为了演示)

f51df22b4be9f5378176ed7e0fc759b0_472ac6a9d91349d19c5f7365df3a06fd.png

然后就会加载类并返回

return dynamicLoader.loadClass(className);

接下来就是这个功能

f8610823bd442a9e3195952229243c4f_1cdecf137a5344fe98d9a543673f32b2.png

这个功能会和@Lookup注解有关系 , 放到后面来说


五.实例化前

加载完类之后紧接着就是实例化之前需要做的一些操作

44e5915b425796dfa58c53b09c5afe02_3f41dcb725964059ad36e04c5a622f89.png

之前的文章我们提到过实现BeanPostProcessors接口可以在Bean初始化前后去对Bean进行一些额外的处理 , 那么在初始化前也提供了这个功能 , 就是InstantiationAwareBeanPostProcessor这个接口

@Component
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 实例化前
    return null;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    // 实例化后
    return null;
  }
}

而初始化前就是做这一步操作的

63bf597153dd634efe15b9b6bd56ebcc_021f373d8cc4411ba7e496cd8042c2c1.png

它会先从缓存判断你是否有实现了InstantiationAwareBeanPostProcessor接口的类 , 如果有 , 那么就执行对应的postProcessBeforeInstantiation()方法

e01a73dd53cca85381a2014590e755be_d8c6b3faed70437e87a4e0140525c833.png

比如我们可以这样写

2ade496c505f84df787b53b926bbfe48_49bf50ff7f9b45a3a3fff992b243e468.png

按理说得到的应该是userService这个Bean , 但是由于我们实现了接口 , 那么它就会在实例化前执行 , 然后返回你自己所定义的结果

a35ca20065e482640eae2182157ff73c_511c62732c3b4d2d82694c9da391be14.png

不等于空就直接返回了


六.推断构造方法

推断构造方法的过程在后面有单独的文章说明


七.实例化

如果在实例化前没有做什么事情 , 那么正常进行实例化

b96d196d9fec1fbc2e5eca47ea551c89_94f5a073740f4de6bfd6486ee4f915ac.png


八.BeanDefinition后置处理器

那么实例化之后就会专门的处理合并之后的BeanDefinition

e2c90baa52ef8041b40154b26288fa97_ff6bef74a403476c9b9d8ef197f12adb.png

也就是这个接口

782811f7352d273e4263028de7eed45a_1e18f68dd08e4232ac5f08aa241fcee6.png

实现这个接口 , 我们就可以在实例化之后设置BeanDefinition的一些属性 , 但是有一部分是设置不了的


九.实例化后

48cedb03c17e70c91c62cc2f24ff7633_5ce20efaa9e942c2b2aa8a9c40c5fffd.png


实例化后在populateBean()这个方法中 , 在属性赋值之前 , 其实和实例化之前差不多 .InstantiationAwareBeanPostProcessor接口实现之后可以重写postProcessBeforeInitialization()和postProcessAfterInitialization()方法, 而实例化前后就是找出实现了InstantiationAwareBeanPostProcessor接口的类分表调用这两个方法


十.属性填充

再往下看

633b9cd98d66b0bbb5e264d902516f93_fb8861f56694453aa2ec7135f7f1a836.png

这个其实为了支持Spring的自带的依赖注入功能 , 但是已经过时了 , 不怎么用 , 但是如果使用的话必须添加一个set方法


public class OrderService {
  private MemberService memberService;
  public void setMemberService(MemberService memberService) {
  this.memberService = memberService;
  }
  public void test(){
  System.out.println(this.memberService);
  }
}

然后在AppConfig添加如下配置

@Bean(autowire = Autowire.BY_TYPE)
  public MemberService memberService(){
  return new MemberService();
  }

这个时候OrderService中的MemberService就是有值的 , 如果配置的是 Autowire.BY_TYPE , 那么它会根据set方法入参的类型去寻找Bean

04d34329821dbf98ad2237e9e42d563c_8fa7ea03e43f48eab8c9d2977e841005.png

如果是 Autowire.BY_NAME , 那么就是截取set方法名 , 去掉set然后首字母小写

750db9f51b9d98b4e92886b83fd6ccdd_43c231186b91419181db2339e8b18b66.png


十一.属性填充后

然后Spring自带的注入完毕之后 , 又会去调用InstantiationAwareBeanPostProcessor的postProcessProperties()方法 , 这个方法可以去处理自己的注解 , 比如AutowiredAnnotationBeanPostProcessor就会去处理@Autowired注解


这里还有个pvs , 这个pvs就是通过自定义的方式来给属性赋值 , 比如这样

ec2c1d03e30292cf50b103fb3ad1ed7e_1f5358f48c48480b946a697e306ea771.png

也就是说在属性填充之前 , 它会去检查你有没有通过这种方式自定义给属性赋值 , 如果赋值了 , 那么@Autowired是不生效的


十二.初始化前

紧接着就是到了初始化这一步 , 我们接着分析源码

26994d05e6ba7d72027f15b961ecb218_bcf1ccbaec204163b65cc29d222b8d0c.png

那么首先会初始化各种Aware回调

f5359fd93ed7372472c8b8bbfc05e13d_84a7af4b5a6a4b98a43464b637489bd4.png

接下来就是初始化前的操作了

e0cb9fb3f2fa2244e02b0f7c448644b6_af26258b6d1e43ebbf503fcd7e622667.png

找出实现了BeanPostProcessor接口的类 , 然后执行postProcessBeforeInitialization()方法

5522f17681afc15a4da78c05083f69f7_f4d33a36443e423baadd694a33795ac8.png


十三.初始化

bb3a57cbf146cc9ae10ce9ae420f9620_667a4153ced5449e95b97595dcdf00b8.png

判断是否实现了InitializingBean接口 , 实现了就调用afterPropertiesSet()方法 , 或者说指定了初始化方法 , 那么就去执行这个方法

5586a8277977b026c8ace918febb7ddf_8ae3d801cb1349e2ab460e74d7bfc270.png


十四.初始化后

21ab2bcc2930c07719ee39bdc7730b5e_8434941d9eb940f094e809c907d6e96f.png

初始化后和初始化前是差不多的 , 只不过是执行的方法不同 , 初始化后执行的是postProcessAfterInitialization

f022db40927014f57e6a333181dba353_576fe5b0207244ba9e5394e43eb3691c.png

初始化完成之后 , 就算真正的创建出了一个Bean对象 , 随后就会有循环依赖相关的处理逻辑 , 循环依赖可以看我之前的文章


十五.Bean销毁

最后就是去判断你有没有定义一些Bean销毁的逻辑

19ebda22ba35d8c4d8bfaf6563a5ff33_11bb0c5dfa534658ae62e601a357e067.png

我们可以通过实现一个接口来定义Bean销毁的逻辑

public class OrderService implements DisposableBean {
  @Override
  public void destroy() throws Exception {
    System.out.println("销毁");
  }
}

而这个销毁逻辑方法被调用是当我们Spring容器关闭的时候被触发

关闭的方式有两种

1.向JVM注册一个关闭的钩子 , 当进程结束 , 容器关闭
applicationContext.registerShutdownHook();
2. 手动调用close()方法关闭
applicationContext.close();

但是这两种方法最后都调用的是doClose()方法


2b116009d602b6acc660d11106b11f89_9aad78ab4af746f1bfffad69befdc102.png

9a88a411f61e2aa0b6128024e6dd7369_8bab76a75e4844189245f7d0553c7e2c.png

也就是这段源代码

  /**
   * Actually performs context closing: publishes a ContextClosedEvent and
   * destroys the singletons in the bean factory of this application context.
   * <p>Called by both {@code close()} and a JVM shutdown hook, if any.
   * @see org.springframework.context.event.ContextClosedEvent
   * @see #destroyBeans()
   * @see #close()
   * @see #registerShutdownHook()
   */
  @SuppressWarnings("deprecation")
  protected void doClose() {
    // Check whether an actual close attempt is necessary...
    // 检查上下文是否属于激活状态
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
      if (logger.isDebugEnabled()) {
        logger.debug("Closing " + this);
      }
      if (!NativeDetector.inNativeImage()) {
        // 移除应用上下文的注册
        LiveBeansView.unregisterApplicationContext(this);
      }
      try {
        // Publish shutdown event.
        // 发布上下文已关闭事件 ContextClosedEvent
        publishEvent(new ContextClosedEvent(this));
      }
      catch (Throwable ex) {
        logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
      }
      // Stop all Lifecycle beans, to avoid delays during individual destruction.
      if (this.lifecycleProcessor != null) {
        try {
          // 调用生命周期管理器的 onClose() 方法,终止对容器中各个bean的生命周期管理
          this.lifecycleProcessor.onClose();
        }
        catch (Throwable ex) {
          logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
        }
      }
      // Destroy all cached singletons in the context's BeanFactory.
      //销毁容器中所有的(单例)bean
      //这里的容器指的是高级容器内嵌的低级容器 beanFactory,bean都是放在内置的beanFactory中的
      destroyBeans();
      // Close the state of this context itself.
      //关闭内置的beanFactory
      closeBeanFactory();
      // Let subclasses do some final clean-up if they wish...
      //预留的扩展点,在关闭beanFactory后做一些额外操作
      onClose();
      // Reset local application listeners to pre-refresh state.
      //重置存储监听器的2个成员变量
      if (this.earlyApplicationListeners != null) {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
      }
      //设置上下文的激活状态为false
      // Switch to inactive.
      this.active.set(false);
    }
  }
  /**
   * Template method for destroying all beans that this context manages.
   * The default implementation destroy all cached singletons in this context,
   * invoking {@code DisposableBean.destroy()} and/or the specified
   * "destroy-method".
   * <p>Can be overridden to add context-specific bean destruction steps
   * right before or right after standard singleton destruction,
   * while the context's BeanFactory is still active.
   * @see #getBeanFactory()
   * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons()
   * 
   * 销毁容器中所有的(单例)bean。
   * 如果bean实现了 DisposableBean 接口,或使用 destroy-method 属性指定了销毁方法,会调用对应的销毁方法
   */
  protected void destroyBeans() {
    getBeanFactory().destroySingletons();
  }
  /**
   * Template method which can be overridden to add context-specific shutdown work.
   * The default implementation is empty.
   * <p>Called at the end of {@link #doClose}'s shutdown procedure, after
   * this context's BeanFactory has been closed. If custom shutdown logic
   * needs to execute while the BeanFactory is still active, override
   * the {@link #destroyBeans()} method instead.
   * 
   * 预留给子类的扩展点,可以在内置的beanFactory关闭后做一些自定义处理
   * 
   */
  protected void onClose() {
    // For subclasses: do nothing by default.
  }

到此为止 , Spring框架的生命周期完成 , 对于比较具体的细节还需要仔细的琢磨 , 谢谢大家的阅读 , 感觉文章不错的话来个一键三连吧

相关文章
|
2天前
|
缓存 安全 PHP
【PHP开发专栏】Symfony框架核心组件解析
【4月更文挑战第30天】本文介绍了Symfony框架,一个模块化且高性能的PHP框架,以其可扩展性和灵活性备受开发者青睐。文章分为三部分,首先概述了Symfony的历史、特点和版本。接着,详细解析了HttpFoundation(处理HTTP请求和响应)、Routing(映射HTTP请求到控制器)、DependencyInjection(管理依赖关系)、EventDispatcher(实现事件驱动编程)以及Security(处理安全和认证)等核心组件。
|
2天前
|
NoSQL 大数据 数据处理
MongoDB聚合框架与复杂查询优化:技术深度解析
【4月更文挑战第30天】本文深入探讨了MongoDB的聚合框架和复杂查询优化技术。聚合框架包含$match、$group、$sort和$project阶段,用于数据处理和分析,提供灵活性和高性能。优化查询涉及创建合适索引、使用聚合框架、简化查询语句、限制返回结果数、避免跨分片查询、只查询所需字段及使用$inc操作符。理解这些技术有助于提升MongoDB在大数据和复杂查询场景下的性能。
|
3天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
3天前
|
JavaScript 前端开发 算法
vue生命周期函数原理解析,vue阻止事件冒泡方法实现
vue生命周期函数原理解析,vue阻止事件冒泡方法实现
|
6天前
|
敏捷开发 开发框架 持续交付
【软件工程】航行敏捷之路:深度解析Scrum框架的精髓
【软件工程】航行敏捷之路:深度解析Scrum框架的精髓
|
6天前
|
Java Shell 测试技术
Spring源码搭建教程
Spring源码搭建教程
11 0
|
7天前
|
canal 缓存 关系型数据库
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
|
7天前
|
SQL 安全 前端开发
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
|
7天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
6月前
|
Java Spring
spring框架之AOP模块(面向切面),附带通知类型---超详细介绍
spring框架之AOP模块(面向切面),附带通知类型---超详细介绍
53 0

推荐镜像

更多