Spring 手动注册bean

一般情况下,我们Spring应用中的bean都是通过注解或者xml注入到容器中的,有些情况下我们可能想手动往容器中注入bean,即编程方式注入bean。

本文所使用源码包版本:spring-beans-5.0.5.RELEASE.

如何注册?

Spring 中用BeanDefinition接口描述一个bean,Spring容器中用Map<String, BeanDefinition> beanDefinitionMap

存储beanName和BeanDefinition对象的映射关系【beanDefinitionMap 可参考DefaultListableBeanFactory】。Spring在实例化一个bean,都是先从 beanDefinitionMap 中获取beanDefinition对象,进而构造出对应的bean。因此,我们手动注册bean的问题,就演化为如何往这个 beanDefinitionMap 放入我们要注册bean对应的 BeanDefinition 对象。

Spring 提供了 BeanDefinitionRegistry 接口来操作底层beanFactory实现的beanDefinitionMap。

【2020-08-17】备注:推荐通过实现 BeanDefinitionRegistryPostProcessor 接口操作 BeanDefinitionRegistry:

  1. public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
  2. /**
  3. * Modify the application context's internal bean definition registry after its
  4. * standard initialization. All regular bean definitions will have been loaded,
  5. * but no beans will have been instantiated yet. This allows for adding further
  6. * bean definitions before the next post-processing phase kicks in.
  7. * @param registry the bean definition registry used by the application context
  8. * @throws org.springframework.beans.BeansException in case of errors
  9. */
  10. void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
  11. }

测试示例:

    我们有个类 H,把它手动添加到Spring 容器中成为一个bean:

  1. public class H {
  2. private String s ;
  3. public void test(){
  4. System.out.println("hello world!");
  5. }
  6. public void setS(String s) {
  7. this.s = s;
  8. }
  9. }

主程序:

  1. @EnableAspectJAutoProxy
  2. @ComponentScan("com.dxc.opentalk.springtest")
  3. public class BootStrap {
  4. public static void main(String[] args) {
  5. AnnotationConfigApplicationContext applicationContext
  6. = new AnnotationConfigApplicationContext(BootStrap.class);
  7. DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
  8. GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  9. beanDefinition.setBeanClass(H.class);
  10. beanFactory.registerBeanDefinition("h", beanDefinition);
  11. H h = (H) applicationContext.getBean("h");
  12. h.test();
  13. }
  14. }

程序运行结果:

hello world!

从容器中获取到了名称为“h”的bean,并且正确打印了方法调用结果。另外,在类H中,可以使用@Autowired注入其他已有的bean的,bean的依赖关系也是支持的。如下:

  1. public class H {
  2. private String s ;
  3. @Autowired
  4. Y y;
  5. public void test(){
  6. y.test();
  7. System.out.println("hello world!");
  8. }
  9. public void setS(String s) {
  10. this.s = s;
  11. }
  12. }

H类依赖一个已有的bean Y:

  1. @Component
  2. public class Y {
  3. public Y() {
  4. System.out.println("Construct Y");
  5. }
  6. public void test(){
  7. System.out.println("y.test()...");
  8. }
  9. }

上述测试代码输出结果:

  1. Construct Y
  2. y.test()...
  3. hello world!

 

涉及源码:

BeanDefinition 接口:

     BeanDefinition描述了一个bean实例,该实例具有属性值,构造函数参数值以及具体实现所提供的更多信息。类图如下,主要三个实现类:RootBeanDefinition、ChildBeanDefinition 和 GenericBeanDefinition。Bean的属性,比如对应Java类的Class、作用域scope、是否延迟加载lazyInit等,都可以通过BeanDefinition控制。

      RootBeanDefinition 可以在配置阶段用于注册各个Bean定义,上面的测试用例 BeanDefinition 也可以使用 RootBeanDefinition 实现。 但是官方推荐,从Spring 2.5开始,以编程方式注册Bean定义的首选方法是{@link GenericBeanDefinition}类。

BeanDefinitionRegistry 接口:

       包含BeanDefinition的注册表的接口,例如 RootBeanDefinition 和 ChildBeanDefinition 实例。 通常由内部使用AbstractBeanDefinition层次结构的 BeanFactory 实现。

  1. /**
  2. * Register a new bean definition with this registry.
  3. * Must support RootBeanDefinition and ChildBeanDefinition.
  4. * @param beanName the name of the bean instance to register
  5. * @param beanDefinition definition of the bean instance to register
  6. * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
  7. * or if there is already a BeanDefinition for the specified bean name
  8. * (and we are not allowed to override it)
  9. * @see RootBeanDefinition
  10. * @see ChildBeanDefinition
  11. */
  12. void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  13. throws BeanDefinitionStoreException;

       注意用到的 registerBeanDefinition 方法,如果构造的 BeanDefinition 无效,或者指定的bean名称已经存在BeanDefinition(如果不允许覆盖它)会抛出 BeanDefinitionStoreException 异常。BeanDefinitionRegistry 还有其他的方法,比如根据bean名称移除beanDefinition的方法 removeDefinition(String beanName)等。

DefaultListableBeanFactory 类:

DefaultListableBeanFactory中用Map<String,BeanDefinition> beanDefinitionMap 保存beanName和BeanDefinition对象的映射关系,同时实现了BeanDefinitionRegistry接口,用来操作beanDefinitionMap,其中 registerBeanDefinition 方法:

  1. @Override
  2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  3. throws BeanDefinitionStoreException {
  4. Assert.hasText(beanName, "Bean name must not be empty");
  5. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  6. if (beanDefinition instanceof AbstractBeanDefinition) {
  7. try {
  8. //校验传入beanDefinition的正确性
  9. ((AbstractBeanDefinition) beanDefinition).validate();
  10. }
  11. catch (BeanDefinitionValidationException ex) {
  12. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  13. "Validation of bean definition failed", ex);
  14. }
  15. }
  16. BeanDefinition oldBeanDefinition;
  17. oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  18. if (oldBeanDefinition != null) {
  19. //如果已存在该bean名称的beanDefinition,并且不允许覆盖,则抛异常
  20. if (!isAllowBeanDefinitionOverriding()) {
  21. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  22. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  23. "': There is already [" + oldBeanDefinition + "] bound.");
  24. }
  25. else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
  26. // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
  27. if (this.logger.isWarnEnabled()) {
  28. this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
  29. "' with a framework-generated bean definition: replacing [" +
  30. oldBeanDefinition + "] with [" + beanDefinition + "]");
  31. }
  32. }
  33. else if (!beanDefinition.equals(oldBeanDefinition)) {
  34. if (this.logger.isInfoEnabled()) {
  35. this.logger.info("Overriding bean definition for bean '" + beanName +
  36. "' with a different definition: replacing [" + oldBeanDefinition +
  37. "] with [" + beanDefinition + "]");
  38. }
  39. }
  40. else {
  41. if (this.logger.isDebugEnabled()) {
  42. this.logger.debug("Overriding bean definition for bean '" + beanName +
  43. "' with an equivalent definition: replacing [" + oldBeanDefinition +
  44. "] with [" + beanDefinition + "]");
  45. }
  46. }
  47. this.beanDefinitionMap.put(beanName, beanDefinition);
  48. }
  49. else {
  50. //检查beanFactory的bean创建阶段是否已开始,可以理解为容器是否已经初始化过了
  51. if (hasBeanCreationStarted()) {
  52. // Cannot modify startup-time collection elements anymore (for stable iteration)
  53. synchronized (this.beanDefinitionMap) {
  54. //把新加入的beanName-beanDefinition映射加入beanDefinitionMap
  55. //把beanName加入的维护的beanDefinitionName列表(ArrayList)中
  56. this.beanDefinitionMap.put(beanName, beanDefinition);
  57. List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
  58. updatedDefinitions.addAll(this.beanDefinitionNames);
  59. updatedDefinitions.add(beanName);
  60. this.beanDefinitionNames = updatedDefinitions;
  61. //如果手动注册的单例bean名称包含了beanName,需要移除
  62. //这个地方默认构造的beanDefinition对象作用域也是“”,等价于单例,但是却没有把 beanName加入到manualSingletonNames集合(LinkedHashSet)中
  63. //最终beanName对应的bean实例化后,manualSingletonNames还是没有这个beanName,略奇怪。。。
  64. if (this.manualSingletonNames.contains(beanName)) {
  65. Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
  66. updatedSingletons.remove(beanName);
  67. this.manualSingletonNames = updatedSingletons;
  68. }
  69. }
  70. }
  71. else {
  72. // Still in startup registration phase
  73. this.beanDefinitionMap.put(beanName, beanDefinition);
  74. this.beanDefinitionNames.add(beanName);
  75. this.manualSingletonNames.remove(beanName);
  76. }
  77. this.frozenBeanDefinitionNames = null;
  78. }
  79. //如果oldBeanDefinition不为null,且单例池缓存中已经有beanName对应的bean,则重置给定bean的所有bean定义缓存,包括从其派生的bean的缓存。
  80. if (oldBeanDefinition != null || containsSingleton(beanName)) {
  81. resetBeanDefinition(beanName);
  82. }
  83. }

SingletonBeanRegistry 接口:

SingletonBeanRegister接口,是单例bean的管理接口,也可以用来注册单例bean,方法:

void registerSingleton(String beanName, Object singletonObject);

DefaultListableBeanFactory类也实现了该接口,我们可以将上面的测试代码修改下,也可以达到注册一个单例bean的目的:

  1. @EnableAspectJAutoProxy
  2. @ComponentScan("com.dxc.opentalk.springtest")
  3. public class BootStrap {
  4. public static void main(String[] args) {
  5. AnnotationConfigApplicationContext applicationContext
  6. = new AnnotationConfigApplicationContext(BootStrap.class);
  7. DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
  8. beanFactory.registerSingleton("h", new H());
  9. H h = (H) applicationContext.getBean("h");
  10. h.test();
  11. }
  12. }

实现起来简单,但是有限制:一是bean依赖项不好处理;二是待注入的实例应该被完全初始化,注册表不会执行任何初始化回调方法,比如InitializingBean的{@code afterPropertiesSet}方法等;。

 

(0)

相关推荐