0
点赞
收藏
分享

微信扫一扫

Spring依赖注入(三) - 注入来源

代码小姐 2023-06-20 阅读 38

Spring依赖注入(三) - 注入来源_System

Spring依赖注入来源

上一章中,我们在第三步的代码分析中可看到,在进行注入的依赖查找的时候,查找来源会从如下几个方式去查找:

  • • 1、先看注入是否是外部化配置,如果是,则注入(@Value的方式)
  • • 2、遍历看注入的对象是否是resolvableDependencies中包含的对象,如果是则返回(非Spring容器管理的对象)
  • • 3、从Spring容器中的对象中查找返回或者是Spring单例对象中查找返回

接下来我们从源码的角度来看一下注入来源的存储结构。

Spring容器中的对象

Spring容器中的对象,大多数说的就是我们使用BeanDefinition构建的对象,这种方式包括了xml、@Bean以及直接使用API构建的方式,当然也包括了一些如AutowiredAnnotationBeanPostProcessor在内的内建对象。

首先BeanDefinition在Spring容器中主要存储在DefaultListableBeanFactory对象中,我们来看看他的主要结构:

// 存储BeanDefinition信息,key为bean的名称列表
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 存储BeanDefinition的名称列表,按照注入顺序排序
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

而BeanDefinition信息是在方法DefaultListableBeanFactory#registerBeanDefinition中注册到上面的集合中的:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  throws BeanDefinitionStoreException {
    // 数据校验,这里只对AbstractBeanDefinition进行校验是因为BeanDefinitionBuilder创建的BeanDefinition信息在getBeanDefinition时候进行过校验
  if (beanDefinition instanceof AbstractBeanDefinition) {
    try {
      ((AbstractBeanDefinition) beanDefinition).validate();
    }
    catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                             "Validation of bean definition failed", ex);
    }
  }

  // 校验是否注册过
  BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
  if (existingDefinition != null) {
    // 注册过的话校验是否可以覆盖注册过的beanDefinition信息
    if (!isAllowBeanDefinitionOverriding()) {
      throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    }
    // .... 
    this.beanDefinitionMap.put(beanName, beanDefinition);
  }
  else {
    // 是否正在创建的过程中
    if (hasBeanCreationStarted()) {
      // Cannot modify startup-time collection elements anymore (for stable iteration)
      synchronized (this.beanDefinitionMap) {
        this.beanDefinitionMap.put(beanName, beanDefinition);
        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
        updatedDefinitions.addAll(this.beanDefinitionNames);
        updatedDefinitions.add(beanName);
        this.beanDefinitionNames = updatedDefinitions;
        removeManualSingletonName(beanName);
      }
    }
    else {
      // Still in startup registration phase
      // 设置beanDefinition信息
      this.beanDefinitionMap.put(beanName, beanDefinition);
      this.beanDefinitionNames.add(beanName);
      removeManualSingletonName(beanName);
    }
    this.frozenBeanDefinitionNames = null;
  }

  if (existingDefinition != null || containsSingleton(beanName)) {
    resetBeanDefinition(beanName);
  }
  else if (isConfigurationFrozen()) {
    clearByTypeCache();
  }
}

Spring单例对象

Spring单例对象可以看成是一种特殊的容器对象,他是我们创建好了对象注册到容器中的,所以容器中不会存储他对应的BeanDefinition信息,Spring也不会管理他对应的生命周期。

单例对象案例:

public class SingletonBeanRegistryDemo {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(SingletonBeanRegistryDemo.class);
    annotationConfigApplicationContext.refresh();

    CommonInjectionEntity commonInjectionEntity = new CommonInjectionEntity(1L, "Singleton Bean Registry Test!");

    // 获得单例注册器
    SingletonBeanRegistry beanFactory = annotationConfigApplicationContext.getBeanFactory();
    // 注册单例
    beanFactory.registerSingleton("commonInjectionEntity", commonInjectionEntity);

    System.out.println(Objects.equals(commonInjectionEntity, annotationConfigApplicationContext.getBean("commonInjectionEntity")));

    annotationConfigApplicationContext.close();
  }
}

从示例我们也可以看出,单例是我们创建好了的对象再注入到容器中。

单例对象的处理主要存在DefaultSingletonBeanRegistry对象中:

// 存储Bean得单例对象,key为对象名称
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 有序的存储Bean单例对象的名称
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

而单例对象的注册主要是在DefaultSingletonBeanRegistry#registerSingleton()方法中:

@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
  synchronized (this.singletonObjects) {
    // 校验缓存是否存在,存在则直接报错
    Object oldObject = this.singletonObjects.get(beanName);
    if (oldObject != null) {
      throw new IllegalStateException("Could not register object [" + singletonObject +
                                      "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
    }
    // 添加单例对象
    addSingleton(beanName, singletonObject);
  }
}

protected void addSingleton(String beanName, Object singletonObject) {
  synchronized (this.singletonObjects) {
    // 添加
    this.singletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
  }
}

非Spring容器管理的对象

什么是非Spring容器管理的对象,换句话来说,也就是我们在使用BeanFactory#getBean()的时候获取不到的对象就是非Spring容器管理的对象。

首先我们来看一下非容器化管理的对象的存储结构,他其实就是DefaultListableBeanFactory对象中的字段resolvableDependencies

private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

他的设置主要是在DefaultListableBeanFactory#registerResolvableDependency()方法中:

@Override
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
  Assert.notNull(dependencyType, "Dependency type must not be null");
  if (autowiredValue != null) {
    if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
      throw new IllegalArgumentException("Value [" + autowiredValue +
                                         "] does not implement specified dependency type [" + dependencyType.getName() + "]");
    }
    this.resolvableDependencies.put(dependencyType, autowiredValue);
  }
}

从设置上来看他非常简单,就只是一个简单的存储,在Spring中AbstractApplicationContext#refresh()方法的第三步prepareBeanFactory()中,对非Spring容器管理的对象进行了模式添加:

Spring依赖注入(三) - 注入来源_ci_02

image-20230325213635546

至此我们也可以借助这四个对象来进行测试:

public class ResolvableDependenciesSourceDemo {

  @Resource
  private BeanFactory beanFactory;
  @Resource
  private ResourceLoader resourceLoader;
  @Resource
  private ApplicationEventPublisher applicationEventPublisher;
  @Resource
  private ApplicationContext applicationContext;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(ResolvableDependenciesSourceDemo.class);
    annotationConfigApplicationContext.refresh();

    // 自动注入校验 注入成功
    checkAutowiredInfo(annotationConfigApplicationContext);
    // 依赖查找校验 全报错
    checkLookUp(annotationConfigApplicationContext);

    annotationConfigApplicationContext.close();
  }

  private static void checkAutowiredInfo(BeanFactory beanFactory) {
    ResolvableDependenciesSourceDemo bean = beanFactory.getBean(ResolvableDependenciesSourceDemo.class);
    System.out.println("beanFactory == resourceLoader" + (bean.beanFactory == bean.resourceLoader));
    System.out.println("resourceLoader == applicationEventPublisher" + (bean.resourceLoader == bean.applicationEventPublisher));
    System.out.println("beanFactory == applicationEventPublisher" + (bean.beanFactory == bean.applicationEventPublisher));
    System.out.println("beanFactory == applicationContext" + (bean.beanFactory == bean.applicationContext));
  }

  private static void checkLookUp(BeanFactory beanFactory) {
    checkLookUp(beanFactory, BeanFactory.class);
    checkLookUp(beanFactory, ResourceLoader.class);
    checkLookUp(beanFactory, ApplicationEventPublisher.class);
    checkLookUp(beanFactory, ApplicationContext.class);
  }

  private static void checkLookUp(BeanFactory beanFactory, Class<?> clazz) {
    try {
      beanFactory.getBean(clazz);
    } catch (BeansException e) {
      System.out.println("Spring上下文不存在对象:" + clazz.getName());
    }
  }
}

注意:如下面例子一样注册到resolvableDependencies中后,不同的注入方式可能会导致的结果不同。

演示:

从下面配置可以看出当使用@Resource中按照名称注入的时候输出的对象与其他形式不同。

public class ResolvableDependenciesSourceDemo {

  @Autowired
  private CommonInjectionEntity autowiredCommonInjectionEntity;

  @Autowired
  @Qualifier(value = "commonInjectionEntity")
  private CommonInjectionEntity aualifierAutowiredCommonInjectionEntity;

  @Resource
  private CommonInjectionEntity resolrceCommonInjectionEntity;

  @Resource
  private CommonInjectionEntity commonInjectionEntity;

  @Autowired
  private Map<String, CommonInjectionEntity> autowiredCommonInjectionEntityMap;

  @Resource
  private Map<String, CommonInjectionEntity> resourceCommonInjectionEntityMap;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(ResolvableDependenciesSourceDemo.class);
    annotationConfigApplicationContext.getBeanFactory().registerResolvableDependency(CommonInjectionEntity.class, new CommonInjectionEntity(1L,"ResolvableDependency中的对象"));
    annotationConfigApplicationContext.refresh();

    ResolvableDependenciesSourceDemo bean = annotationConfigApplicationContext.getBean(ResolvableDependenciesSourceDemo.class);
    
    // Autowired按照类型注入:CommonInjectionEntity{id=1, desc='ResolvableDependency中的对象'}
    System.out.println(bean.autowiredCommonInjectionEntity);
    // Autowired按照名称注入:CommonInjectionEntity{id=1, desc='ResolvableDependency中的对象'}
    System.out.println(bean.aualifierAutowiredCommonInjectionEntity);
    // Resource按照类型注入:CommonInjectionEntity{id=1, desc='ResolvableDependency中的对象'}
    System.out.println(bean.resolrceCommonInjectionEntity);
    // Resource按照名称注入:CommonInjectionEntity{id=2, desc='Spring IOC 容器中的对象'}
    System.out.println(bean.commonInjectionEntity);
    // Autowired注入Map:{cn.phshi.domain.CommonInjectionEntity@27a8c74e=CommonInjectionEntity{id=1, desc='ResolvableDependency中的对象'}}
    System.out.println(bean.autowiredCommonInjectionEntityMap);
    // Resource注入Map:{cn.phshi.domain.CommonInjectionEntity@27a8c74e=CommonInjectionEntity{id=1, desc='ResolvableDependency中的对象'}}
    System.out.println(bean.resourceCommonInjectionEntityMap);

    annotationConfigApplicationContext.close();
  }

  @Bean
  public CommonInjectionEntity commonInjectionEntity() {
    return new CommonInjectionEntity(2L,"Spring IOC 容器中的对象");
  }
}

我们会发现当使用@Resource名称注入的时候输出的结果会与其他注入的输出结果不同。虽然我们平时开发几乎用不到这种情况,但是我们还是要认识这种坑。为什么会产生这种情况呢?

我们知道@Resource会优先名称注入,名称注入找不到再通过类型注入,产生上面的原因就是因为这个。@Resource具体注入的代码存在于CommonAnnotationBeanPostProcessor#autowireResource()中,我们来看看区别这块的代码:

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
  throws NoSuchBeanDefinitionException {

  Object resource;
  Set<String> autowiredBeanNames;
  String name = element.name;

  if (factory instanceof AutowireCapableBeanFactory) {
    AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
    DependencyDescriptor descriptor = element.getDependencyDescriptor();
    // 名称校验 通过factory.containsBean判断IOC容器中是否存在对象 注意加了!
    if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
      autowiredBeanNames = new LinkedHashSet<>();
      // 通过类型查找,其实具体非容器管理对象查找逻辑在内部
      resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
      if (resource == null) {
        throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
      }
    }
    // 就是在IOC容器中取对象
    else {
      // 其实就是执行 getBean()
      resource = beanFactory.resolveBeanByName(name, descriptor);
      autowiredBeanNames = Collections.singleton(name);
    }
  }

  // .... 
  
  return resource;
}

外部化配置

外部化配置使用简单,这里就不做示例

举报

相关推荐

0 条评论