一、IOC分析

1. IOC是什么?

IOC:Inversion of Control控制反转,也称依赖倒置(反转)

问题:如何理解控制反转?

反转:依赖对象的获得被反转了。由自己创建,反转为从IOC容器中获取(和自动注入)

2. IOC容器带来什么好处?

  1)代码更简洁,不需要去new需要使用的对象了。

  2)面向接口编程,使用者与具体类解耦,易扩展、替换实现者。

  3)可以方便进行AOP增强。进行AOP的前提是有IOC

3. IOC容器做什么工作?

  IOC容器的工作:负责创建、管理类实例,向使用者提供实例。

4. IOC容器是否是工厂模式的实例?

  是的。IOC容器负责来创建类实例对象,使用者需要实例就从IOC容器中get。也称IOC容器为bean工厂。

二、IOC设计实现

1. IOC容器的工作

创建、管理bean。它是一个工厂,负责对外提供bean实例。

问:bean是什么?

  bean是组件,就是类对象!

1)IOC容器应该具备什么行为(对外接口)?

  对外提供bean实例,getBean()方法

2) 这个getBean()方法是否需要参数?需要几个参数、什么类型的参数?

  简单工厂模式中,当工厂能创建很多类产品时,如要创建某类产品,需要告诉工厂。

3)这个getBean()方法的返回值应是什么类型?

  各种类型的bean,那就只能是Object了。

经过上面的问题Bean工厂的接口就可以定义出来了!!!

2. Bean工厂接口:

Bean工厂代码:

  1. package com.study.spring.beans;
  2.  
  3. /**
  4. *
  5. * @Description: IOC容器(bean工厂)接口:负责创建bean实例
  6. * @author leeSmall
  7. * @date 2018年11月29日
  8. *
  9. */
  10. public interface BeanFactory {
  11. /**
  12. * 获取bean
  13. *
  14. * @param name bean的名字
  15. *
  16. * @return bean 实例
  17. * @throws Exception
  18. */
  19. Object getBean(String name) throws Exception;
  20. }

Bean工厂怎么知道该如何创建bean?

如何告诉Bean工厂?

  就是一个定义注册,我们可以给它定义一个bean定义注册接口

3. Bean定义注册接口

Bean定义注册接口代码:

  1. package com.study.spring.beans;
  2.  
  3. /**
  4. *
  5. * @Description: bean定义BeanDefinition定义好了就需要告诉IOC容器(bean工厂):
  6. * 通过bean定义注册接口BeanDefinitionRegistry把bean定义BeanDefinition注册到IOC容器(bean工厂)BeanFactory
  7. * @author leeSmall
  8. * @date 2018年11月29日
  9. *
  10. */
  11. public interface BeanDefinitionRegistry {
  12.  
  13. //注册bean定义
  14. void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException;
  15.  
  16. //获取bean定义
  17. BeanDefinition getBeanDefinition(String beanName);
  18.  
  19. //判断是否包含bean定义
  20. boolean containsBeanDefinition(String beanName);
  21.  
  22. }

4. Bean定义

问题1:bean定义的用途是什么?

告诉Bean工厂该如何创建某类bean

问题2:获得类的实例的方式有哪些?

 new 构造方法

  1. Person p = new Person();

工厂方法

静态的

  1. public class PersonFactory {
  2. public static Person getPerson() {
  3. return new Person();
  4. }
  5. }

成员方法

  1. public class PersonFactory {
  2. public Person getPerson() {
  3. return new Person();
  4. }
  5. }

问题3:Bean工厂帮我们创建bean时,它需要获知哪些信息?

1)new 构造方法的方式创建bean时,Bean工厂需要知道要创建的类的类名

2)静态工厂方法的方式创建bean时,Bean工厂需要知道工厂类名、工厂方法名

3)成员工厂方法的方式创建bean时,Bean工厂需要知道工厂类名(工厂bean名)、工厂方法名

  因为需要获取工厂类对象去调用工厂方法名创建bean,所以直接给工厂bean名先创建工厂bean对象

问题4:每次从Bean工厂获取bean实例时,是否都需要创建一个新的?

  否,有的只需要单实例

问题5:Bean定义是给Bean工厂创建bean用的,那么Bean定义接口应向Bean工厂提供哪些方法?

new 构造方法的方式创建bean时,需要告诉bean工厂怎么获取类的名称——获取bean的类名:getBeanClass (): Class

静态工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂方法名:getFactoryMethodName() : String

成员工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂bean名:getFactoryBeanName() : String

是否是单例等方法:getScope() : Sting、isSingleton()、isPrototype()

问题6:类对象交给IOC容器来管理,类对象的生命周期中还可能有什么生命阶段事情要做吗?

比如创建对象后可能需要进行一些初始化

还有一些对象在销毁时可能要进行一些特定的销毁逻辑(如释放资源)

那就在Bean定义中提供让用户可指定初始化、销毁方法。

对Bean工厂就需提供getInitMethodName()、getDestroyMethodName()

5. Bean定义接口

bean定义接口BeanDefinition代码:

  1. package com.study.spring.beans;
  2.  
  3. import org.apache.commons.lang3.StringUtils;
  4.  
  5. /**
  6. *
  7. * @Description: bean定义接口:要IOC容器(bean工厂)创建bean实例,就得告诉IOC容器(bean工厂)需要创建什么样的bean-BeanDefinition
  8. * @author leeSmall
  9. * @date 2018年11月29日
  10. *
  11. */
  12. public interface BeanDefinition {
  13.  
  14. String SCOPE_SINGLETION = "singleton";
  15.  
  16. String SCOPE_PROTOTYPE = "prototype";
  17.  
  18. /**
  19. * 类:new 构造方法的方式创建bean时,需要告诉bean工厂怎么获取类的名称
  20. */
  21. Class<?> getBeanClass();
  22.  
  23. /**
  24. * Scope
  25. */
  26. String getScope();
  27.  
  28. /**
  29. * 是否单例
  30. */
  31. boolean isSingleton();
  32.  
  33. /**
  34. * 是否原型
  35. */
  36. boolean isPrototype();
  37.  
  38. /**
  39. * 工厂bean名:成员工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂bean名
  40. */
  41. String getFactoryBeanName();
  42.  
  43. /**
  44. * 工厂方法名:静态工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂方法名
  45. */
  46. String getFactoryMethodName();
  47.  
  48. /**
  49. * 初始化方法
  50. */
  51. String getInitMethodName();
  52.  
  53. /**
  54. * 销毁方法
  55. */
  56. String getDestroyMethodName();
  57.  
  58. /**
  59. * 校验bean定义的合法性
  60. */
  61. default boolean validate() {
  62. // 没定义class,工厂bean或工厂方法没指定,则不合法。
  63. if (this.getBeanClass() == null) {
  64. if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) {
  65. return false;
  66. }
  67. }
  68.  
  69. // 定义了类,又定义工厂bean,不合法
  70. if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) {
  71. return false;
  72. }
  73.  
  74. return true;
  75. }
  76.  
  77. }

实现bean定义接口BeanDefinition实现一个通用的bean定义GenericBeanDefinition

  1. package com.study.spring.beans;
  2.  
  3. import org.apache.commons.lang3.StringUtils;
  4.  
  5. /**
  6. *
  7. * @Description: bean定义有了就需要实现bean定义接口BeanDefinition实现一个通用的bean
  8. * 定义GenericBeanDefinition,用来承载数据
  9. * @author leeSmall
  10. * @date 2018年11月29日
  11. *
  12. */
  13. public class GenericBeanDefinition implements BeanDefinition {
  14.  
  15. //bean的名称
  16. private Class<?> beanClass;
  17.  
  18. //scope 默认单例
  19. private String scope = BeanDefinition.SCOPE_SINGLETION;
  20.  
  21. //工厂bean名
  22. private String factoryBeanName;
  23.  
  24. //工厂方法名
  25. private String factoryMethodName;
  26.  
  27. //初始化方法
  28. private String initMethodName;
  29.  
  30. //销毁方法
  31. private String destroyMethodName;
  32.  
  33. //设置bean的名称
  34. public void setBeanClass(Class<?> beanClass) {
  35. this.beanClass = beanClass;
  36. }
  37.  
  38. //设置scope
  39. public void setScope(String scope) {
  40. if (StringUtils.isNotBlank(scope)) {
  41. this.scope = scope;
  42. }
  43. }
  44.  
  45. //设置工厂bean名
  46. public void setFactoryBeanName(String factoryBeanName) {
  47. this.factoryBeanName = factoryBeanName;
  48. }
  49.  
  50. //设置工厂方法名
  51. public void setFactoryMethodName(String factoryMethodName) {
  52. this.factoryMethodName = factoryMethodName;
  53. }
  54.  
  55. //设置初始化方法
  56. public void setInitMethodName(String initMethodName) {
  57. this.initMethodName = initMethodName;
  58. }
  59.  
  60. //设置销毁方法
  61. public void setDestroyMethodName(String destroyMethodName) {
  62. this.destroyMethodName = destroyMethodName;
  63. }
  64.  
  65. @Override
  66. public Class<?> getBeanClass() {
  67. return this.beanClass;
  68. }
  69.  
  70. @Override
  71. public String getScope() {
  72. return this.scope;
  73. }
  74.  
  75. @Override
  76. public boolean isSingleton() {
  77. return BeanDefinition.SCOPE_SINGLETION.equals(this.scope);
  78. }
  79.  
  80. @Override
  81. public boolean isPrototype() {
  82. return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);
  83. }
  84.  
  85. @Override
  86. public String getFactoryBeanName() {
  87. return this.factoryBeanName;
  88. }
  89.  
  90. @Override
  91. public String getFactoryMethodName() {
  92. return this.factoryMethodName;
  93. }
  94.  
  95. @Override
  96. public String getInitMethodName() {
  97. return this.initMethodName;
  98. }
  99.  
  100. @Override
  101. public String getDestroyMethodName() {
  102. return this.destroyMethodName;
  103. }
  104.  
  105. @Override
  106. public String toString() {
  107. return "GenericBeanDefinition [beanClass=" + beanClass + ", scope=" + scope + ", factoryBeanName="
  108. + factoryBeanName + ", factoryMethodName=" + factoryMethodName + ", initMethodName=" + initMethodName
  109. + ", destroyMethodName=" + destroyMethodName + "]";
  110. }
  111.  
  112. @Override
  113. public int hashCode() {
  114. final int prime = 31;
  115. int result = 1;
  116. result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode());
  117. result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode());
  118. result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode());
  119. result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode());
  120. result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode());
  121. result = prime * result + ((scope == null) ? 0 : scope.hashCode());
  122. return result;
  123. }
  124.  
  125. @Override
  126. public boolean equals(Object obj) {
  127. if (this == obj)
  128. return true;
  129. if (obj == null)
  130. return false;
  131. if (getClass() != obj.getClass())
  132. return false;
  133. GenericBeanDefinition other = (GenericBeanDefinition) obj;
  134. if (beanClass == null) {
  135. if (other.beanClass != null)
  136. return false;
  137. } else if (!beanClass.equals(other.beanClass))
  138. return false;
  139. if (destroyMethodName == null) {
  140. if (other.destroyMethodName != null)
  141. return false;
  142. } else if (!destroyMethodName.equals(other.destroyMethodName))
  143. return false;
  144. if (factoryBeanName == null) {
  145. if (other.factoryBeanName != null)
  146. return false;
  147. } else if (!factoryBeanName.equals(other.factoryBeanName))
  148. return false;
  149. if (factoryMethodName == null) {
  150. if (other.factoryMethodName != null)
  151. return false;
  152. } else if (!factoryMethodName.equals(other.factoryMethodName))
  153. return false;
  154. if (initMethodName == null) {
  155. if (other.initMethodName != null)
  156. return false;
  157. } else if (!initMethodName.equals(other.initMethodName))
  158. return false;
  159. if (scope == null) {
  160. if (other.scope != null)
  161. return false;
  162. } else if (!scope.equals(other.scope))
  163. return false;
  164. return true;
  165. }
  166.  
  167. }

我们继续看下面的图:

 说明:bean定义BeanDefinition通过bean定义注册接口BeanDefinitionRegistry注册到Bean工厂BeanFactory,Bean工厂BeanFactory负责创建bean

6. BeanFactory实现

实现一个最基础的默认bean工厂:DefaultBeanFactory

说明:

6.1 实现bean定义信息注册接口

问题1:bean定义信息如何存放?

  Map

问题2:bean定义是否可以重名?重名怎么办?

  重名抛异常

6.2 实现bean工厂

问题1:创建的bean用什么存放,方便下次获取?

  Map,因为getBean是通过名字来取的,放在Map中更好

问题2:在getBean方法中要做哪些事?

  创建bean实例,进行初始化

6.3 Bean工厂实现代码:

  1. package com.study.spring.beans;
  2.  
  3. import java.io.Closeable;
  4. import java.io.IOException;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.lang.reflect.Method;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Objects;
  10. import java.util.concurrent.ConcurrentHashMap;
  11.  
  12. import org.apache.commons.lang3.StringUtils;
  13. import org.apache.commons.logging.Log;
  14. import org.apache.commons.logging.LogFactory;
  15.  
  16. /**
  17. *
  18. * @Description: bean定义BeanDefinition有了,bean定义注册BeanDefinitionRegistry也有了,
  19. * 就可以实现一个默认的bean工厂DefaultBeanFactory来创建bean实例了,DefaultBeanFactory除了需要实现BeanFactory外,
  20. * 还需要实现Bean定义注册接口BeanDefinitionRegistry,因为要把bean定义注册到bean工厂里面
  21. * @author leeSmall
  22. * @date 2018年11月29日
  23. *
  24. */
  25. public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {
  26.  
  27. private final Log logger = LogFactory.getLog(getClass());
  28.  
  29. //用Map来存放bean定义信息
  30. private Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256);
  31.  
  32. //用Map来存放创建的bean实例,注意这里只是存放单例bean,多实例每次都要创建新的,不需要存放
  33. private Map<String, Object> beanMap = new ConcurrentHashMap<>(256);
  34.  
  35. //注册bean定义
  36. @Override
  37. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  38. throws BeanDefinitionRegistException {
  39. //判断给入的beanName和beanDefinition不能为空
  40. Objects.requireNonNull(beanName, "注册bean需要给入beanName");
  41. Objects.requireNonNull(beanDefinition, "注册bean需要给入beanDefinition");
  42.  
  43. // 校验给入的bean是否合法
  44. if (!beanDefinition.validate()) {
  45. throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法:" + beanDefinition);
  46. }
  47.  
  48. //如果已存在bean定义就抛异常
  49. if (this.containsBeanDefinition(beanName)) {
  50. throw new BeanDefinitionRegistException(
  51. "名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName));
  52. }
  53.  
  54. //把bean定义放到Map里面
  55. this.beanDefintionMap.put(beanName, beanDefinition);
  56. }
  57.  
  58. //根据bean的名字从Map里面获取bean定义
  59. @Override
  60. public BeanDefinition getBeanDefinition(String beanName) {
  61. return this.beanDefintionMap.get(beanName);
  62. }
  63.  
  64. //根据bean的名字判断Map里面是否包含bean定义
  65. @Override
  66. public boolean containsBeanDefinition(String beanName) {
  67.  
  68. return this.beanDefintionMap.containsKey(beanName);
  69. }
  70.  
  71. //根据bean的名字获取bean实例,里面主要做的工作是创建bean实例和对bean实例进行初始化
  72. @Override
  73. public Object getBean(String name) throws Exception {
  74. return this.doGetBean(name);
  75. }
  76.  
  77. //根据bean的名字获取bean实例,里面主要做的工作是创建bean实例和对bean实例进行初始化
  78. protected Object doGetBean(String beanName) throws Exception {
  79. //判断给入的bean名字不能为空
  80. Objects.requireNonNull(beanName, "beanName不能为空");
  81.  
  82. //先从beanMap里面获取bean实例
  83. Object instance = beanMap.get(beanName);
  84.  
  85. //如果beanMap里面已存在bean实例就直接返回,不需要走后面的流程了
  86. if (instance != null) {
  87. return instance;
  88. }
  89.  
  90. //从beanDefintionMap里面获取bean定义信息
  91. BeanDefinition bd = this.getBeanDefinition(beanName);
  92. //bean定义信息不能为空
  93. Objects.requireNonNull(bd, "beanDefinition不能为空");
  94.  
  95. //获取bean的类型
  96. Class<?> type = bd.getBeanClass();
  97. if (type != null) {
  98. //如果bean的类型不为空,并且工厂方法名为空,说明是使用构造方法的方式来创建bean实例
  99. if (StringUtils.isBlank(bd.getFactoryMethodName())) {
  100. // 构造方法来构造对象
  101. instance = this.createInstanceByConstructor(bd);
  102. }
  103. //如果bean的类型不为空,并且工厂方法名不为空,说明是使用静态工厂方法的方式来创建bean实例
  104. else {
  105. // 静态工厂方法
  106. instance = this.createInstanceByStaticFactoryMethod(bd);
  107. }
  108. }
  109. //如果bean的类型为空,说明是使用工厂bean的方式来创建bean实例
  110. else {
  111. // 工厂bean方式来构造对象
  112. instance = this.createInstanceByFactoryBean(bd);
  113. }
  114.  
  115. // 执行初始化方法
  116. this.doInit(bd, instance);
  117.  
  118. //存放单例的bean到beanMap
  119. if (bd.isSingleton()) {
  120. beanMap.put(beanName, instance);
  121. }
  122.  
  123. return instance;
  124. }
  125.  
  126. // 构造方法来构造对象 反射
  127. private Object createInstanceByConstructor(BeanDefinition bd)
  128. throws InstantiationException, IllegalAccessException {
  129. try {
  130. //拿到bean的类型,然后调用newInstance通过反射来创建bean实例
  131. return bd.getBeanClass().newInstance();
  132. } catch (SecurityException e1) {
  133. logger.error("创建bean的实例异常,beanDefinition:" + bd, e1);
  134. throw e1;
  135. }
  136. }
  137.  
  138. // 静态工厂方法 反射
  139. private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
  140. //拿到bean的类型
  141. Class<?> type = bd.getBeanClass();
  142. //通过静态工厂方法方法的名字getFactoryMethodName反射出bean的方法m创建bean实例
  143. Method m = type.getMethod(bd.getFactoryMethodName(), null);
  144. return m.invoke(type, null);
  145. }
  146.  
  147. // 工厂bean方式来构造对象 反射
  148. private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {
  149.  
  150. //通过bean定义信息中工厂bean的名字获取工厂bean的实例
  151. Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
  152.  
  153. //通过bean定义信息中工厂方法的名字反射出工厂bean的方法m创建bean实例
  154. Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null);
  155. return m.invoke(factoryBean, null);
  156. }
  157.  
  158. //执行初始化方法
  159. private void doInit(BeanDefinition bd, Object instance) throws Exception {
  160. // 获取bean定义中的初始化方法,如果存在初始化方法就通过反射去执行初始化方法
  161. if (StringUtils.isNotBlank(bd.getInitMethodName())) {
  162. Method m = instance.getClass().getMethod(bd.getInitMethodName(), null);
  163. m.invoke(instance, null);
  164. }
  165. }
  166.  
  167. //销毁
  168. @Override
  169. public void close() throws IOException {
  170. // 执行单例实例的销毁方法
  171. for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) {
  172. String beanName = e.getKey();
  173. BeanDefinition bd = e.getValue();
  174.  
  175. //获取bean定义中的销毁方法,如果存在销毁方法就通过反射去执行销毁方法
  176. if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) {
  177. Object instance = this.beanMap.get(beanName);
  178. try {
  179. Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null);
  180. m.invoke(instance, null);
  181. } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
  182. | InvocationTargetException e1) {
  183. logger.error("执行bean[" + beanName + "] " + bd + " 的 销毁方法异常!", e1);
  184. }
  185. }
  186. }
  187. }
  188. }

 6.4 扩展DefaultBeanFactory

对于单例bean,我们可以提前实例化,这样做的好处是不用在需要的时候再取获取了,可以保证线程安全,提高性能

  1. package com.study.spring.beans;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.apache.commons.logging.Log;
  7. import org.apache.commons.logging.LogFactory;
  8.  
  9. /**
  10. *
  11. * @Description: 提前实例化单例bean
  12. * @author leeSmall
  13. * @date 2018年11月27日
  14. *
  15. */
  16. public class PreBuildBeanFactory extends DefaultBeanFactory {
  17.  
  18. private final Log logger = LogFactory.getLog(getClass());
  19.  
  20. private List<String> beanNames = new ArrayList<>();
  21.  
  22. @Override
  23. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  24. throws BeanDefinitionRegistException {
  25. super.registerBeanDefinition(beanName, beanDefinition);
  26. synchronized (beanNames) {
  27. beanNames.add(beanName);
  28. }
  29. }
  30.  
  31. public void preInstantiateSingletons() throws Exception {
  32. synchronized (beanNames) {
  33. for (String name : beanNames) {
  34. BeanDefinition bd = this.getBeanDefinition(name);
  35. if (bd.isSingleton()) {
  36. this.doGetBean(name);
  37. if (logger.isDebugEnabled()) {
  38. logger.debug("preInstantiate: name=" + name + " " + bd);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. }

6.5 测试IOC容器(bean工厂)创建bean实例

需要创建的bean实例ABean

  1. package com.study.spring.samples;
  2.  
  3. /**
  4. *
  5. * @Description: 需要创建的bean实例ABean
  6. * @author leeSmall
  7. * @date 2018年11月29日
  8. *
  9. */
  10. public class ABean {
  11.  
  12. public void doSomthing() {
  13. System.out.println(System.currentTimeMillis() + " " + this);
  14. }
  15.  
  16. public void init() {
  17. System.out.println("ABean.init() 执行了");
  18. }
  19.  
  20. public void destroy() {
  21. System.out.println("ABean.destroy() 执行了");
  22. }
  23. }

创建bean实例ABean的工厂ABeanFactory

  1. package com.study.spring.samples;
  2.  
  3. /**
  4. *
  5. * @Description: 创建bean实例ABean的工厂ABeanFactory
  6. * @author leeSmall
  7. * @date 2018年11月29日
  8. *
  9. */
  10. public class ABeanFactory {
  11.  
  12. //静态工厂方式
  13. public static ABean getABean() {
  14. return new ABean();
  15. }
  16.  
  17. //工厂bean的方式
  18. public ABean getABean2() {
  19. return new ABean();
  20. }
  21. }

测试IOC容器(bean工厂)创建bean实例ABean

  1. package v1;
  2.  
  3. import org.junit.AfterClass;
  4. import org.junit.Test;
  5.  
  6. import com.dn.spring.beans.BeanDefinition;
  7. import com.dn.spring.beans.DefaultBeanFactory;
  8. import com.dn.spring.beans.GenericBeanDefinition;
  9. import com.dn.spring.samples.ABean;
  10. import com.dn.spring.samples.ABeanFactory;
  11.  
  12. /**
  13. *
  14. * @Description: 测试IOC容器(bean工厂)创建bean实例ABean
  15. * @author leeSmall
  16. * @date 2018年11月29日
  17. *
  18. */
  19. public class DefaultBeanFactoryTest {
  20.  
  21. static DefaultBeanFactory bf = new DefaultBeanFactory();
  22.  
  23. //测试构造方法方式创建bean实例
  24. @Test
  25. public void testRegist() throws Exception {
  26.  
  27. //创建bean定义
  28. GenericBeanDefinition bd = new GenericBeanDefinition();
  29.  
  30. //设置bean的类名
  31. bd.setBeanClass(ABean.class);
  32. //设置是否单例
  33. bd.setScope(BeanDefinition.SCOPE_SINGLETION);
  34. // bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
  35.  
  36. //设置bean的初始化方法
  37. bd.setInitMethodName("init");
  38. //设置bean的销毁方法
  39. bd.setDestroyMethodName("destroy");
  40.  
  41. //把bean定义注册到bean工厂DefaultBeanFactory bf
  42. bf.registerBeanDefinition("aBean", bd);
  43.  
  44. }
  45.  
  46. //静态工厂方法的方式创建bean实例
  47. @Test
  48. public void testRegistStaticFactoryMethod() throws Exception {
  49. //创建bean定义
  50. GenericBeanDefinition bd = new GenericBeanDefinition();
  51. //设置工厂bean的名字
  52. bd.setBeanClass(ABeanFactory.class);
  53. //设置工厂方法名
  54. bd.setFactoryMethodName("getABean");
  55. //把bean定义注册到bean工厂DefaultBeanFactory bf
  56. bf.registerBeanDefinition("staticAbean", bd);
  57. }
  58.  
  59. //工厂bean的方式创建bean实例
  60. @Test
  61. public void testRegistFactoryMethod() throws Exception {
  62. //创建工厂bean定义
  63. GenericBeanDefinition bd = new GenericBeanDefinition();
  64. //设置工厂bean的名字
  65. bd.setBeanClass(ABeanFactory.class);
  66. String fbname = "factory";
  67. //把工厂bean注册到bean工厂DefaultBeanFactory bf
  68. bf.registerBeanDefinition(fbname, bd);
  69.  
  70. //创建bean定义
  71. bd = new GenericBeanDefinition();
  72. //设置工厂bean的名字
  73. bd.setFactoryBeanName(fbname);
  74. //设置工厂bean的方法名
  75. bd.setFactoryMethodName("getABean2");
  76. //设置是否是单列
  77. bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
  78.  
  79. //把bean定义注册到bean工厂DefaultBeanFactory bf
  80. bf.registerBeanDefinition("factoryAbean", bd);
  81. }
  82.  
  83. //获取bean实例并调用里面的方法
  84. @AfterClass
  85. public static void testGetBean() throws Exception {
  86. System.out.println("构造方法方式------------");
  87. for (int i = 0; i < 3; i++) {
  88. ABean ab = (ABean) bf.getBean("aBean");
  89. ab.doSomthing();
  90. }
  91.  
  92. System.out.println("静态工厂方法方式------------");
  93. for (int i = 0; i < 3; i++) {
  94. ABean ab = (ABean) bf.getBean("staticAbean");
  95. ab.doSomthing();
  96. }
  97.  
  98. System.out.println("工厂方法方式------------");
  99. for (int i = 0; i < 3; i++) {
  100. ABean ab = (ABean) bf.getBean("factoryAbean");
  101. ab.doSomthing();
  102. }
  103.  
  104. //销毁
  105. bf.close();
  106. }
  107. }

输出结果:

  1. 构造方法方式------------
  2. ABean.init() 执行了
  3. 1543478493599 com.dn.spring.samples.ABean@46fbb2c1
  4. 1543478493599 com.dn.spring.samples.ABean@46fbb2c1
  5. 1543478493599 com.dn.spring.samples.ABean@46fbb2c1
  6. 静态工厂方法方式------------
  7. 1543478493599 com.dn.spring.samples.ABean@5ef04b5
  8. 1543478493599 com.dn.spring.samples.ABean@5ef04b5
  9. 1543478493599 com.dn.spring.samples.ABean@5ef04b5
  10. 工厂方法方式------------
  11. 1543478493599 com.dn.spring.samples.ABean@5f4da5c3
  12. 1543478493600 com.dn.spring.samples.ABean@443b7951
  13. 1543478493600 com.dn.spring.samples.ABean@14514713
  14. ABean.destroy() 执行了

三、DI分析

DI(Dependency Injection)依赖注入分析

问题1:哪些地方会有依赖?

  构造参数依赖

  属性依赖

问题2:依赖注入的本质是什么?

  赋值,给入构造参数值,给属性赋值

问题3:参数值、属性值可能是什么值?

  直接值、bean依赖

举例:

  1. public class Girl{
  2. public Girl(String name,int age,char cup,Boy boyfriend){
  3. }
  4.  
  5. }

name,age,cup都是直接值,boyfriend是bean依赖

问题4:直接值会有哪几种情况?

  基本数据类型、String

  数组、集合

  Properties

  Map

本质:参数值、属性值都是值。bean工厂在进行依赖注入时,就是给入值。

四、DI实现

1. 构造参数依赖

 1.1 构造参数依赖定义分析

  1. public class Girl{
  2. public Girl(String name,int age,char cup,Boy boyfriend){
  3. }
  4.  
  5. }

问题1:我们要创建一个Girl是如何创建的?

  1. Boy leeSmall = new Boy("leeSmall");
  2. Girl girl = new Girl("小青"18'D',leeSmall);

直接把值传入构造函数即可

问题2:我们可不可以这样来定义构造参数依赖?

  第一个参数值是:"小青"

  第二个参数值是:18

  第三个参数值是:'D'

  第四个参数值是:依赖一个Boy Bean

完全可以!

构造参数依赖设计

问题1:参数可以有多个,用什么存储?

  集合:List

问题2:参数有顺序,如何体现顺序?

  按参数顺序放入List

问题3:参数可以值直接值,也可以是bean依赖,如何表示?

  因为可以有多种值,那就只能用Object

  List<Object>  constructorArgumentValues

问题4:如果用Object来表示值,如何区分是Bean依赖?

  为Bean依赖定义一种数据类型BeanReference,bean工厂在构造Bean实例时,遍历判断参数是否是BeanReference,如果是则替换为依赖的bean实例。

问题5:如果直接值是数组、集合等,它们的元素中有的是bean依赖,怎么处理?

  元素值还是用BeanReference,同样bean工厂在使用时需遍历替换。

1.2 BeanReference

BeanReference就是用来说明bean依赖的:依赖哪个Bean

  1. package com.dn.spring.beans;
  2. /**
  3. * 用于依赖注入中描述bean依赖
  4. */
  5. public class BeanReference {
  6. private String beanName;
  7.  
  8. public BeanReference(String beanName) {
  9. super();
  10. this.beanName = beanName;
  11. }
  12.  
  13. /**
  14. * 获得引用的beanName
  15. */
  16. public String getBeanName() {
  17. return this.beanName;
  18. }
  19. }

1.3 在BeanDefinition中增加获得构造参数值的方法

  1. List<?> getConstructorArgumentValues();

在GenericBeanDefinition中增加对应的实现

  1. //构造参数存放属性
  2. private List<?> constructorArgumentValues;
  3.  
  4. //获得构造参数值
  5. public List<?> getConstructorArgumentValues() {
  6. return constructorArgumentValues;
  7. }
  8.  
  9. //设置构造参数值
  10. public void setConstructorArgumentValues(List<?> constructorArgumentValues) {
  11. this.constructorArgumentValues = constructorArgumentValues;
  12. }

构造参数依赖有了,下面就可以来实现构造参数依赖注入了!

1.4 BeanFactory中实现构造参数依赖注入

1.4.1 首先需要把bean定义中的构造参数引用转为真实的值,在DefaultBeanFactory中增加一个方法来干这事。

代码实现:

  1. //把bean定义中的构造参数引用转为真实的值
  2. private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {
  3.  
  4. return this.getRealValues(bd.getConstructorArgumentValues());
  5.  
  6. }
  7.  
  8. //把bean定义中的构造参数引用转为真实的值
  9. private Object[] getRealValues(List<?> defs) throws Exception {
  10. if (CollectionUtils.isEmpty(defs)) {
  11. return null;
  12. }
  13.  
  14. Object[] values = new Object[defs.size()];
  15. int i = 0;
  16. Object v = null;
  17. for (Object rv : defs) {
  18. if (rv == null) {
  19. v = null;
  20. } else if (rv instanceof BeanReference) {
  21. v = this.doGetBean(((BeanReference) rv).getBeanName());
  22. } else if (rv instanceof Object[]) {
  23. // TODO 处理集合中的bean引用
  24. } else if (rv instanceof Collection) {
  25. // TODO 处理集合中的bean引用
  26. } else if (rv instanceof Properties) {
  27. // TODO 处理properties中的bean引用
  28. } else if (rv instanceof Map) {
  29. // TODO 处理Map中的bean引用
  30. } else {
  31. v = rv;
  32. }
  33.  
  34. values[i++] = v;
  35. }
  36.  
  37. return values;
  38. }

问题:有参数了,如何断定是哪个构造方法、哪个工厂方法?需要考虑下面的情况

a)方法是可以重载的

b)形参定义时可能是接口或者父类,实参则是具体的子实现

c)可以通过反射获取提供的构造方法、方法,如下:

java.lang.Class

反射获取构造方法:

  1. //获取所有构造方法
  2. @CallerSensitive
  3. public Constructor<?>[] getConstructors() throws SecurityException {
  4. checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
  5. return copyConstructors(privateGetDeclaredConstructors(true));
  6. }
  7. //获取指定构造方法
  8. @CallerSensitive
  9. public Constructor<T> getConstructor(Class<?>... parameterTypes)
  10. throws NoSuchMethodException, SecurityException {
  11. checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
  12. return getConstructor0(parameterTypes, Member.PUBLIC);
  13. }

反射获取方法:

  1. //获取所有方法
  2. @CallerSensitive
  3. public Method[] getMethods() throws SecurityException {
  4. checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
  5. return copyMethods(privateGetPublicMethods());
  6. }
  7. //获取指定方法
  8. @CallerSensitive
  9. public Method getMethod(String name, Class<?>... parameterTypes)
  10. throws NoSuchMethodException, SecurityException {
  11. checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
  12. Method method = getMethod0(name, parameterTypes, true);
  13. if (method == null) {
  14. throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
  15. }
  16. return method;
  17. }

判断逻辑:

a)先根据参数的类型进行精确匹配查找,如未找到,则进行第二步查找;

b)获得所有的构造方法遍历,通过参数数量过滤,再比对形参类型与实参类型

1.4.2 当我们判断出构造方法或者工厂方法后,对于原型Bean,下次获取是否就可以省去判断了?

也就是说,对于原型Bean,我们可以缓存下这个构造方法或工厂方法。如何实现?

在BeanDefinition中增加缓存的方法:

  1. /* 下面的四个方法是供beanFactory中使用的 */
  2. //获取构造方法
  3. public Constructor<?> getConstructor();
  4.  
  5. //缓存构造方法
  6. public void setConstructor(Constructor<?> constructor);
  7.  
  8. //获取工厂方法
  9. public Method getFactoryMethod();
  10.  
  11. //缓存工厂方法
  12. public void setFactoryMethod(Method factoryMethod);

在GenericBeanDefinition中增加对应的实现:

  1. /* 下面的四个方法是供beanFactory中使用的 */
  2. //获取构造方法
  3. public Constructor<?> getConstructor() {
  4. return constructor;
  5. }
  6.  
  7. //缓存构造方法
  8. public void setConstructor(Constructor<?> constructor) {
  9. this.constructor = constructor;
  10. }
  11.  
  12. //获取工厂方法
  13. public Method getFactoryMethod() {
  14. return factoryMethod;
  15. }
  16.  
  17. //缓存工厂方法
  18. public void setFactoryMethod(Method factoryMethod) {
  19. this.factoryMethod = factoryMethod;
  20. }

1.4.3 接下来可以写查找构造方法或查找工厂方法的代码了

1)在DefaultBeanFactory中增加查找构造方法的方法

  1. //查找构造方法的方法
  2. private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception {
  3.  
  4. Constructor<?> ct = null;
  5.  
  6. //如果参数为空,则返回无参的构造方法
  7. if (args == null) {
  8. return bd.getBeanClass().getConstructor(null);
  9. }
  10.  
  11. // 对于原型bean,从第二次开始获取bean实例时,可直接获得第一次缓存的构造方法。
  12. ct = bd.getConstructor();
  13. if (ct != null) {
  14. return ct;
  15. }
  16.  
  17. // 先根据参数类型获取精确匹配的构造方法
  18. Class<?>[] paramTypes = new Class[args.length];
  19. int j = 0;
  20. for (Object p : args) {
  21. paramTypes[j++] = p.getClass();
  22. }
  23. try {
  24. ct = bd.getBeanClass().getConstructor(paramTypes);
  25. } catch (Exception e) {
  26. // 这个异常不需要处理
  27. }
  28.  
  29. //如果根据参数类型进行精确批次查找没有找到构造方法,就获得所有的构造方法遍历,通过参数数量过滤,再比对形参类型与实参类型
  30. if (ct == null) {
  31.  
  32. // 没有精确参数类型匹配的,则遍历匹配所有的构造方法
  33. // 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型
  34. outer: for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {
  35. Class<?>[] paramterTypes = ct0.getParameterTypes();
  36. if (paramterTypes.length == args.length) {
  37. for (int i = 0; i < paramterTypes.length; i++) {
  38. //isAssignableFrom方法表示是否可以把args[i].getClass()赋值给paramterTypes[i]
  39. if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
  40. continue outer;
  41. }
  42. }
  43.  
  44. ct = ct0;
  45. break outer;
  46. }
  47. }
  48. }
  49.  
  50. //如果找到构造方法了,并且是原型的就缓存起来
  51. if (ct != null) {
  52. // 对于原型bean,可以缓存找到的构造方法,方便下次构造实例对象。在BeanDefinfition中获取设置所用构造方法的方法。
  53. // 同时在上面增加从beanDefinition中获取的逻辑。
  54. if (bd.isPrototype()) {
  55. bd.setConstructor(ct);
  56. }
  57. return ct;
  58. } else {
  59. throw new Exception("不存在对应的构造方法!" + bd);
  60. }
  61. }

2)修改DefaultBeanFactory中用构造方法创建实例的代码调用determineConstructor

  1. // 构造方法来构造对象
  2. private Object createInstanceByConstructor(BeanDefinition bd) throws Exception {
  3. try {
  4. //获取bean定义中的构造参数
  5. Object[] args = this.getConstructorArgumentValues(bd);
  6. //如果构造参数为空就使用无参构造函数
  7. if (args == null) {
  8. return bd.getBeanClass().newInstance();
  9. }
  10. //查找有参构造函数并返回
  11. else {
  12. // 决定构造方法
  13. return this.determineConstructor(bd, args).newInstance(args);
  14. }
  15. } catch (SecurityException e1) {
  16. logger.error("创建bean的实例异常,beanDefinition:" + bd, e1);
  17. throw e1;
  18. }
  19. }

3)按照增加查找构造方法的方式修改静态工厂方法、工厂方法方式的参数依赖

增加查找工厂方法的方法

  1. //查找工厂方法的方法
  2. private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception {
  3. if (type == null) {
  4. type = bd.getBeanClass();
  5. }
  6.  
  7. //获取bean定义中国工厂方法的名字
  8. String methodName = bd.getFactoryMethodName();
  9.  
  10. //如果参数为空就返回无参的方法
  11. if (args == null) {
  12. return type.getMethod(methodName, null);
  13. }
  14.  
  15. Method m = null;
  16. // 对于原型bean,从第二次开始获取bean实例时,可直接获得第一次缓存的构造方法。
  17. m = bd.getFactoryMethod();
  18. if (m != null) {
  19. return m;
  20. }
  21.  
  22. // 先根据参数类型获取精确匹配的方法
  23. Class[] paramTypes = new Class[args.length];
  24. int j = 0;
  25. for (Object p : args) {
  26. paramTypes[j++] = p.getClass();
  27. }
  28. try {
  29. m = type.getMethod(methodName, paramTypes);
  30. } catch (Exception e) {
  31. // 这个异常不需要处理
  32. }
  33.  
  34. //如果根据参数类型进行精确批次查找没有找到工厂方法,就获得所有的构造方法遍历,通过参数数量过滤,再比对形参类型与实参类型
  35. if (m == null) {
  36.  
  37. // 没有精确参数类型匹配的,则遍历匹配所有的方法
  38. // 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型
  39. outer: for (Method m0 : type.getMethods()) {
  40. if (!m0.getName().equals(methodName)) {
  41. continue;
  42. }
  43. Class<?>[] paramterTypes = m.getParameterTypes();
  44. if (paramterTypes.length == args.length) {
  45. for (int i = 0; i < paramterTypes.length; i++) {
  46. //isAssignableFrom方法表示是否可以把args[i].getClass()赋值给paramterTypes[i]
  47. if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
  48. continue outer;
  49. }
  50. }
  51.  
  52. m = m0;
  53. break outer;
  54. }
  55. }
  56. }
  57.  
  58. //如果找到构造方法了,并且是原型的就缓存起来
  59. if (m != null) {
  60. // 对于原型bean,可以缓存找到的方法,方便下次构造实例对象。在BeanDefinfition中获取设置所用方法的方法。
  61. // 同时在上面增加从beanDefinition中获取的逻辑。
  62. if (bd.isPrototype()) {
  63. bd.setFactoryMethod(m);
  64. }
  65. return m;
  66. } else {
  67. throw new Exception("不存在对应的构造方法!" + bd);
  68. }
  69. }

修改DefaultBeanFactory中用工厂方法创建实例的代码调用determineFactoryMethod

  1. // 静态工厂方法
  2. private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
  3.  
  4. Class<?> type = bd.getBeanClass();
  5. Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues());
  6. Method m = this.determineFactoryMethod(bd, realArgs, null);
  7. return m.invoke(type, realArgs);
  8. }
  9.  
  10. // 工厂bean方式来构造对象
  11. private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {
  12.  
  13. Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
  14. Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues());
  15. Method m = this.determineFactoryMethod(bd, realArgs, factoryBean.getClass());
  16.  
  17. return m.invoke(factoryBean, realArgs);
  18. }

1.4.4 测试

参数依赖注入测试

  1. package v2;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.junit.Test;
  7.  
  8. import com.dn.spring.beans.BeanReference;
  9. import com.dn.spring.beans.GenericBeanDefinition;
  10. import com.dn.spring.beans.PreBuildBeanFactory;
  11. import com.dn.spring.samples.ABean;
  12. import com.dn.spring.samples.ABeanFactory;
  13. import com.dn.spring.samples.CBean;
  14. import com.dn.spring.samples.CCBean;
  15.  
  16. /**
  17. *
  18. * @Description: 参数依赖注入测试
  19. * @author leeSmall
  20. * @date 2018年12月1日
  21. *
  22. */
  23. public class DItest {
  24. static PreBuildBeanFactory bf = new PreBuildBeanFactory();
  25.  
  26. //构造函数参数依赖注入
  27. @Test
  28. public void testConstructorDI() throws Exception {
  29.  
  30. GenericBeanDefinition bd = new GenericBeanDefinition();
  31. bd.setBeanClass(ABean.class);
  32. List<Object> args = new ArrayList<>();
  33. args.add("abean01");
  34. args.add(new BeanReference("cbean"));
  35. bd.setConstructorArgumentValues(args);
  36. bf.registerBeanDefinition("abean", bd);
  37.  
  38. bd = new GenericBeanDefinition();
  39. bd.setBeanClass(CBean.class);
  40. args = new ArrayList<>();
  41. args.add("cbean01");
  42. bd.setConstructorArgumentValues(args);
  43. bf.registerBeanDefinition("cbean", bd);
  44.  
  45. bf.preInstantiateSingletons();
  46.  
  47. ABean abean = (ABean) bf.getBean("abean");
  48.  
  49. abean.doSomthing();
  50. }
  51.  
  52. //静态工厂方法参数依赖注入
  53. @Test
  54. public void testStaticFactoryMethodDI() throws Exception {
  55.  
  56. GenericBeanDefinition bd = new GenericBeanDefinition();
  57. bd.setBeanClass(ABeanFactory.class);
  58. bd.setFactoryMethodName("getABean");
  59. List<Object> args = new ArrayList<>();
  60. args.add("abean02");
  61. args.add(new BeanReference("cbean02"));
  62. bd.setConstructorArgumentValues(args);
  63. bf.registerBeanDefinition("abean02", bd);
  64.  
  65. bd = new GenericBeanDefinition();
  66. bd.setBeanClass(CBean.class);
  67. args = new ArrayList<>();
  68. args.add("cbean02");
  69. bd.setConstructorArgumentValues(args);
  70. bf.registerBeanDefinition("cbean02", bd);
  71.  
  72. bf.preInstantiateSingletons();
  73.  
  74. ABean abean = (ABean) bf.getBean("abean02");
  75.  
  76. abean.doSomthing();
  77. }
  78.  
  79. //普通工厂方法的参数依赖注入
  80. @Test
  81. public void testFactoryMethodDI() throws Exception {
  82.  
  83. GenericBeanDefinition bd = new GenericBeanDefinition();
  84. bd.setFactoryBeanName("abeanFactory");
  85. bd.setFactoryMethodName("getABean2");
  86. List<Object> args = new ArrayList<>();
  87. args.add("abean03");
  88. args.add(new BeanReference("cbean02"));
  89. bd.setConstructorArgumentValues(args);
  90. bf.registerBeanDefinition("abean03", bd);
  91.  
  92. bd = new GenericBeanDefinition();
  93. bd.setBeanClass(ABeanFactory.class);
  94. bf.registerBeanDefinition("abeanFactory", bd);
  95.  
  96. bf.preInstantiateSingletons();
  97.  
  98. ABean abean = (ABean) bf.getBean("abean03");
  99.  
  100. abean.doSomthing();
  101. }
  102.  
  103. }

输出结果:

  1. 调用了含有CBean参数的构造方法
  2. 1543644406604 abean01 cb.name=cbean01
  3.  
  4. 调用了含有CBean参数的构造方法
  5. 1543644406607 abean02 cb.name=cbean02
  6.  
  7. 调用了含有CBean参数的构造方法
  8. 1543644406608 abean03 cb.name=cbean02

1.4.5 循环依赖如何处理

问题:构造对象时可以循环依赖吗/

构造实例对象时的循环依赖,会陷入僵死局面,是不允许构造实例时的循环依赖的。那么怎么发现循环依赖呢?

发现循环依赖的方法:加入一个正在构造的bean的记录,每个bean开始构造时加入该记录中,构造完成后从记录中移除。如果有依赖,先看依赖的bean是否在构造中,如是就构成了循环依赖,抛出异常

代码实现:

在DefaultBeanFactory增加如下代码:

  1. //记录正在构造的bean
  2. private ThreadLocal<Set<String>> buildingBeans = new ThreadLocal<>();
  3.  
  4. // 记录正在创建的Bean
  5. Set<String> ingBeans = this.buildingBeans.get();
  6. if (ingBeans == null) {
  7. ingBeans = new HashSet<>();
  8. this.buildingBeans.set(ingBeans);
  9. }
  10.  
  11. // 检测循环依赖 因为如果已bean正在构造,这时发现有其他的bean依赖它,此时它又没有创建好久需要抛异常了
  12. if (ingBeans.contains(beanName)) {
  13. throw new Exception(beanName + " 循环依赖!" + ingBeans);
  14. }
  15.  
  16. // 记录正在创建的Bean
  17. ingBeans.add(beanName);
  18. .....................
  19. ......................
  20. // 创建好实例后,移除创建中记录
  21. ingBeans.remove(beanName);

循环依赖测试代码

  1. package v2;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.junit.Test;
  7.  
  8. import com.dn.spring.beans.BeanReference;
  9. import com.dn.spring.beans.GenericBeanDefinition;
  10. import com.dn.spring.beans.PreBuildBeanFactory;
  11. import com.dn.spring.samples.DBean;
  12. import com.dn.spring.samples.EBean;
  13.  
  14. /**
  15. *
  16. * @Description: 循环依赖测试
  17. * @author leeSmall
  18. * @date 2018年12月1日
  19. *
  20. */
  21. public class CirculationDiTest {
  22.  
  23. static PreBuildBeanFactory bf = new PreBuildBeanFactory();
  24.  
  25. //DBean依赖EBean,EBean又依赖DBean 抛异常
  26. @Test
  27. public void testCirculationDI() throws Exception {
  28. GenericBeanDefinition bd = new GenericBeanDefinition();
  29. bd.setBeanClass(DBean.class);
  30. List<Object> args = new ArrayList<>();
  31. args.add(new BeanReference("ebean"));
  32. bd.setConstructorArgumentValues(args);
  33. bf.registerBeanDefinition("dbean", bd);
  34.  
  35. bd = new GenericBeanDefinition();
  36. bd.setBeanClass(EBean.class);
  37. args = new ArrayList<>();
  38. args.add(new BeanReference("dbean"));
  39. bd.setConstructorArgumentValues(args);
  40. bf.registerBeanDefinition("ebean", bd);
  41.  
  42. bf.preInstantiateSingletons();
  43. }
  44. }

2. 属性依赖

问题1:属性依赖是什么?

  某个属性依赖某个值,需要外部给入这个值

问题2:该如何来描述一个属性依赖?

  属性名、值,定义一个来来表示这两个值

问题3:会有多个属性依赖,怎么存放?

  List存放

问题4:属性值得情况和构造参数值得情况一样吗?

  一样

2.1 定义属性依赖实体

属性依赖实体类类图如下:

属性依赖实体类代码:

  1. package com.study.spring.beans;
  2.  
  3. /**
  4. *
  5. * @Description: 属性值依赖实体
  6. * @author leeSmall
  7. * @date 2018年12月1日
  8. *
  9. */
  10. public class PropertyValue {
  11.  
  12. private String name;
  13.  
  14. private Object value;
  15.  
  16. public PropertyValue(String name, Object value) {
  17. super();
  18. this.name = name;
  19. this.value = value;
  20. }
  21.  
  22. public String getName() {
  23. return name;
  24. }
  25.  
  26. public void setName(String name) {
  27. this.name = name;
  28. }
  29.  
  30. public Object getValue() {
  31. return value;
  32. }
  33.  
  34. public void setValue(Object value) {
  35. this.value = value;
  36. }
  37.  
  38. }

2.2 在BeanDefinition接口中增加获得属性依赖定义的方法

  1. /**
  2. * 获得属性依赖定义的方法
  3. *
  4. */
  5. List<PropertyValue> getPropertyValues();

2.3 在GenericBeanDefinition增加对应的实现

  1. //存放属性依赖
  2. private List<PropertyValue> propertyValues;
  3.  
  4. //获取属性依赖
  5. public List<PropertyValue> getPropertyValues() {
  6. return propertyValues;
  7. }
  8.  
  9. //设置属性依赖
  10. public void setPropertyValues(List<PropertyValue> propertyValues) {
  11. this.propertyValues = propertyValues;
  12. }

2.4 在DefaultBeanFactory中实现属性依赖

  1. //属性依赖实现
  2. private void setPropertyDIValues(BeanDefinition bd, Object instance) throws Exception {
  3. //bean定义中没有属性依赖就直接返回
  4. if (CollectionUtils.isEmpty(bd.getPropertyValues())) {
  5. return;
  6. }
  7. //如果bean定义中有属性依赖就设置值,设置方式和构造参数的设置一样
  8. for (PropertyValue pv : bd.getPropertyValues()) {
  9. //属性依赖没有给名字就直接跳过
  10. if (StringUtils.isBlank(pv.getName())) {
  11. continue;
  12. }
  13. //获取类对象
  14. Class<?> clazz = instance.getClass();
  15. //通过属性名获取类对象里面声明的字段
  16. Field p = clazz.getDeclaredField(pv.getName());
  17.  
  18. //设置字段可访问
  19. p.setAccessible(true);
  20.  
  21. //把属性值转化为真正的值,和构造参数一样
  22. Object rv = pv.getValue();
  23. Object v = null;
  24. if (rv == null) {
  25. v = null;
  26. } else if (rv instanceof BeanReference) {
  27. v = this.doGetBean(((BeanReference) rv).getBeanName());
  28. } else if (rv instanceof Object[]) {
  29. // TODO 处理集合中的bean引用
  30. } else if (rv instanceof Collection) {
  31. // TODO 处理集合中的bean引用
  32. } else if (rv instanceof Properties) {
  33. // TODO 处理properties中的bean引用
  34. } else if (rv instanceof Map) {
  35. // TODO 处理Map中的bean引用
  36. } else {
  37. v = rv;
  38. }
  39.  
  40. //把真正的值v设置到属性p里面 即属性p依赖的值v
  41. p.set(instance, v);
  42.  
  43. }
  44. }

在doGetBean(String beanName)中增加对设置属性依赖的调用

  1. // 给入属性依赖
  2. this.setPropertyDIValues(bd, instance);

注意:属性依赖是允许循环依赖的,因为是在实例创建好之后才设置属性依赖的值的!!!

2.5 属性依赖测试

  1. package v2;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.junit.Test;
  7.  
  8. import com.dn.spring.beans.BeanReference;
  9. import com.dn.spring.beans.GenericBeanDefinition;
  10. import com.dn.spring.beans.PreBuildBeanFactory;
  11. import com.dn.spring.beans.PropertyValue;
  12. import com.dn.spring.samples.ABean;
  13. import com.dn.spring.samples.CBean;
  14. import com.dn.spring.samples.FBean;
  15.  
  16. /**
  17. *
  18. * @Description: 属性依赖测试
  19. * @author leeSmall
  20. * @date 2018年12月1日
  21. *
  22. */
  23. public class PropertyDItest {
  24. static PreBuildBeanFactory bf = new PreBuildBeanFactory();
  25.  
  26. @Test
  27. public void testPropertyDI() throws Exception {
  28.  
  29. //构造参数依赖
  30. GenericBeanDefinition bd = new GenericBeanDefinition();
  31. bd.setBeanClass(ABean.class);
  32. List<Object> args = new ArrayList<>();
  33. args.add("abean01");
  34. args.add(new BeanReference("cbean"));
  35. bd.setConstructorArgumentValues(args);
  36. bf.registerBeanDefinition("abean", bd);
  37.  
  38. //构造参数依赖
  39. bd = new GenericBeanDefinition();
  40. bd.setBeanClass(CBean.class);
  41. args = new ArrayList<>();
  42. args.add("cbean01");
  43. bd.setConstructorArgumentValues(args);
  44. bf.registerBeanDefinition("cbean", bd);
  45.  
  46. //属性依赖
  47. bd = new GenericBeanDefinition();
  48. bd.setBeanClass(FBean.class);
  49. List<PropertyValue> propertyValues = new ArrayList<>();
  50. propertyValues.add(new PropertyValue("name", "FFBean01"));
  51. propertyValues.add(new PropertyValue("age", 18));
  52. propertyValues.add(new PropertyValue("aBean", new BeanReference("abean")));
  53. bd.setPropertyValues(propertyValues);
  54. bf.registerBeanDefinition("fbean", bd);
  55.  
  56. bf.preInstantiateSingletons();
  57.  
  58. FBean fbean = (FBean) bf.getBean("fbean");
  59. System.out.println("设置的属性依赖值: " + fbean.getName() + " " + fbean.getAge());
  60. fbean.getaBean().doSomthing();
  61. }
  62.  
  63. }

输出结果:

  1. 调用了含有CBean参数的构造方法
  2. 设置的属性依赖值: FFBean01 18
  3. 1543649718902 abean01 cb.name=cbean01

完整代码获取地址:

Spring IOC分析和手写实现Spring IOC代码:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v1

Spring DI分析和手写实现Spring DI代码:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v2

框架源码系列二:手写Spring-IOC和Spring-DI(IOC分析、IOC设计实现、DI分析、DI实现)的更多相关文章

  1. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

  2. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

  3. 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程

    一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...

  4. Java集合框架源码(二)——hashSet

    注:本人的源码基于JDK1.8.0,JDK的版本可以在命令行模式下通过java -version命令查看. 在前面的博文(Java集合框架源码(一)——hashMap)中我们详细讲了HashMap的原 ...

  5. 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)

    一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...

  6. 框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)

    一.为什么要提供配置的方法 经过前面的手写Spring IOC.手写Spring DI.手写Spring AOP,我们知道要创建一个bean对象,需要用户先定义好bean,然后注册到bean工厂才能创 ...

  7. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  8. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  9. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

随机推荐

  1. mongodb安装、配置

    1.下载: https://www.mongodb.com/download-center#community 2.进入到mongodb下载目录: a .新建data目录:mkdir data; b. ...

  2. bzoj1625:[Usaco2007 Dec]宝石手镯(背包dp板子)

    1625: [Usaco2007 Dec]宝石手镯 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1349  Solved: 954[Submit][St ...

  3. R语言语法基础二

    R语言语法基础二 重塑数据 增加行和列 # 创建向量 city = c("Tampa","Seattle","Hartford"," ...

  4. Win10+Ubuntu 二三事

    拯救者R720,反反复复弄了不少次,记录一下有用的blog 卸载 http://www.cnblogs.com/xia-Autumn/p/6294055.html https://blog.csdn. ...

  5. 【二维树状数组】计数问题 @JSOI2009/upcexam5911

    时间限制: 1 Sec 内存限制: 128 MB 题目描述 一个n*m的方格,初始时每个格子有一个整数权值.接下来每次有2种操作: 改变一个格子的权值: 求一个子矩阵中某种特定权值出现的个数. 输入 ...

  6. poj2718 Smallest Difference(dfs+特判,还可以贪心更快)

    https://vjudge.net/problem/POJ-2718 其实不太理解为什么10超时了.. 这题似乎是有贪心优化的方法的,我下面直接暴力了.. 暴力之余要特判两个点:1.超时点就是n=1 ...

  7. Python的pandas

    pandas 是python中很重要的组件,网上关于pandas 的文章也很多,比如Python科学计算之Pandas 和 Python数据分析入门 Pandas基于两种数据类型:series与dat ...

  8. 一些mysql小技巧总结

    1.mysql中不清除表里的数据重新设置自增的id的方法 设置主键id自增的数据库表删除数据后,自增id不会自动重新计算,想要重新设置自增的id可以用如下命令: alter table table_n ...

  9. FFM及DeepFFM模型在推荐系统的探索及实践

    12月20日至23日,全球人工智能与机器学习技术大会 AiCon 2018 在北京国际会议中心盛大举行,新浪微博AI Lab 的资深算法专家 张俊林@张俊林say 主持了大会的 搜索推荐与算法专题,并 ...

  10. spring mvc 实战化项目之三板斧

    laravel实战化项目之三板斧 spring mvc 实战化项目之三板斧 asp.net mvc 实战化项目之三板斧 接上文希望从一张表(tb_role_info 用户角色表)的CRUD展开spri ...