首页 > 其他分享 >Spring依赖注入(三) - 注入来源

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

时间:2023-06-20 18:04:23浏览次数:45  
标签:依赖 beanFactory 对象 Spring beanName bean new 注入

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

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;
}

外部化配置

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

标签:依赖,beanFactory,对象,Spring,beanName,bean,new,注入
From: https://blog.51cto.com/u_16115561/6524012

相关文章

  • 5步带你玩转SpringBoot自定义自动配置那些知识点
    目前SpringBoot框架真的深受广大开发者喜爱,毕竟它最大的特点就是:快速构建基于Spring的应用程序的框架,而且它提供了各种默认的功能和配置,可以让开发者快速搭建应用程序的基础结构。但是,当我们需要自定义一些配置时,我们就需要使用自定义自动配置。今天一定让大家深刻体验干货知识点......
  • springboot+websocket简单使用
    一、引入依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.o......
  • springboot简易配置
    server:port:8089servlet:encoding:charset:UTF-8tomcat:uri-encoding:UTF-8spring:datasource:type:com.zaxxer.hikari.HikariDataSourcejdbc-url:jdbc:mysql://localhost:3306/mydb_base?serverTimezone=UTC&useUnicode=t......
  • SpringMVC中接收前端传递的参数,设置了编码过滤器filter,但在控制台中还是出现乱码问题
    SpringMVC中接收前端传递的参数,设置了编码过滤器filter,但在控制台中还是出现乱码问题。 在SpringMVC中遇到乱码问题不要慌,先配个SpringMVC的自带编码过滤器试试 <filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.spr......
  • SpringBoot整合Cache缓存深入理解
    我们在上一篇的基础上继续学习。SpringBoot整合cache缓存入门一、@Caching注解@Caching注解用于在方法或者类上,同时指定多个Cache相关的注解。属性名描述cacheable用于指定@Cacheable注解put用于指定@CachePut注解evict用于指定@CacheEvict注解示例代码如下:importcom.example.mys......
  • SpringBoot02
    Springboot021.Springboot注册三大组件Servlet、Filter、Listener三大组件和我们之前学习的web阶段中Servlet、Filter、Listener一样servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层......
  • 6. SpringMVC的视图
    SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户SpringMVC视图的种类很多,默认有转发视图和重定向视图当工程引入jstl的依赖,转发视图会自动转换为JstlView若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图......
  • Docker配置SpringBoot+ShardingJDBC+MybatisPlus项目实现分库分表与读写分离
    Docker配置SpringBoot+ShardingJDBC+MybatisPlus项目实现分库分表与读写分离 分类于 实战例子本文ShardingJDBC相关知识主要参考自ShardingJDBC官方文档,更多的用法建议到官网文档查看。前言传统的业务系统都是将数据集中存储至单一数据节点的解决方案,如今随着互联网数据......
  • Web项目中使用Spring 3.x + Quartz 2.x实现作业调度详解
    Quartz是一个基于Java的作业调度管理的轻量级框架,目前在很多企业应用中被使用,它的作用类似于java.util中的Timer和TimeTask、数据库中的job等,但Quartz的功能更强大、更灵活。从Quartz2开始,你可以使用POJO作为一个任务(Job),这种开发方式进一步降低了代码的耦合度,如果跟Spring进行整合,......
  • 基于spring cloud技术栈构建的一款源码级jvs低代码平台,值得收藏
    开发团队在日常的项目开发过程中,会遇到各种各样单点需求。确保应用程序能够满足特定的业务需求并与现有系统和服务进行有效集成,那么是团队选择对应技术栈或者整体开发工具的核心考量:核心关注的点:1、技术栈的选择,一定要通用,人才选择面比较广2、能力的扩展性,能否自己添加各种能力,最......