写在前面的话

相关背景及资源:

曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

工程代码地址 思维导图地址

工程结构图:

大体思路

总体来说,bean definition是什么,我们前面几讲,说了个大概了;目前,我们将聚焦于怎么获取bean definition。

我们这次做个实验,就是将bean definition(一共两个bean,有依赖关系,依赖是手动指定的)定义在json文件内,然后自定义一个applicationcontext,从该文件内读取bean definiton,最后我们测试下是否能work。

注意哈,这里的依赖,依然和前面讲的一样,都是手动指定依赖,类似@Autowired这种,还会放到后面才会讲,开车也要先学手动档嘛,是伐?

建议大家直接拖源码下来看:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-beans-json-extend

定义json文件

json文件内,要表达bean definition,按照我们前面说的,基本就包括几个必要的就行了,比如beanClassName。但我这里还是展示一个完整的,但我也是用fastjson先在之前的工程里生成了一个json,之后再拷贝到了json文件里:

  1. // 这里获取到的bean definition的实际类型是 GenericBeanDefiniton,所以序列化出来的的json,就是一个
  2. // GenericBeanDefiniton集合的json
  3. List<BeanDefinition> beanDefinitionList = factory.getBeanDefinitionList()
  4. JSON.toJSONString(beanDefinitionList)

json文件内容如下:

  1. [
  2. {
  3. "abstract": false,
  4. "autowireCandidate": true,
  5. "autowireMode": 0,
  6. "beanClass": "org.springframework.simple.TestService",
  7. "beanClassName": "org.springframework.simple.TestService",
  8. "constructorArgumentValues": {
  9. "argumentCount": 0,
  10. "empty": true,
  11. "genericArgumentValues": [],
  12. "indexedArgumentValues": {}
  13. },
  14. "dependencyCheck": 0,
  15. "enforceDestroyMethod": true,
  16. "enforceInitMethod": true,
  17. "lazyInit": false,
  18. "lenientConstructorResolution": true,
  19. "methodOverrides": {
  20. "empty": true,
  21. "overrides": []
  22. },
  23. "nonPublicAccessAllowed": true,
  24. "primary": false,
  25. "propertyValues": {
  26. "converted": false,
  27. "empty": true,
  28. "propertyValueList": [],
  29. "propertyValues": []
  30. },
  31. "prototype": false,
  32. "qualifiers": [],
  33. "resolvedAutowireMode": 0,
  34. "role": 0,
  35. "scope": "",
  36. "singleton": true,
  37. "synthetic": false
  38. },
  39. {
  40. "abstract": false,
  41. "autowireCandidate": true,
  42. "autowireMode": 0,
  43. "beanClass": "org.springframework.simple.byconstructor.TestControllerByConstructor",
  44. "beanClassName": "org.springframework.simple.byconstructor.TestControllerByConstructor",
  45. "constructorArgumentValues": {
  46. "argumentCount": 2,
  47. "empty": false,
  48. "genericArgumentValues": [],
  49. "indexedArgumentValues": {
  50. 0: {
  51. "converted": false,
  52. "value": {
  53. "beanName": "testService",
  54. "toParent": false
  55. }
  56. },
  57. 1: {
  58. "converted": false,
  59. "value": "wire by constructor"
  60. }
  61. }
  62. },
  63. "dependencyCheck": 0,
  64. "enforceDestroyMethod": true,
  65. "enforceInitMethod": true,
  66. "lazyInit": false,
  67. "lenientConstructorResolution": true,
  68. "methodOverrides": {
  69. "empty": true,
  70. "overrides": []
  71. },
  72. "nonPublicAccessAllowed": true,
  73. "primary": false,
  74. "propertyValues": {
  75. "converted": false,
  76. "empty": true,
  77. "propertyValueList": [],
  78. "propertyValues": []
  79. },
  80. "prototype": false,
  81. "qualifiers": [],
  82. "resolvedAutowireMode": 0,
  83. "role": 0,
  84. "scope": "",
  85. "singleton": true,
  86. "synthetic": false
  87. }
  88. ]

大家可能看得有点懵,其实换成xml,就是类似下面这样的:

  1. <bean name="testService" class="org.springframework.simple.TestService" />
  2. <bean id="testController" class="org.springframework.simple.TestController">
  3. <constructor-arg ref="testService"/>
  4. </bean>

扩展 applicationContext

  1. package org.springframework.beans.extend.json.applicationcontext;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.extend.json.JsonBeanDefinitionReader;
  4. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
  5. import org.springframework.beans.factory.xml.ResourceEntityResolver;
  6. import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
  7. import org.springframework.context.ApplicationContext;
  8. import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
  9. import java.io.IOException;
  10. public class ClassPathJsonApplicationContext extends AbstractRefreshableConfigApplicationContext {
  11. @Override
  12. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  13. //其实主要内容和xmlapplicationcontext是一样的,主要就是下面这行不一样,new了一个json reader
  14. JsonBeanDefinitionReader beanDefinitionReader = new JsonBeanDefinitionReader(beanFactory);
  15. beanDefinitionReader.setEnvironment(this.getEnvironment());
  16. beanDefinitionReader.setResourceLoader(this);
  17. // 这里通过json bean definiton reader去读取bean definition
  18. loadBeanDefinitions(beanDefinitionReader);
  19. }
  20. /**
  21. *通过json bean definiton reader去读取bean definition
  22. **/
  23. protected void loadBeanDefinitions(JsonBeanDefinitionReader reader) throws BeansException, IOException {
  24. // 这里获取json文件的path,这个location是在new ClassPathJsonApplicationContext时传进来的
  25. String[] configResources = getConfigLocations();
  26. if (configResources != null) {
  27. reader.loadBeanDefinitions(configResources);
  28. }
  29. }
  30. public ClassPathJsonApplicationContext(String configLocation) throws BeansException {
  31. this(new String[] {configLocation}, true, null);
  32. }
  33. /**
  34. * 这里一模一样,不需要任何变化
  35. **/
  36. public ClassPathJsonApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
  37. throws BeansException {
  38. super(parent);
  39. setConfigLocations(configLocations);
  40. if (refresh) {
  41. refresh();
  42. }
  43. }
  44. }

扩展jsonBeanDefinitionReader

  1. package org.springframework.beans.extend.json;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.beans.factory.BeanDefinitionStoreException;
  6. import org.springframework.beans.factory.config.ConstructorArgumentValues;
  7. import org.springframework.beans.factory.config.RuntimeBeanReference;
  8. import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
  9. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  10. import org.springframework.beans.factory.support.BeanNameGenerator;
  11. import org.springframework.beans.factory.support.GenericBeanDefinition;
  12. import org.springframework.context.annotation.AnnotationBeanNameGenerator;
  13. import org.springframework.core.NamedThreadLocal;
  14. import org.springframework.core.io.ClassPathResource;
  15. import org.springframework.core.io.Resource;
  16. import org.springframework.core.io.support.EncodedResource;
  17. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  18. import org.springframework.util.CollectionUtils;
  19. import org.springframework.util.StreamUtils;
  20. import org.xml.sax.InputSource;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.nio.charset.Charset;
  24. import java.util.*;
  25. /**
  26. * 类似
  27. * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}
  28. * 只是本类是去json文件里读取bean definition
  29. *
  30. */
  31. @Slf4j
  32. public class JsonBeanDefinitionReader extends AbstractBeanDefinitionReader {
  33. private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
  34. new NamedThreadLocal<Set<EncodedResource>>("json bean definition resources currently being loaded");
  35. public JsonBeanDefinitionReader(BeanDefinitionRegistry registry) {
  36. super(registry);
  37. }
  38. @Override
  39. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  40. // 以下照抄xmlbeanDefintionReader开始
  41. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  42. if (currentResources == null) {
  43. currentResources = new HashSet<EncodedResource>(4);
  44. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  45. }
  46. EncodedResource encodedResource = new EncodedResource(resource);
  47. if (!currentResources.add(encodedResource)) {
  48. throw new BeanDefinitionStoreException(
  49. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  50. }
  51. //照抄xmlbeanDefintionReader结束
  52. //这里的encodedResource.getResource()就是我们的json文件,这里通过spring core里面的一个工具类读取为InputStream
  53. String json = null;
  54. try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
  55. json = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
  56. } catch (IOException e) {
  57. log.error("{}",e);
  58. return 0;
  59. } finally {
  60. currentResources.remove(encodedResource);
  61. if (currentResources.isEmpty()) {
  62. this.resourcesCurrentlyBeingLoaded.remove();
  63. }
  64. }
  65. //熟悉的fastjson,熟悉的味道
  66. List<GenericBeanDefinition> list = JSON.parseArray(json, GenericBeanDefinition.class);
  67. if (CollectionUtils.isEmpty(list)) {
  68. return 0;
  69. }
  70. /**
  71. * 1:因为GenericBeanDefinition,只有setBeanClassName,所以bean反序列化时,只序列化了这个字 * 段;实际我们知道,beanClass很重要,所以我们只能自己处理一下了
  72. * 2:第二个问题,我们在下面解释
  73. **/
  74. for (GenericBeanDefinition genericBeanDefinition : list) {
  75. /**
  76. * 1、处理beanClass
  77. */
  78. Class<?> clazz = null;
  79. try {
  80. clazz = Thread.currentThread().getContextClassLoader().loadClass(genericBeanDefinition.getBeanClassName());
  81. } catch (ClassNotFoundException e) {
  82. log.error("bean class cant be load for beandefinition: {}",genericBeanDefinition);
  83. throw new RuntimeException();
  84. }
  85. genericBeanDefinition.setBeanClass(clazz);
  86. /**
  87. * 2、处理constructor问题,因为Object value = valueHolder.getValue();
  88. * 是Object类型,但这个实际是一个可变类型,当构造器参数为String类型时,这个Object就是 * String类型的,当构造器参数类型为其他bean的引用时,这个object就是RuntimeBeanReference * 的,
  89. * 因为fastjson把我的object转成jsonobject类型了,所以这里要手动搞成RuntimeBeanReference
  90. */
  91. ConstructorArgumentValues constructorArgumentValues = genericBeanDefinition.getConstructorArgumentValues();
  92. if (constructorArgumentValues.isEmpty()) {
  93. continue;
  94. }
  95. Map<Integer, ConstructorArgumentValues.ValueHolder> map = constructorArgumentValues.getIndexedArgumentValues();
  96. if (CollectionUtils.isEmpty(map)) {
  97. continue;
  98. }
  99. for (ConstructorArgumentValues.ValueHolder valueHolder : map.values()) {
  100. Object value = valueHolder.getValue();
  101. if (value instanceof JSONObject) {
  102. JSONObject jsonObject = (JSONObject) value;
  103. RuntimeBeanReference runtimeBeanReference = jsonObject.toJavaObject(RuntimeBeanReference.class);
  104. valueHolder.setValue(runtimeBeanReference);
  105. }
  106. }
  107. }
  108. //这里new一个BeanNameGenerator,这是自带的
  109. setBeanNameGenerator(new AnnotationBeanNameGenerator());
  110. BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
  111. // 获取BeanDefinitionRegistry,bean factory默认实现了BeanDefinitionRegistry
  112. BeanDefinitionRegistry registry = getRegistry();
  113. //注册bean definition到BeanDefinitionRegistry里面去
  114. for (GenericBeanDefinition genericBeanDefinition : list) {
  115. String beanName = beanNameGenerator.generateBeanName(genericBeanDefinition, registry);
  116. registry.registerBeanDefinition(beanName,genericBeanDefinition);
  117. }
  118. return list.size();
  119. }
  120. }

收工了,测试一下

  1. public class BootStrap {
  2. public static void main(String[] args) {
  3. // new一个我们的自定义json上下文
  4. ClassPathJsonApplicationContext context = new ClassPathJsonApplicationContext("beanDefinition.json");
  5. // getBean试一下
  6. TestControllerByConstructor bean = context.getBean(TestControllerByConstructor.class);
  7. System.out.println(bean);
  8. }
  9. }

可以看到,已经注入进去了。没有什么问题。

总结

今天比较晚,写得也比较急,有问题的话,请大家务必指出,谢谢大家

源码地址:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-beans-json-extend

曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean definition的?的更多相关文章

  1. 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

    写在前面的话&&About me 网上写spring的文章多如牛毛,为什么还要写呢,因为,很简单,那是人家写的:网上都鼓励你不要造轮子,为什么你还要造呢,因为,那不是你造的. 我不是要 ...

  2. 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享 工程代码地址 思维导图地址 工程结构图: 正 ...

  3. 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享 工程代码地址 思维导图地址 工程结构图: 大 ...

  4. 曹工说Spring Boot源码(5)-- 怎么从properties文件读取bean

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  6. 曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中得到了什么(上)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  7. 曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  8. 曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. # 曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

随机推荐

  1. jinjia2

    ansible-playbook --become --become-method=su -K copy.yml - hosts: web remote_user: ansible tasks: - ...

  2. php基础文档

    目录 PHP简介 PHP概述和名词解释 PHP常见数据类型 PHP运算符 PHP流程控制语句 PHP函数 PHP类与对象 PHP会话session与缓存cookie(扩展) 1.PHP简介 PHP,即 ...

  3. C#Windows Forms 计算器--xdd

    一.计算器 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data ...

  4. SpringBoot系列之集成Thymeleaf用法手册

    目录 1.模板引擎 2.Thymeleaf简介 2.1).Thymeleaf定义 2.2).适用模板 3.重要知识点 3.1).th:text和th:utext 3.2).标准表达式 3.3).Thy ...

  5. 基于 HTML5 + WebGL 的地铁 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  6. 关于HashMap容量的初始化,还有这么多学问。

    在<HashMap中傻傻分不清楚的那些概念>文章中,我们介绍了HashMap中和容量相关的几个概念,简单介绍了一下HashMap的扩容机制. 文中我们提到,默认情况下HashMap的容量是 ...

  7. 09-kubernetes StatefulSet

    目录 StatefulSet 简单测试 使用 StatefulSet 创建基础的PV StatefulSet 清单 StatefulSet 有状态应用副本集 无状态的, 更关注的是群体 有状态的, 更 ...

  8. 利用scatter()绘制颜色映射的二次方曲线

    程序如下: import matplotlib.pyplot as plt x_value = list(range(1, 1001)) y_value = [x**2 for x in x_valu ...

  9. 关于layer的基本所有的事件全部失效问题

    只要在页面中,要是存在id="undefined", layer的基本所有的事件全部失效. <input type="radio" id="un ...

  10. redis--linux环境搭建

    1.redis诞生的背景 在这要从08年开始说起,一个意大利的小伙子创建一个访问网站信息的LLOOGG.COM网站,用来记录网站的访问记录,查看最近一万条访问信息,每次访问都会将数据存入mysql当中 ...