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 指定扫描路径

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

2.3.2 》 AnnotationApplicationContext.java 扫描、生成classList

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

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

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

1public AnnotationApplicationContext(Class<AnnotationConfig> config) { 2 this.config = config; 3 try { 4 // 扫描 5 ComponentScan componentScan = config.getAnnotation(ComponentScan.class); 6 // com.hz.spring.demo.service 7 String path = componentScan.value(); 8 path = path.replace(".", "/"); 9 // 通过类加载器 获取target class 路径 10 ClassLoader classLoader = AnnotationApplicationContext.class.getClassLoader(); 11 URL url = classLoader.getResource(path); 12 if (url != null) { 13 // 获取classList 14 File file = new File(url.getFile()); 15 List<Class<?>> classList = this.scanAndGetClassList(classLoader, file); 16 // bean定义、建立beanName映射关系、保存beanPostProcessor对象 17 this.compBdMap(classList); 18 // 实例化。创建bean。放入单例池 19 // 核心、核心、核心 20 this.instanceBean(); 21 } 22 } catch (Exception e) { 23 log.error("AnnotationApplicationContext error:", e); 24 } 25}

扫描、加载class

1private List<Class<?>> scanAndGetClassList(ClassLoader classLoader, File file) { 2List<Class<?>> classList = Lists.newArrayList(); 3if (file.isDirectory()) { 4 File[] files = file.listFiles(); 5 if (files != null) { 6 for (File f : files) { 7 try { 8 String absolutePath = f.getAbsolutePath(); 9 // Window target文件路径 10 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\User.class 11 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\UserService.class 12 // absolutePath = absolutePath.substring(absolutePath.indexOf("com\\"), absolutePath.indexOf(".class")); 13 // absolutePath = absolutePath.replace("\\", "."); 14 // Mac target文件路径 15 // /Users/huzhong5/hz/IdeaProjects/yb-claim/psc-invoicing/target/test-classes/com/hz/spring/demo/service/UserService.class 16 absolutePath = absolutePath.substring(absolutePath.indexOf("com/"), absolutePath.indexOf(".class")); 17 absolutePath = absolutePath.replace("/", "."); 18 // absolutePath: com.hz.spring.demo.service.UserService 19 Class<?> clazz = classLoader.loadClass(absolutePath); 20 classList.add(clazz); 21 } catch (Exception e) { 22 log.error("scanAndGetClassList error e:", e); 23 } 24 } 25 } 26 } 27 return classList; 28 }

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

遍历classList

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

保存beanPostProcessor对象

1private void compBdMap(List<Class<?>> classList) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException 2 { 3 for (Class<?> clazz : classList) { 4 // 遍历 @Component("") 类。设置bean属性 5 if (clazz.isAnnotationPresent(Component.class)) { 6 Component clazzAnnotation = clazz.getAnnotation(Component.class); 7 String beanName = clazzAnnotation.value(); 8 BeanDefinition beanDefinition = new BeanDefinition(); 9 beanDefinition.setBeanClass(clazz); 10 if (clazz.isAnnotationPresent(Scope.class)) { 11 Scope scopeAnnotation = clazz.getAnnotation(Scope.class); 12 String scope = scopeAnnotation.value(); 13 beanDefinition.setScope(scope); 14 } else { 15 // 默认单例 16 beanDefinition.setScope(SCOPE_SINGLETON); 17 } 18 // userService:beanDefinition 19 beanDefinitionMap.put(beanName, beanDefinition); 20 // 保存beanPostProcessor对象 21 // 通过反射获取对象 22 // clazz : SQYCBeanPostProcessor 23 if (BeanPostProcessor.class.isAssignableFrom(clazz)) { 24 BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance(); 25 beanPostProcessorList.add(instance); 26 } 27 } 28 } 29 }

遍历beanDefinitionMap,判断beanDefinition作用域

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

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

1private void instanceBean() { 2 Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet(); 3 for (Map.Entry<String, BeanDefinition> entry : entries) { 4 BeanDefinition beanDefinition1 = entry.getValue(); 5 String beanName = entry.getKey(); 6 if (SCOPE_SINGLETON.equals(beanDefinition1.getScope())) { 7 if (!singletonObjects.containsKey(beanName)) { 8 // create 9 Object instance = this.doCreateBean(beanName, beanDefinition1); 10 singletonObjects.put(beanName, instance); 11 } 12 } else { 13 this.doCreateBean(beanName, beanDefinition1); 14 } 15 } 16 }

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

实例化bean

对象填充属性

BeanNameAware 设置beanName

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

InitializingBean 初始化,设置属性

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

1private Object doCreateBean(String beanName, BeanDefinition beanDefinition1) { 2 // com.hz.spring.demo.service.UserService 3 Class<?> beanClass = beanDefinition1.getBeanClass(); 4 try { 5 // 实例化bean 6 Object instance = beanClass.getConstructor().newInstance(); 7 // 填充属性。为bean添加属性。如:userService 添加属性 user 8 // 解析Autowired注解的属性 9 Field[] declaredFields = beanClass.getDeclaredFields(); 10 for (Field declaredField : declaredFields) { 11 if (declaredField.isAnnotationPresent(Autowired.class)) { 12 // user 他也是一个bean。执行从单例池获取就可以 13 // 根据beanName获取对象 14 Object bean = this.getBean(declaredField.getName()); 15 declaredField.setAccessible(true); 16 // 将属性declaredField 赋值给 instance 17 declaredField.set(instance, bean); 18 } 19 } 20 // award.通过beanNameAward实现获取beanName 21 if (instance instanceof BeanNameAware) { 22 ((BeanNameAware) instance).setBeanName(beanName); 23 } 24 // 初始化之前。BeanPostProcessor干预。应用场景:AOP 、属性注入、资源回收 25 for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { 26 instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName); 27 } 28 // 初始化。在属性注入完成后调用。可以对属性进行一些改动 29 if (instance instanceof InitializingBean) { 30 try { 31 ((InitializingBean) instance).afterPropertiesSet(); 32 } catch (Exception e) { 33 log.error("doCreateBean InitializingBean error:", e); 34 } 35 } 36 37 // 初始化之后。BeanPostProcessor干预 38 for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { 39 instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName); 40 } 41 return instance; 42} catch (Exception e) { 43 log.error("doCreateBean error:", e); 44} 45return null; 46}

根据beanName获取bean

1public Object getBean(String beanName) { 2 BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); 3 String scope = beanDefinition.getScope(); 4 if (SCOPE_SINGLETON.equals(scope)) { 5 Object bean = singletonObjects.get(beanName); 6 if (bean == null) { 7 bean = this.doCreateBean(beanName, beanDefinition); 8 } 9 return bean; 10 } else if (SCOPE_PROTOTYPE.equals(scope)) { 11 return this.doCreateBean(beanName, beanDefinition); 12 } 13 return null; 14}

BeanPostProcessor实现类定义

1@Component("sqBeanPostProcessor") 2@Slf4j 3public class SqBeanPostProcessor implements BeanPostProcessor { 4 @Override 5 public Object postProcessBeforeInitialization(Object bean, String beanName) { 6 log.info("SqBeanPostProcessor {} 初始化之前", beanName); 7 return bean; 8 } 9 10 @Override 11 public Object postProcessAfterInitialization(Object bean, String beanName) { 12 log.info("SqBeanPostProcessor {} 初始化之后", beanName); 13 // 可以改变对象。很强大 14 return bean; 15 } 16}

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

2.3.5 》 业务类实现

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

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

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

setBeanName() 通过beanNameAward实现获取beanName

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

1@Component("userService") 2@Scope(value = "singleton") 3@Slf4j 4public class UserService implements BeanNameAware, InitializingBean { 5 @Autowired 6 private User user; 7 /** 8 * 可以通过spring award回调方法实现 9 * beanName应用场景: 10 * spring + dubbo。dubbo暴露服务,单个服务的地址可能是beanName的名称组成 11 */ 12 private String beanName; 13 /** 14 * 所有属性填充后。获得 15 */ 16 private String userName; 17 18 public void test() { 19 log.info("UserService test() user.age:{},beanName:{},userName:{}", user.getAge(), beanName, userName); 20 } 21 22 /** 23 * BeanNameAward 24 * 25 * @param beanName 26 */ 27 @Override 28 public void setBeanName(String beanName) { 29 this.beanName = beanName; 30 log.info("UserService setBeanName() beanName:{}", beanName); 31 } 32 33 /** 34 * InitializingBean 35 * 所有属性填充后获得 36 * 37 * @throws Exception 38 */ 39 @Override 40 public void afterPropertiesSet() throws Exception { 41 this.userName = "w"; 42 this.user.setAge("24"); 43 log.info("UserService afterPropertiesSet() userName:{},age:{}", userName, user.getAge()); 44 } 45}

2.3.6 》 运行结果

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

1@Slf4j 2public class Application { 3 public static void main(String[] args) { 4 AnnotationApplicationContext configWebApplicationContext = new AnnotationApplicationContext(AnnotationConfig.class); 5 UserService userService = (UserService) configWebApplicationContext.getBean("userService"); 6 userService.test(); 7 } 8}

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

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生命周期及扩展点代码模拟, 希望对大家有所帮助。🤝🤝🤝

作者:京东保险 胡忠

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

Spring 源码分析之 bean 实例化原理

本次主要想写springbean的实例化相关的内容。创建springbean实例是springbean生命周期的第一阶段。bean的生命周期主要有如下几个步骤:<fontcolor'red'创建bean的实例</font给实例化出来的bean填充属性初始化bea通过IOC容器使用bean

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  

Spring Bean生命周期

1\.简介在【Spring核心流程梳理】我们介绍了Spring容器的refresh过程,但是我们并没有进入到SpringBean的创建等生命周期等内容。这里,就来梳理一下SpringBean的生命周期。我们还是关注流程,重点关注核心的入口,忽略具体实现的细节。目标也很简单:1.知道SpringBean相关的扩展点