【spring源码解析】bean的一生

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 【spring源码解析】bean的一生

使ssm的的时候。首先就会找到spring ,为什么?

应为我们需要将对象交给srping来统一管理,

我们先来复习一下,spring的两大特性

ioc

我们同过spring来帮助我们创建类,帮助我们类之间的解耦


aop

可以在不改变类的源码情况下,添加一些增强的方法,可以是前置,后置 ,异常等等


我们想要用框架,实现越来越多的功能的时候,往往要填加很多很多的bean组件,在使用的时候就可以直接去使用ioc容器中的类,


后置处理器

创建类也有区分不同


有工厂创建的单例

有bean的创建

这些都需要有对应的后置处理器,接下来我们就来看看spring给我们提供了那些灵活的处理器,只要是注入到容器中的bean在即将初始化的时候,都会执行后置处理器,完成之后才放入spring的池子中。


bean的处理器

BeanPostProcessor : bean的后置处理器作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下


源码中有两个方法:

postProcessBeforeInitialization:实例化,依赖注入完毕,再调用显示初始化之前完成游戏额定制初始化的任务


postProcessAfterInitialization:实例化,依赖注入,初始化后执行

这里我们写个小demo来体验一下


新建一个类,叫myBeanPostProcessor,我们实现这个后置处理器的接口


参数解释 :


参数1 bean :就是我们注入到ioc的对象

参数2 beanName : 就是我们注入到ioc的对象名字

public class MyBeanPostProcessor implements BeanPostProcessor {
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    System.out.println(beanName+"初始化执行前");
    return bean;
  }
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println(beanName+"初始化执行后");
    return bean;
  }
}

简单实例,实行之后,可以看我们注入对象的初始化前和初始化后的输出信息


Bean工厂后置处理器

这个bean工厂处理器,是在所有bean的创造之前对bean的一些修改,我们可以在没有构造对象之前,对bean做什么,


postProcessBeanFactory方法 : 参数有bean工厂的对象,可以查看到一些信息

1.png



他就是返回一个在bena创建之前的创建工厂对象,我们可以查看工厂中要创建的bean信息


BeanDefinitionRegistryPostProcessor(后置bean组件信息处理器)

为了处理我们在放容器到spring的时候的一些配置,比如是否单例,初始化方法,销毁方法,等等信息的后置处理器

2.png



方法


postProcessBeanDefinitionRegistrybean对象的配置信息注册


postProcessBeanFactory之后进到bean的工厂


public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry)
      throws BeansException {
    System.out.println("postProcessBeanDefinitionRegistry:" + beanDefinitionRegistry);
  }
  public void postProcessBeanFactory(
      ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    System.out.println("ConfigurableListableBeanFactory:" + configurableListableBeanFactory);
  }

Bean组件的各个生命周期


bean组件信息处理器:组件信息注册

bean工厂创建,我们自己写的后置工厂处理器是一样的,

bean创建和注入完成

初始化前

初始化完成

初始后

销毁

小demo,使用Bean工厂操作bean的注入

全新配置文件 : beanFactoryProcessor.xml,就放两个没有任何配置的bean对象  

    <bean id="mypostProcessBeanFactory" class="com.hyc.Processor.MypostProcessBeanFactory"/>
    <bean id="demobean" class="com.hyc.springBean.demoBean"></bean>

用beanFactory操作bean对象,实现配置,和属性注入


public class MypostProcessBeanFactory implements BeanFactoryPostProcessor {
  public void postProcessBeanFactory(
      ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    System.out.println("configurableListableBeanFactory" + configurableListableBeanFactory);
    // 获取对象
    AbstractBeanDefinition demobean =
        (AbstractBeanDefinition) configurableListableBeanFactory.getBeanDefinition("demobean");
    // 设置初始化方法
    demobean.setInitMethodName("init1");
    // 注入name值
    MutablePropertyValues propertyValues = demobean.getPropertyValues();
    propertyValues.add("name", "PostProcessorFactory bane name");
    // 设置销毁方法
    demobean.setDestroyMethodName("destroy1");
  }
}

之后输出查看信息

1.png

接下来我们复制一个自定义bean工厂

2.png

配置文件新增一个bean


<bean id="mypostProcessBeanFactory2"class="com.hyc.Processor.MypostProcessBeanFactory2"/>


给两个工厂实现优先级接口,设置优先级,


一号工厂优先级为5


二号工厂优先级为10


public class MypostProcessBeanFactory2 implements BeanFactoryPostProcessor, Ordered {
 //之前工厂的代码省略
  public int getOrder() {
    return 10;
  }

启动测试

1.png

可以看到有级别为5的是线执行的


完美,以上就是用后置处理器来操作bean属性的是实战了


spring事件监听器

可以监听我们spring中发生的时间,我们也可以自定义一个spring的事件监听器

我们学习这个主要是要知道spring在容器中执行的时候都派发了什么事件

这里我们实现ApplicationListener<ApplicationEvent>

这里泛型里的对象是全局的消息,我们也可以写一个自己的自定义的


public class MyApplicationEvent extends ApplicationEvent {
  public MyApplicationEvent(Object source) {
    super(source);
  }
  String username;
  String emial;
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getEmial() {
    return emial;
  }
  public void setEmial(String emial) {
    this.emial = emial;
  }
  public MyApplicationEvent(Object source, String username, String emial) {
    super(source);
    this.username = username;
    this.emial = emial;
  }
}

之后创建新的配置文件 Listener.xml将事件监听器加入到spring容器中


<bean id="listener" class="com.hyc.listener.myApplicationListener"></bean>


修改事件监听器,让我们来看看是在生成bean的时候都派发了什么事件吧

public class myApplicationListener implements ApplicationListener<ApplicationEvent> {
  public void onApplicationEvent(ApplicationEvent event) {
    System.out.println("接受到的事件" + event);
      //这里怕判断是如果有我们自己的定义内容将事件监听器强转成我们的自定监听器,保证自己的自定义事件也可以输出
    if (event instanceof MyApplicationEvent) {
      MyApplicationEvent myApplicationEvent = (MyApplicationEvent) event;
      System.out.println("用户名" + myApplicationEvent.getUsername());
      System.out.println("用户邮箱" + myApplicationEvent.getEmial());
    }
  }
}

之后测试

public class listenerMain {
  public static void main(String[] args) {
    ClassPathXmlApplicationContext ApplicationContext =
        new ClassPathXmlApplicationContext("Listener.xml");
    //创建事件对象
    MyApplicationEvent event = new MyApplicationEvent("事件内容", "james", "123@qq,com");
    //发送事件
    ApplicationContext.publishEvent(event);
    ApplicationContext.close();
    //
  }
}

1.png

可以看到图中spring的加载过程越来越明显清晰了起来


探究源码

我们进入调试查看源码

由于源码非常之多,这里就只记录执行过程中做的一些事情


工厂源码

加载过程

新建对象找到spring对象的位置,找到配置文件的位置

1.png


之后判断refresh,开始刷新


refresh


spring最核心的方法,refresh都做了什么呢?


首先就是准备刷新:


记录毫秒值,是不是关闭,选择启动

日志打印,给子类做一些默认的事情,校验一些属性,有没有子类复写的一些属性,如果有就执行,没有继续往下走

判断,之后将应用的一些初始化事件放到集合里

准备好刷新之后,


准备bean工厂


添加bean的加载器,spring表达式的解析器,属性编辑器

设置一些必要的后置处理器

忽略一些接口

将bean的信息注册,加入资源解析器,注册事件发布,上下文对象

获取一些系统属性判断加载,如:jre的名字,user.home的路径,语言环境等等

之后返回工厂对象


工厂对象创建


判断是否有工厂,没有的话创建

创建一个默认的工厂返回。默认创建的工厂包含了很多的默认信息

之后给bean工厂按满足的条件赋值

解析描述信息类,加载描述信息,如果是xml那么就是解析xml

将解析完的类描述i信息,复制到Bean工厂里

到这里工厂的创建就完成了


后置处理器

注册全部的后置处理器,一般来说。有多少注入的类就有多少后置处理器


执行流程

遍历,有多少后置处理器,创建集合,循环判断所有处理器,是否属于当前接口,之后将所有的后置处理器放在对应的集合里,给处理器id

优先级

内部的

等等

之后,给优先级集合中的处理器排序,有序的注册

之后判断其他的处理器的集合

最后判断没有优先级的集合,最终全部注册到容器中


国际化和事件监听器

判断是否有国际化,如果包含,加入到工厂里,输出日志调用

事件监听器也是,判断是否有,如果保函就从里面拿出来,日志输出,如果不保函就创建一个事件监听器,


注册事件监听器

默认是没有,如果有,拿到全部的默认监听器

设置临时类加载器

判断事件监听器是否是空的,如果不是就全都拿来发出去,


创建bean

创建单例对象,不能是懒加载的

首先先寻找工厂里有没有值解析器,如果没有就创建一个新的值解析器

查看冻结配置,为true就将存放再配置文件的bean组件们,转换成字符串数组

根据名字从集合中拿出来,遍历创建对象,根据名字拿到bean的信息,满足三个条件才往后走


不能是抽象的

必须是单例的

不能是懒加载的


如果不满足,那就是工厂创建

从缓存拿到单例对象,要是没有就去看是否满足判断条件,查看是否多次创建对象,之后查看bean工厂,

标记当前对象,正在创建,之后放到set集合中,表示最后一次创建,

检查当前bean的信息,查看是否需要其他的依赖来创建,判断是不是单例,

用集合存放对象和一个构造工厂

有一个实例resolveBeforeInstantiation的创建,通过后置处理器,来实现创建代理对象,这一步就是有可能创建对象的,当我们要用aop的时候就用这个方法拿到代理对象.

如果没有就创建对象,传入对象信息,再次判断是不是单例的,之后创建单例对象,

之后判断对象,看是不是需要必须指定的构造方法,默认是空参数构造方法,

之后拿到bean实例,判断是否为空,判断接口类型,检查对象,拿到无参数构造方法

创建对象,用反射标记当前的构造方法,用无参构造方法构建实例,到这里bean对象就创建成功了

这里的bean对象是最初始的状态,包装给BeanWapper做一个初始化,到这里空的对象就创建好了。

之后调用后置处理器,回调如果组件里有就调后置处理器,最后到了这里开始对空对象赋值

之后再次拿到单例对象,之后就去拿到赋值,返回创建的对象

通过单例工厂创建出对象,将新建的对象放入单例对象集合中

移除用来检查循环依赖的单例工厂,确认注册对象成功,set的不会存放重复值的,所以就一个

经过大量判断之后返回对象实例


完成刷新,发布事件消息

初始化包含的处理器,如果有就get拿进去,如果没有那就创建

之后刷新后置处理器

发送创建事件信息,告诉spring初始化完成了

之后注册上下文拿到当前的名字。如果不等于空那就加入到applicationContext中,至此完成


循环依赖问题

我们创建两个对象,


UserA的构造方法调用UserB对象


循环依赖,

1.png


这个是调试断点进入到有参构造方法


进入之后,可以看到需要参数要引用到UserB,UserB也是个bean所以spring要去创建userB所以userB就需要参数创建userA导致一直循环创建

当spring发现UserB还在创建userA的时候spring就会抛出异常为什么再次创建的时候就异常了呢?

因为存放获取bean的是set之前创建bean的时候我们有提到,只要创建bean就会放在里,有个判断,set是没有重复值的只要判断没通过,就证明之前有正在创建的对象·,于是抛出异常

通过缓存的要创建的bean来判断是否重复引用bean,解决循环依赖问题

如何解决呢?


我们用构造方法调用引用其他类,创建的时候,一直在重复的创建对象

我们只需要,用属性来声明一个应用对象,需要的时候调用,使用的完就销毁

就不会存在循环依赖的问题了


总结

spring框架的精妙之处,太多值得我们学习,这次的bean的一生只是将思想和大致过程讲解,还有很多需要结合场景的细节,需要我们自己的去探索,不断地深究才可以让自己的技术变得强大


相关文章
|
21天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
21天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
21天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
21天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
7天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
20天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
20天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
25天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
62 6
|
27天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
99 3
|
22天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。

推荐镜像

更多