bean的一生

你曾读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.mzph.cn/news/645852.shtml

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

相关文章

Linux系统Shell脚本编程之条件语句

一、条件测试 Shell 环境根据命令执行后的返回状态值 " $? " 来判断是否执行成功&#xff0c;当返回值为0时表示成功&#xff0c;否则表示失败或异常&#xff08;非0值&#xff09;。使用专门的测试工具 test 命令&#xff0c;可以对特定条件进行测试&#xff0c;并…

BurpSuite Pro 2023.12.1.2下载与破解-最新版BurpSuite Pro

本文在我的博客地址是&#xff1a;https://h4cker.zip/post/f05ae2e66da503f6383dffe48cdf5bac 上一次BurpSuite的分享还是在2020年 由于CSDN有防盗链&#xff0c;我自己的博客都无法访问这篇博文的图片了 至于为什么再写一次&#xff0c;是因为我看到群里这张图&#xff1a;…

小型洗衣机哪家好?专门洗内衣内裤的小型洗衣机

内衣专用的洗衣机是一种专门设计用于洗涤内衣和贴身衣物的小型洗衣机。相比于普通的大型洗衣机&#xff0c;这样的小型洗衣机在设计和功能上有很大的区别。内衣专用的迷你洗衣机可以有效地保护内衣和贴身衣物的质量和卫生。在传统的洗衣机中&#xff0c;如果经常将内衣裤、袜子…

mask transformer相关论文阅读

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

【复现】奥威亚视屏云平台文件读取漏洞_27

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 奥威亚视屏云平台拥有丰富的应用模块&#xff0c;包括结对帮扶、网络教研、教研共同体、优课汇聚、教学资源、在线巡课、AI课堂分…

eBay在人工智能道路上的成败得失:衡量标准是关键

我是2006年加入eBay的。2009年&#xff0c;这家公司的运营状况非常糟糕&#xff0c;其股价创历史新低&#xff08;远低于近24美元的历史高位&#xff09;&#xff0c;还出现削减各项成本、负增长、市场占有率降低、技术团队缺乏创新能力等情况。 简而言之&#xff0c;eBay公司…

从零开始:Git 上传与使用指南

Git 是一种非常强大的版本控制系统&#xff0c;它可以帮助您在多人协作开发项目中更好地管理代码版本&#xff0c;并确保每个团队成员都能及时地获取最新的代码更改。在使用 Git 进行版本控制之前&#xff0c;您需要先进行一些设置&#xff0c;以确保您的代码能够顺利地与远程仓…

【C++】类与对象(上)

我们好像在没接触过编程语言时就听说 C语言时面向过程&#xff0c;而CPP&#xff0c;java…是面向对象&#xff0c; 当时看不懂也不理解&#xff0c;现在看懂了却还是没有理解 本篇文章我们会一起初步认识C的灵魂&#xff1a;类 目录 面向过程与面向对象的初步认识&#xff1…

量子网络是什么

量子网络是基于量子力学规律对量子信息进行存储、处理和传输的物理装置&#xff0c;是实现量子通讯和大规模量子计算的基础。清华大学研究团队利用同种离子的双类型量子比特编码&#xff0c;在国际上首次实现无串扰的量子网络节点&#xff0c;对未来实现量子通讯和大规模量子计…

程序媛的mac修炼手册-- 如何用Python节省WPS会员费

上篇分享了如何用微博爬虫&#xff0c;咱举例爬了女明星江疏影的微博数据。今天就用这些数据&#xff0c;给大家安利一下怎么用Python实现WPS中部分Excel付费功能。 MacOS系统自带的工具&#xff0c;绝大多数都非常顶&#xff0c;除Numbers外。当然&#xff0c;page比起word来&…

gin中使用swagger生成接口文档

想要使用gin-swagger为你的代码自动生成接口文档&#xff0c;一般需要下面三个步骤&#xff1a; 按照swagger要求给接口代码添加声明式注释&#xff0c;具体参照声明式注释格式。使用swag工具扫描代码自动生成API接口文档数据使用gin-swagger渲染在线接口文档页面 第一步&…

QT ui界面修改后不更新

ui界面修改后不更新 在ui界面重新修改布局后&#xff0c;debug模式下发现ui界面没有更新 尝试1&#xff1a;qmake rebuild发现ui界面都没有更新 尝试2&#xff1a; 删除build-XXXX-Desktop_Qt_5_12_6_MSVC2017_64bit-Debug 重新qmake rebuild 界面没有更新 尝试3&…

新能源、新智造、新技术、新未来2024上海国际氢能产业展览会7月魔都开展!

氢能作为一种来源丰富、绿色低碳、应用广泛的二次能源&#xff0c;是实现可再生能源大规模消纳&#xff0c;电网大规模调峰和跨季节、跨地域储能的重要途径&#xff0c;对构建我国新型电力系统和实现碳达峰碳中和目标具有重要意义。 为落实国家关于发展氢能产业的决策部署&…

某顺cookie逆向

目标网站:aHR0cHM6Ly9xLjEwanFrYS5jb20uY24v 这个网站是对cookie进行反爬虫的&#xff0c;可以看到cookie中有一个加密参数v 二、分析参数 可以使用hook方法&#xff0c;来hook住cookie中v生成的位置&#xff0c;可以直接在控制台中输入hook函数 (function () {use strict;v…

编译原理2.3习题 语法制导分析[C++]

图源&#xff1a;文心一言 编译原理习题整理~&#x1f95d;&#x1f95d; 作为初学者的我&#xff0c;这些习题主要用于自我巩固。由于是自学&#xff0c;答案难免有误&#xff0c;非常欢迎各位小伙伴指正与讨论&#xff01;&#x1f44f;&#x1f4a1; 第1版&#xff1a;自…

提取视频中的某一帧画面,留住视频中的美好瞬间

你是否曾经被视频中的某一帧画面深深吸引&#xff0c;却又惋惜于无法将其永久保存&#xff1f;现在&#xff0c;有了我们【媒体梦工厂】&#xff0c;这一遗憾将成为过去&#xff0c;这个软件可以提取视频中的某一帧保存为图片&#xff0c;为你留住那些稍纵即逝的美好。 所需工…

LVDS 两通道总线驱动器——MS2652D

产品简述 MS2652D 是一款低功耗、高数据传输率的两通道 CMOS 差分 LVDS 信号总线驱动芯片&#xff0c;其支持的数据接收率超过 155.5 Mbps (77.7 MHz) 。 MS2652D 将接 TTL/CMOS 输入信号&#xff0c;转换成低压 (425mV) 的差分输出信 号。芯片驱动器还支持三态输出…

PageHelper学习使用

基于mybatis源码和PageHelper源码进行的测试 版本 mybatis3.5.0&#xff0c;pageHelper6.0.0 测试用例 依赖 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version> &…

怎么实现抖音引导到微信小程序丨数灵通教你

抖音是一款热门的社交媒体应用&#xff0c;许多用户希望能够通过抖音跳转到微信小程序&#xff0c;以实现更多的引流和推广效果。以下是关于抖音跳转到微信小程序的科普信息&#xff1a; 1.优势和用途&#xff1a;通过抖音跳转到微信小程序可以带来多个优势和用途&#xff1a; …

Redis核心技术与实战【学习笔记】 - 1.Redis为什么高性能

作为键值数据库&#xff0c;Redis 的应用非常广泛&#xff0c;如果你是后端工程师&#xff0c;我猜你出去面试&#xff0c;八成都会被问到与它相关的性能问题。比如说&#xff0c;为了保证数据的可靠性&#xff0c;Redis 需要在磁盘上读写 AOF 和 RDB&#xff0c;但在高并发场景…