bean的一生

news2024/12/24 6:28:47

你曾读spring源码 “不知所云”、“绞尽脑汁”、“不知所措”嘛🤣🤣🤣

那这篇文章可能会对你有所帮助,小编尝试用简单、易懂的例子来模拟spring经典代码👉Spring Bean生命周期及扩展点,

让你能够****轻松的读懂Spring Bean的生命周期,更加深入的理解Spring。


那好,下面小编将从如下几个步骤来介绍✍️✍️✍️

1》回顾Spring Bean相关知识点

1.1什么是Bean

1.2什么是 Spring Bean 的生命周期

1.3Spring Bean 的生命周期的扩展点

2模拟 Spring Bean 生命周期及扩展点

2.1流程图

2.2代码功能介绍

2.3代码实现

2.3.1指定扫描路径

2.3.2扫描、生成classList

2.3.3bean定义、建立beanName映射关系、保存beanPostProcessor对象

2.3.4实例化bean、对象填充属性、执行award方法、BeanPostProcessor干预、初始化、放入单例池、获取bean

2.3.5业务类实现

2.3.6运行结果

3总结


1 》回顾Spring Bean相关知识点

1. 1什么是Bean

对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。

1.2什么是 Spring Bean 的生命周期

普通的Java对象生命周期:

实例化

该对象不再被使用时通过垃圾回收机制进行回收

Spring Bean 的生命周期:

实例化 Instantiation

属性赋值 Populate

初始化 Initialization

销毁 Destruction

1.3Spring Bean 的生命周期的扩展点

Bean 自身的方法

实例化 -> 属性赋值 -> 初始化 -> 销毁

容器级的方法(BeanPostProcessor 一系列接口)

主要是后处理器方法,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor 接口方法。

这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。

在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

Bean 级生命周期方法

可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean、DisposableBean 等方法,这些方法只对当前 Bean 生效。


如上为Spring Bean知识的回顾👏👏👏,如果忘记了,没关系,让咱们在代码中寻找记忆。下面咱们开始模拟Spring Bean 生命周期。

2 》 模拟 Spring Bean 生命周期及扩展点

在看代码之前,首先,先给大家展示一下流程图、代码功能介绍,方便大家理解。

2.1 》 流程图

2.2 》 代码功能介绍

Application.java 启动类,实例化spring容器

AnnotationConfig.java 配置类,指定扫描路径

AnnotationApplicationContext.java 核心类,bean创建、获取

BeanDefinition.java BeanDefinition定义

SqBeanPostProcessor.java 后置处理器,初始化前后对bean进行干预

User.java 业务类,用户对象,用户信息设置

UserService.java 业务类,用户service,实现BeanNameAware、InitializingBean

spring注解模拟

Autowired.java

Component.java

Lazy.java

Scope.java

ComponentScan.java

spring接口模拟

BeanNameAware.java

BeanPostProcessor.java

InitializingBean.java


现在咱们开始看代码、看代码、看代码✌️✌️✌️

2.3》 代码实现

2.3.1》 AnnotationConfig.java 指定扫描路径

@ComponentScan("com.hz.spring.demo.service")
public class AnnotationConfig { 
 
}

2.3.2 》 AnnotationApplicationContext.java 扫描、生成classList

根据配置类config,获取扫描路径

通过类加载器获取target class 路径

扫描文件路径,加载类文件,放入classList中,为类操作做准备

public AnnotationApplicationContext(Class<AnnotationConfig> config) {  
  this.config = config;   
   try {        
   // 扫描        
   ComponentScan componentScan = config.getAnnotation(ComponentScan.class);       
    // com.hz.spring.demo.service        
    String path = componentScan.value();        
    path = path.replace(".", "/");        
    // 通过类加载器 获取target class 路径        
    ClassLoader classLoader = AnnotationApplicationContext.class.getClassLoader();        
    URL url = classLoader.getResource(path);        
    if (url != null) {            
        // 获取classList            
        File file = new File(url.getFile());           
        List<Class<?>> classList = this.scanAndGetClassList(classLoader, file);            
        // bean定义、建立beanName映射关系、保存beanPostProcessor对象           
         this.compBdMap(classList);            
        // 实例化。创建bean。放入单例池            
        // 核心、核心、核心            
         this.instanceBean();        
         }   
     } catch (Exception e) {        
         log.error("AnnotationApplicationContext error:", e);   
     }
}

扫描、加载class

private List<Class<?>> scanAndGetClassList(ClassLoader classLoader, File file) {    
List<Class<?>> classList = Lists.newArrayList();    
if (file.isDirectory()) {       
     File[] files = file.listFiles();        
     if (files != null) {            
         for (File f : files) {                
             try {                    
                 String absolutePath = f.getAbsolutePath();                    
                 // Window target文件路径                    
                 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\User.class                    
                 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\UserService.class                    
                 // absolutePath = absolutePath.substring(absolutePath.indexOf("com\\"), absolutePath.indexOf(".class"));                    
                 // absolutePath = absolutePath.replace("\\", ".");                    
                 // Mac target文件路径                    
                 // /Users/huzhong5/hz/IdeaProjects/yb-claim/psc-invoicing/target/test-classes/com/hz/spring/demo/service/UserService.class                    
                 absolutePath = absolutePath.substring(absolutePath.indexOf("com/"), absolutePath.indexOf(".class"));                    
                 absolutePath = absolutePath.replace("/", ".");                   
                 // absolutePath: com.hz.spring.demo.service.UserService                    
                 Class<?> clazz = classLoader.loadClass(absolutePath);                    
                 classList.add(clazz);                
               } catch (Exception e) {                    
                   log.error("scanAndGetClassList error e:", e);               
                }           
          }        
     }    
 }    
     return classList;
 }

2.3.3 》 bean定义、建立beanName映射关系、保存beanPostProcessor对象

遍历classList

创建BeanDefinition、BeanDefinition赋值属性、建立beanName与BeanDefinition映射关系

保存beanPostProcessor对象

private void compBdMap(List<Class<?>> classList) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
 {    
 for (Class<?> clazz : classList) {        
 // 遍历 @Component("") 类。设置bean属性        
 if (clazz.isAnnotationPresent(Component.class)) {            
 Component clazzAnnotation = clazz.getAnnotation(Component.class);           
  String beanName = clazzAnnotation.value();           
   BeanDefinition beanDefinition = new BeanDefinition();           
    beanDefinition.setBeanClass(clazz);           
     if (clazz.isAnnotationPresent(Scope.class)) {                
        Scope scopeAnnotation = clazz.getAnnotation(Scope.class);               
        String scope = scopeAnnotation.value();                
        beanDefinition.setScope(scope);           
     } else {                
       // 默认单例               
        beanDefinition.setScope(SCOPE_SINGLETON);           
      }           
       // userService:beanDefinition            
       beanDefinitionMap.put(beanName, beanDefinition);            
       // 保存beanPostProcessor对象            
       // 通过反射获取对象            
       // clazz : SQYCBeanPostProcessor            
       if (BeanPostProcessor.class.isAssignableFrom(clazz)) {                
          BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();               
          beanPostProcessorList.add(instance);           
       }       
    }    
   }
 }

遍历beanDefinitionMap,判断beanDefinition作用域

作用域为单例Bean,放入单例池

作用域为多例Bean,多次创建

private void instanceBean() {   
    Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet();   
    for (Map.Entry<String, BeanDefinition> entry : entries) {       
        BeanDefinition beanDefinition1 = entry.getValue();        
        String beanName = entry.getKey();        
        if (SCOPE_SINGLETON.equals(beanDefinition1.getScope())) {            
           if (!singletonObjects.containsKey(beanName)) {                
            // create                
            Object instance = this.doCreateBean(beanName, beanDefinition1);                
            singletonObjects.put(beanName, instance);            
            }        
        } else {            
           this.doCreateBean(beanName, beanDefinition1);       
        }    
    }
 }

2.3.4 》 实例化bean、对象填充属性、执行award 方法、BeanPostProcessor干预、初始化、放入单例池、获取bean

实例化bean

对象填充属性

BeanNameAware 设置beanName

BeanPostProcessor,初始化前后进行bean干预

InitializingBean 初始化,设置属性

bean创建完成,返回bean,放入单例池

private Object doCreateBean(String beanName, BeanDefinition beanDefinition1) {    
    // com.hz.spring.demo.service.UserService    
    Class<?> beanClass = beanDefinition1.getBeanClass();    
    try {        
    // 实例化bean        
    Object instance = beanClass.getConstructor().newInstance();        
    // 填充属性。为bean添加属性。如:userService 添加属性 user        
    // 解析Autowired注解的属性        
    Field[] declaredFields = beanClass.getDeclaredFields();        
    for (Field declaredField : declaredFields) {            
        if (declaredField.isAnnotationPresent(Autowired.class)) {               
        // user 他也是一个bean。执行从单例池获取就可以                
        // 根据beanName获取对象                
        Object bean = this.getBean(declaredField.getName());                
        declaredField.setAccessible(true);               
        // 将属性declaredField 赋值给 instance               
        declaredField.set(instance, bean);            
        }        
    }        
    // award.通过beanNameAward实现获取beanName        
    if (instance instanceof BeanNameAware) {            
        ((BeanNameAware) instance).setBeanName(beanName);        
    }       
    // 初始化之前。BeanPostProcessor干预。应用场景:AOP 、属性注入、资源回收        
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {            
        instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);        
    }        
    // 初始化。在属性注入完成后调用。可以对属性进行一些改动       
    if (instance instanceof InitializingBean) {            
        try {                
            ((InitializingBean) instance).afterPropertiesSet();            
        } catch (Exception e) {                
            log.error("doCreateBean InitializingBean error:", e);            
        }        
    }        
    
    // 初始化之后。BeanPostProcessor干预      
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {            
        instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);        
    }       
    return instance;    
} catch (Exception e) {        
   log.error("doCreateBean error:", e);    
}    
return null;
}

根据beanName获取bean

public Object getBean(String beanName) {    
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);    
    String scope = beanDefinition.getScope();    
    if (SCOPE_SINGLETON.equals(scope)) {        
        Object bean = singletonObjects.get(beanName);        
        if (bean == null) {            
            bean = this.doCreateBean(beanName, beanDefinition);        
        }        
        return bean;    
    } else if (SCOPE_PROTOTYPE.equals(scope)) {        
        return this.doCreateBean(beanName, beanDefinition);    
    }    
    return null;
}

BeanPostProcessor实现类定义

@Component("sqBeanPostProcessor")
@Slf4j
public class SqBeanPostProcessor implements BeanPostProcessor {    
    @Override    
    public Object postProcessBeforeInitialization(Object bean, String beanName) {        
        log.info("SqBeanPostProcessor {} 初始化之前", beanName);        
        return bean;   
    }    
    
    @Override    
    public Object postProcessAfterInitialization(Object bean, String beanName) {        
        log.info("SqBeanPostProcessor {} 初始化之后", beanName);        
        // 可以改变对象。很强大    
        return bean;   
    }
}

如上,bean创建流程就完成啦✌️✌️✌️

2.3.5 》 业务类实现

下面,咱们看看业务类怎样对Spring Bean 进行扩展,

UserService业务核心类定义,实现BeanNameAware、InitializingBean,对bean进行干预;

test() 方法,获取用户属性、beanName

setBeanName() 通过beanNameAward实现获取beanName

afterPropertiesSet() 在属性注入完成后调用,可以对属性进行一些改动

@Component("userService")
@Scope(value = "singleton")
@Slf4j
public class UserService implements BeanNameAware, InitializingBean {   
     @Autowired    
     private User user;    
     /**     
     * 可以通过spring award回调方法实现     
     * beanName应用场景:     
     * spring + dubbo。dubbo暴露服务,单个服务的地址可能是beanName的名称组成     
     */   
      private String beanName;    
      /**     
      * 所有属性填充后。获得     
      */    
      private String userName;    
      
      public void test() {        
          log.info("UserService test() user.age:{},beanName:{},userName:{}", user.getAge(), beanName, userName);    
      }    
      
      /**  
      * BeanNameAward    
      *     
      * @param beanName     
      */   
      @Override    
      public void setBeanName(String beanName) {        
          this.beanName = beanName;        
          log.info("UserService setBeanName() beanName:{}", beanName);    
      }   
       
      /**    
       * InitializingBean     
       * 所有属性填充后获得     
       *     
       * @throws Exception     
       */    
       @Override    
       public void afterPropertiesSet() throws Exception {        
           this.userName = "w";        
           this.user.setAge("24");        
           log.info("UserService afterPropertiesSet() userName:{},age:{}", userName, user.getAge());    
       }
}

2.3.6 》 运行结果

Application.java 启动类,实例化spring容器,获取userService对象,调用test方法。

@Slf4j 
public class Application { 
    public static void main(String[] args) { 
        AnnotationApplicationContext configWebApplicationContext = new AnnotationApplicationContext(AnnotationConfig.class); 
        UserService userService = (UserService) configWebApplicationContext.getBean("userService"); 
        userService.test(); 
    } 
}

下面我们来运行下,结果如下:

17:41:39.466 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之前 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之后 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之前 17:41:39.472 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService setBeanName() beanName:userService 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之前 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService afterPropertiesSet() userName:w,age:24 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService test() user.age:24,beanName:userService,userName:w


3 》 总结

如上为Spring Bean生命周期及扩展点代码模拟, 希望对大家有所帮助。🤝🤝🤝

作者:京东保险 胡忠

来源:京东云开发者社区 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1409296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Word中插入公式并引用

1、如何插入公式 在word中,键入快捷键 “alt” + “=”,即可快速插入一个公式,并立即编辑。 2、利用表格框住公式 新建一个 1 行 3 列的表格,总宽度为页面宽度,第一个单元格和最后一个单元格都保持在 2.25cm,中间尽可能长。我设置的这个数值比较合理。 记住,要把表格…

mask transformer相关论文阅读

前面讲了mask-transformer对医学图像分割任务是非常适用的。本文就是总结一些近期看过的mask-transformer方面的论文。 因为不知道mask transformer是什么就看了一些论文。后来得出结论&#xff0c;应该就是生成mask的transformer就是mask transformer。 相关论文&#xff1a; …

数据结构OJ题——二叉树前序、中序遍历非递归实现(Java版)

二叉树前序、中序遍历非递归实现 前序非递归遍历实现中序非递归遍历实现 前序非递归遍历实现 题目&#xff1a; 二叉树前序遍历非递归实现 总体思路&#xff1a;用非递归的方式模拟递归遍历。 以下图为例&#xff1a; 图示详解&#xff1a; 代码实现&#xff1a; /*** Defi…

【4.LCD显示】蓝桥杯嵌入式一周拿奖速成系列

系列文章目录 蓝桥杯嵌入式系列文章目录(更多此系列文章可见) 文章目录 系列文章目录LCD显示一、官方例程讲解二、main.c --> LcdProcess总结 LCD显示 因为官方给了我们LCD的例程,所以很easy,我们照着套就行 LcdProcess() 一、官方例程讲解 二、main.c --> LcdProcess…

143基于matlab的2D平面桁架有限元分析

基于matlab的2D平面桁架有限元分析&#xff0c;可以改变材料参数&#xff0c;输出平面结构外形&#xff0c;各桁架应力&#xff0c;位移及作用力。可查看节点力&#xff0c;程序已调通&#xff0c;可直接运行。 143 matlab 平面桁架 有限元分析 桁架应力 (xiaohongshu.com)

ubuntu下docker卸载和重新安装

卸载&#xff1a;步骤一&#xff1a;停止Docker服务 首先&#xff0c;我们需要停止正在运行的Docker服务。打开终端&#xff0c;执行以下命令&#xff1a; sudo systemctl stop docker 步骤二&#xff1a;删除Docker安装包 接下来&#xff0c;我们需要删除已经安装的Docker软件…

《WebKit 技术内幕》学习之六(3): CSS解释器和样式布局

3 WebKit布局 3.1 基础 当WebKit创建RenderObject对象之后&#xff0c;每个对象是不知道自己的位置、大小等信息的&#xff0c;WebKit根据框模型来计算它们的位置、大小等信息的过程称为布局计算&#xff08;或者称为排版&#xff09;。 图描述了这一过程中涉及的主要WebKit…

全球机器人产业:技术创新驱动下的市场与竞争新态势

原创 | 文 BFT机器人 近年来&#xff0c;随着颠覆性技术创新的不断涌现、市场新需求的迅速崛起以及外部冲击的深远影响&#xff0c;机器人产业正经历着前所未有的变革。在技术领域&#xff0c;机器人技术不断突破&#xff0c;智能化、自主化、协同化水平日益提升&#xff1b;在…

防火墙综合拓扑接口配置

目录 1、先给Server1、Server2&#xff0c;PC1、Client1、Client2、PC2配置IP、掩码、 网关。 2、LSW1 3、Cloud1 4、FW2 5、 Web界面配置防火墙 6、测试 1、先给Server1、Server2&#xff0c;PC1、Client1、Client2、PC2配置IP、掩码、 网关。 2、LSW1 [Huawei]int g …

明天见!跨越“白酒+文旅+文创”赛道,密鉴品牌将大幅焕新

执笔 | 洪大大 编辑 | 扬 灵 过去的2023年&#xff0c;外部环境的变化叠加产业周期的调整&#xff0c;使得行业呈现出更强的挤压态势&#xff0c;在此背景下&#xff0c;白酒品牌期望对各方资源进行高效整合与充分联动&#xff0c;以此来应对行业周期调整并适应产业升级步伐…

松散子序列(第十四届蓝桥杯省赛PythonB组)

给定一个仅含小写字母的字符串 s&#xff0c;假设 s 的一个子序列 t 的第 i 个字符对应了原字符串中的第 pi 个字符。 我们定义 s 的一个松散子序列为&#xff1a;对于 i>1 总是有 pi−pi−1≥2。 设一个子序列的价值为其包含的每个字符的价值之和&#xff08;a∼z 分别为…

【江科大】STM32:I2C通信外设(硬件)

在将2C通信外设之前&#xff0c;我们先捋一捋&#xff0c;串口的相关特点来和I2C进行一个对北比。 首先&#xff1a; 1,大部分单片机&#xff0c;设计的PCB板均带有串口通信的引脚&#xff08;也就是通信基本都借助硬件收发器来实现&#xff09; 2.对于串口的异步时序&#xff…

Hudi学习笔记(一)

大数据发展背景 Hudi用于管理分布式文件系统上大型分析数据集存储&#xff0c;支持Spark和Flink整合。它能够是DFS数据集在分钟级时延内支持变更&#xff0c;也支持下游系统对这个数据集的增量处理。 学习目标 什么是数据湖为什么使用数据湖Hudi基本功能如何编译Hudi源码Hud…

4 课程分类查询

4 课程分类查询 4.1 需求分析 下边根据内容管理模块的业务流程&#xff0c;下一步要实现新增课程&#xff0c;在新增课程界面&#xff0c;有三处信息需要选择&#xff0c;如下图&#xff1a; 课程等级、课程类型来源于数据字典表&#xff0c;此部分的信息前端已从系统管理服…

SpringMVC第四天(SSM整合)

SSM整合流程 1.创建工程 2.SSM整合 ①Spring SpringConfig package com.cacb.config;import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import;…

opencv#34 边缘检测(二)

Laplacian(拉普拉斯)算子 前面介绍的Sobel算子和Scharr算子存在的问题: 1.要分别计算两个方向&#xff08;x,y)的边缘&#xff0c;之后将两方向的边缘进行叠加。 2.边缘与方向相关性较大。当我们通过Sobel算子提取x方向检测时&#xff0c;它所能够检测到的边缘都是一个沿着y…

如何在转接的NVME 固态盘上安装WIN 系统并引导启动

问题&#xff1a; 家里的台式机一直挂着一块128G的SSD固态盘&#xff0c;由于家里自己建了NAS存储&#xff0c;所以基本数据都在NAS里&#xff0c;台式机就没有挂机械盘了&#xff0c;但是最近台式机空间被系统侵蚀&#xff0c;显然就不够用了&#xff0c;除了清理系统&#xf…

八数码(BFS + 队列 + 哈希表)

这题虽然比较难&#xff0c;但仍然遵循BFS的思路图片引自我的上一篇文章&#xff1a; 走迷宫&#xff08;BFS 队列&#xff09;-CSDN博客 难点 &#xff08;1&#xff09;如何将一个二维数组表示的状态记录下来&#xff0c;并且需要便于知道某个状态是否访问过 &#xff0…

Biotin-PEG4-TSA,生物素-PEG4-酪胺,用于标记蛋白质、核酸等生物分子

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;Biotin-PEG4-Tyramide&#xff0c;Biotin-PEG4-TSA&#xff0c;生物素-PEG4-酪胺&#xff0c;Biotin PEG4 Tyramide&#xff0c;Biotin PEG4 TSA 一、基本信息 产品简介&#xff1a;Biotin PEG4 Tyramide is compos…

CSS 楼梯弹弹球

<template><view class="loader"></view> </template><script></script><style>body {background-color: #212121;/* 设置背景颜色为 #212121 */}.loader {position: relative;/* 设置定位为相对定位 */width: 120px;/* 设…