7. IOC 依赖来源 Dependency Sources

77 阅读7分钟

7. IOC 依赖来源 Dependency Sources

7.1 依赖查找的来源

依赖查找 Dependency Lookup 的来源:

  • 外部:

    • BeanDefinition
      • <bean id='"user" class="org....User" >
      • @Bean,@Component
      • BeanDefinitionBuilder 创建 BeanDefinition,并进行注册
    • 单例对象
      • api 手动注册外部单例对象,SingletonBeanRegistry#registerSingleton
  • 内部:

    • Spring 内建 BeanDefinition

    image-20210425161123960

    • Spring 内建单例对象

    image-20210425161239547

7.2 依赖注入的来源

依赖注入的来源在依赖查找的基础上面,增加了两个新的来源:

  1. 非 Spring 容器管理的对象 ResolvableDependency,也称为 游离对象,这些对象并没有注册到容器。
  2. 外部配置,使用 @Value 引入

image-20210425161914387

ResolvableDependency 非 Spring 容器管理的对象有 4 个:

  • BeanFactory
  • ResourceLoader
  • ApplicationEventPublisher
  • ApplicationContext

为什么依赖查找的来源没有 ResolvableDependency ?

其实很容易想明白,依赖查找就是通过 BeanFactory 查找 bean,即已经得到 BeanFactory 和 ApplicationContext 了,自然就不需要去查找这几个对象了

  1. 注册 4 个非Spring容器管理的对象到容器ResolvableDependency,保存在上下文的 resolvableDependencies 属性中

Spring 框架在应用上下文启动时refresh()会调用下面这个方法,注册 4 个非Spring容器管理的对象到容器,源码如下:

AbstractApplicationContext.java

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 注册非容器管理对象ResolvableDependency, this是当前应用上下文	
    // 可以看到后3个其实是同一个对象this
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
}

DefaultListableBeanFactory.java
    
// 注册ResolvableDependency, 将其保存到resolvableDependencies中
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
    Assert.notNull(dependencyType, "Dependency type must not be null");
    if (autowiredValue != null) {
        this.resolvableDependencies.put(dependencyType, autowiredValue);
    }
}
  1. 在6.14 章节,依赖处理时会根据目标 bean 的类型进行层次性查找,那如果目标 bean 是非 Spring 容器管理的对象即 ResolvableDependency 呢?下面这个例子就使用 @Autowired 依赖注入了 4 个 ResolvableDependency 对象
public class ResolvableDependencySourceDemo {
    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private ResourceLoader resourceLoader;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private ApplicationContext applicationContext;
        public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(DependencySourceDemo.class);
        applicationContext.refresh();
    }
}
  1. 源码分析,在依赖处理过程中,会去容器中查找符合条件的目标 bean,上述例子中依赖的都是非 Spring 容器管理的对象,即BeanFactoryUtils.beanNamesForTypeIncludingAncestors()从容器中找不到这些类型的 bean,因此还会去 this.resolvableDependencies 中根据类型进行查找(无法根据名称查找)
protected Map<String, Object> findAutowireCandidates(
    @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

    // 根据bean类型进行层次性查找候选bean,
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    
    // 遍历4个非 Spring 容器管理的对象 ResolvableDependency
    // 如果目标bean类型是ResolvableDependency 4个对象的类型, 则将该对象返回
    // ResolvableDependency包含BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
        Class<?> autowiringType = classObjectEntry.getKey();
        // 若目标bean类型是autowiringType
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = classObjectEntry.getValue();
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            if (requiredType.isInstance(autowiringValue)) {
                // 将对象类名作为key,对象作为value加入到result并返回
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }
    // 判断循环依赖? 跳过
    for (String candidate : candidateNames) {
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    if (result.isEmpty()) {
    }
    return result;
}
  1. 查看是否注入成功。
public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    applicationContext.register(DependencySourceDemo.class);
    applicationContext.refresh();

    DependencySourceDemo bean = applicationContext.getBean(DependencySourceDemo.class);
    System.out.println(bean.beanFactory);
    System.out.println(bean.resourceLoader);
    System.out.println(bean.applicationEventPublisher);
    System.out.println(bean.applicationContext);

    System.out.print("注入的applicationContext与当前应用上下文是同一个对象: ");
    System.out.println(bean.applicationContext == applicationContext);
    System.out.print("注入的BeanFactory与当前应用上下文中的BeanFactory是同一个对象: ");
    System.out.println((bean.beanFactory == applicationContext.getBeanFactory()));}
    // 检验容器中是否有 BeanFactory 类型的bean, 肯定没有,
    // 虽然可以进行依赖注入, 但是BeanFactory不是由 Spring 容器管理的
    try {
        applicationContext.getBean(BeanFactory.class);
    } catch (NoSuchBeanDefinitionException e) {
        System.out.println("容器中没有 BeanFactory 类型的 bean");
    }
}

输出结果:

  • 可以看到注入的 BeanFactory 是 DefaultListableBeanFactory 类型,且与当前应用上下文的 BeanFactory 是同一个对象,
  • 注入的 ApplicationContext 也与当前上下文 ApplicationContext 是同一个对象
  • 注入的 resourceLoader,applicationEventPublisher,applicationContext 都是同一个对象,即当前应用上下文
  • ResolvableDependency 对象可以用来依赖注入,但不能进行依赖查找
org.springframework.beans.factory.support.DefaultListableBeanFactory@3745e5c6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,dependencySourceDemo]; root of factory hierarchy
org.springframework.context.annotation.AnnotationConfigApplicationContext@7c75222b, started on Tue Apr 27 23:06:01 CST 2021
org.springframework.context.annotation.AnnotationConfigApplicationContext@7c75222b, started on Tue Apr 27 23:06:01 CST 2021
org.springframework.context.annotation.AnnotationConfigApplicationContext@7c75222b, started on Tue Apr 27 23:06:01 CST 2021
注入的applicationContext与当前应用上下文是同一个对象: true
注入的BeanFactory与当前应用上下文中的BeanFactory是同一个对象: true
容器中没有 BeanFactory 类型的 bean

7.3 Spring 容器管理和游离对象

image-20210427234647260

7.4 BeanDefinition 作为依赖来源

通过 7.1 章节我们知道,依赖来源包括 BeanDefinition,包括 xml 配置,@Bean 注解配置,BeanDefinitionBuilder 手动创建的 BeanDefinition。

BeanDefinition 作为依赖来源,有以下几个特点:

  • 元数据:BeanDefinition 由 xml 配置,@Bean 注解配置,BeanDefinitionBuilder 手动创建
  • 注册到容器:BeanDefinitionRegistry#registerBeanDefinition
  • 类型:延迟和非延迟
  • 顺序:Bean 初始化时按照声明顺序进行
  • 存储:DefaultListableBeanFactory.beanDefinitionMap 属性,是 ConcurrentHashMap 类型

1. 外部 BeanDefinition

通过 xml,@Bean 注解,BeanDefinitionBuilder 手动注册 bean 的方式,见 4.4 章节

  1. 注册 BeanDefinition 到容器源码分析,无论是 Spring 内建 BeanDefinition,还是 xml 配置,@Bean 注解配置的 bean,都要调用下面的方法注册 BeanDefinition 到容器。DefaultListableBeanFactory 是 BeanDefinitionRegistry 接口的实现类,实现了注册 BeanDefinition 功能
DefaultListableBeanFactory.java, 

// 保存BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存bean名称, 有序
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

// 实现了BeanDefinitionRegistry接口registerBeanDefinition方法
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    // 校验bean
    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }

    // 查找bean名称是否已被占用
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
		// bean名称已被占用, 会使用当前bean进行覆盖
        // Springboot2.1中修改为抛出异常BeanDefinitionOverrideException
    } else {
        if (hasBeanCreationStarted()) {
            synchronized (this.beanDefinitionMap) {
                // 将BeanDefinition注册到容器,即保存到beanDefinitionMap
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                // 添加bean的名称
                updatedDefinitions.add(beanName);
                // beanDefinitionNames是ArrayList,保证bean初始化的顺序与声明顺序相同
                this.beanDefinitionNames = updatedDefinitions;
                removeManualSingletonName(beanName);
            }
        } else {
            // 将BeanDefinition注册到容器,即保存到beanDefinitionMap
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 将bean名称保存到beanDefinitionNames
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        
        this.frozenBeanDefinitionNames = null;
    }

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

// 补充: debug各种配置bean的方式, 看看注册beandefinition时是不是都一样, 已经debug了, 都是调用该方法

2. Spring 内建 BeanDefinition

通过 7.1 章节我们知道 Spring 有多个内建 BeanDefinition 会注册到容器。

  1. 源码分析,注册 Spring 内建 BeanDefinition 到上下文。在注解驱动, Annotation 类型上下文等情况下都会调用该方法加载 Spring 内建 bean
AnnotationConfigUtils.java
    
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {

	// .....

    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注册internalConfigurationAnnotationProcessor
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        // 设置bean的class
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // 注册bean到上下文registry, bean名称为CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME
        // registerPostProcessor 源码见下一个方法
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册internalAutowiredAnnotationProcessor
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
	// 注册internalCommonAnnotationProcessor
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
	// 注册internalPersistenceAnnotationProcessor
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
            def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                                                AnnotationConfigUtils.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
	// 注册internalEventListenerProcessor
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }
	// 注册internalEventListenerFactory
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

// 注册beanDefinition到registry
private static BeanDefinitionHolder registerPostProcessor(
    BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 见上个代码块
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);
}
  1. // 补充: 依赖注入Spring内建BeanDefinition

7.5 单例对象作为依赖来源

  • 来源:外部普通 java 对象,不一定是 pojo
  • 注册:SingletonBeanRegistry#RegisterSingleton
  • 无生命周期管理
  • 无法实现延迟初始化,因为在外部已经初始化,然后才注册到容器的
  • 存储:DefaultSingletonBeanRegistry.singletonObjects 属性,ConcurrentHashMap类型
  • 处理依赖:DefaultSingletonBeanRegistry#getSingleton

1. 外部单例对象

  1. 参考 4.4.4 章节 外部对象注册到容器,下面的例子是手动创建一个 user 对象,将user 对象注册到容器,显而易见,容器无法对 user 进行延迟初始化,也无法进行生命周期管理
public static void main(String[] args) {
    // 1. 创建应用上下文
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    // 2. 创建一个外部 user 对象
    User user = new User();

    // 3. 注册外部对象到容器
    SingletonBeanRegistry beanFactory = applicationContext.getBeanFactory();
    beanFactory.registerSingleton("mercy", user);
    // 4. 启动应用上下文
    applicationContext.refresh();

    // 5. 依赖查找, 会优先从容器的 singletonObjects 属性中查找 bean
    User mercy = applicationContext.getBean("mercy", User.class);
    // 6. 判断容器中的 bean 是否是那个外部对象
    System.out.println("mercy == user: " + (mercy == user));

    // 7. 关闭应用上下文
    applicationContext.close();
}

输出结果:

User{id=null, name='null'}
mercy == user: true
  1. 注册外部单例对象到容器源码分析,外部 java 对象注册到容器,是保存到容器的 singletonObjects 属性中,是一个ConcurrentHashMap 集合
DefaultSingletonBeanRegistry.java

// 保存外部对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 保存外部对象bean名称顺序
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
    
// DefaultListableBeanFactory继承了DefaultSingletonBeanRegistry
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    Assert.notNull(beanName, "Bean name must not be null");
    Assert.notNull(singletonObject, "Singleton object must not be null");
    
    synchronized (this.singletonObjects) {
        Object oldObject = this.singletonObjects.get(beanName);
        // 容器中已经存在同名bean, 抛出异常
        if (oldObject != null) {
            throw new IllegalStateException("Could not register object [" + singletonObject +
                                            "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
        }
        // 将外部java对象保存到ConcurrentHashMap集合singletonObjects
        addSingleton(beanName, singletonObject);
    }
}

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        // singleton和factoryBean互斥
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        // 保存外部对象到LinkedHashSet集合中, 可以保证顺序
        this.registeredSingletons.add(beanName);
    }
}
  1. 依赖查找单例 bean 源码分析,在以来查找 getBean() 时会优先从容器的 singletonObjects 属性中查找 bean,查找不到才去 BeanDefinitionMap 中查找。依赖注入底层也是通过依赖查找获取 bean 的,也要执行下面这块代码,调用时机是处理依赖查找候选bean findAutowireCandidates()
DefaultSingletonBeanRegistry.java

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 查找外部单例对象user
    Object singletonObject = this.singletonObjects.get(beanName);
    // 跳过
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// ...
    }
    return singletonObject;
}

// 补充: 这里的单例是什么意思?经测试,两个 user 对象都可以注册到容器,只要 bean 名称不同即可

这个 singleton 和 spring 的 scope 里面的单例是一回事

2. Spring 内部单例对象

  1. 通过 7.1 章节我们知道 Spring 内建单例对象有 environment,systemProperties 等,下面的例子我们依赖注入Spring 内建单例对象,也可以通过依赖查找 Spring 内建单例对象
public class SingletonDependencySourceDemo {

    @Autowired
    private Environment environment;

    // 注入spring内建单例对象,
    // 将字段名称作为 bean 名称去容器 singletonObjects 属性中查找
    @Autowired
    private Map systemProperties;

    @Autowired
    private Map systemEnvironment;

    @Autowired
    private MessageSource messageSource;

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

        SingletonDependencySourceDemo demo = applicationContext.getBean(SingletonDependencySourceDemo.class);
        System.out.println(demo.environment);
        System.out.println(demo.systemProperties.get("java.runtime.name"));
        System.out.println(demo.systemEnvironment.get("JAVA_HOME"));
        System.out.println(demo.messageSource);

        // 依赖查找
        Environment environment = applicationContext.getBean(Environment.class);
        System.out.println(environment==demo.environment);
    }
}

输出结果

StandardEnvironment {activeProfiles=[], defaultProfiles=[default],...}
Java(TM) SE Runtime Environment
C:\local\Java\jdk1.8.0_201
Empty MessageSource
true
  1. Spring 框架在应用上下文启动时refresh()会调用下面这几个方法,注册 6 个内建单例 bean,源码如下:
AbstractApplicationContext.java

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// ....

    // 注册内建environment bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    // 注册内建systemProperties bean
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    // 注册内建systemEnvironment bean
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

// 注册内建bean messageSource
protected void initMessageSource() {}

// 注册内建bean applicationEventMulticaster
protected void initApplicationEventMulticaster() {}

// 注册内建bean lifecycleProcessor
protected void initLifecycleProcessor() {}

  1. 具体注册到容器的代码与上一小节 7.7.1 SingletonBeanRegistry#RegisterSingleton 相同,依赖注入或依赖查找 Spring 内建单例 bean 的源码与上一小节 7.7.1 DefaultSingletonBeanRegistry#getSingleton 相同

7.6 游离对象作为依赖来源

  • 注册:ConfigurableListableBeanFactory#registerResolvableDependency
  • 无生命周期管理
  • 无法实现延迟初始化 bean
  • 可以依赖注入,无法通过依赖查找
  • 处理依赖:DefaultListableBeanFactory#doResolveDependency -> findAutowireCandidates

1. 外部游离对象

  1. 手动注册游离对象到容器,ConfigurableListableBeanFactory#registerResolvableDependency
public class ResolvableDependencySourceDemo {
    @Autowired
    private String value;

    @PostConstruct
    public void init() {
        System.out.println("依赖注入成功, value: " + value);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(ResolvableDependencySourceDemo.class);

        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        // 手动注册游离对象
        beanFactory.registerResolvableDependency(String.class, "hello world");
        // 启动上下文
        applicationContext.refresh();

        try {
            applicationContext.getBean(String.class);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("容器中没有找到 String 类型的 bean, 说明 ResolvableDependency 对象无法进行依赖查找");
        }
    }
}

输出结果:

依赖注入成功, value: hello world
容器中没有找到 String 类型的 bean, 说明 ResolvableDependency 对象无法进行依赖查找
  1. 源码分析,DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory 接口的 registerResolvableDependency 方法,负责将游离对象注册到容器中,保存到容器的resolvableDependencies属性中,与 Spring 内建游离对象一样
DefaultListableBeanFactory.java

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

public void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue) {
    Assert.notNull(dependencyType, "Dependency type must not be null");
    if (autowiredValue != null) {
        // 判断游离对象autowiredValue是不是指定的类型dependencyType
        // 判断游离对象是不是ObjectFactory类型
        if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
            throw new IllegalArgumentException("Value [" + autowiredValue +
                                               "] does not implement specified dependency type [" + dependencyType.getName() + "]");
        }
        // 将游离对象保存到resolvableDependencies集合中
        this.resolvableDependencies.put(dependencyType, autowiredValue);
    }
}
  1. 游离对象如何进行依赖注入,以及 Spring 框架默认注册的游离对象和注册时机,见7.2章节

7.7 外部配置作为依赖来源

  • 使用 @Value 将外部配置作为依赖来源
  • 处理外部化配置的依赖:DefaultListableBeanFactory#doResolveDependency
  • 非常规的 Spring 对象依赖来源
  • 无生命周期管理
  • 无法实现延迟初始化
  • 无法通过依赖查找
  1. 首先创建一个 default.properties 文件,作为外部化配置
user.id = 22
user.name=巴蒂斯特
hero.name=巴蒂斯特
user.resource = aaa
  1. 使用 @Value 注解依赖注入外部化配置,使用 @PropertySource 指定配置文件
@Configuration  // 这个是必须的
@PropertySource("default.properties")
public class ExternalConfigurationDependencySourceDemo {

    // 设置默认值为-1
    @Value("${user.id:-1}")
    private Long id;

    // 这里会注入优先级更高的系统属性,即当前操作系统用户的名称
    @Value("${user.name}")
    private String name;

    @Value("${hero.name}")
    private String heroName;
    
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(ExternalConfigurationDependencySourceDemo.class);

        // 启动上下文
        applicationContext.refresh();
        ExternalConfigurationDependencySourceDemo bean = applicationContext.getBean(ExternalConfigurationDependencySourceDemo.class);
        System.out.println("user.id: " + bean.id);
        System.out.println("user.name: " + bean.name);
        System.out.println("hero.name: " + bean.heroName);
    }
}

输出结果

user.id: 22
user.name: mao		# 这里会注入优先级更高的系统属性, 即当前操作系统用户名称
hero.name: 巴蒂斯特
  1. 源码分析,处理依赖的逻辑依然在 DefaultListableBeanFactory#doResolveDependency 中,与处理 BeanDefinition 和 游离对象作为依赖来源的方法是同一个,与 @Autowired 的处理类相同都是 AutowireAnnotationBeanPostProcessor
DefaultListableBeanFactory.java

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                  Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    Object shortcut = descriptor.resolveShortcut(this);
    if (shortcut != null) {
        return shortcut;
    }

    // 获取要注入的bean类型
    Class<?> type = descriptor.getDependencyType();
    // 6.14 章节跳过了这部分, 因为这里是处理 @Value注解的
    // 查找@Value 的信息, 这里是${user.id}
    // 见下方代码块
    Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    if (value != null) {
        if (value instanceof String) {
            // 解析配置文件中的user.id, 返回"22" 
            String strVal = resolveEmbeddedValue((String) value);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                                 getMergedBeanDefinition(beanName) : null);
            value = evaluateBeanDefinitionString(strVal, bd);
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
            // 将"22"转为Long类型的22L
            // 将value转为type类型, 若类型不匹配抛出TypeMismatchException
            // 详细见类型转换章节
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        }
        catch (UnsupportedOperationException ex) {
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }
    }
    // 处理集合类型的依赖
    Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    if (multipleBeans != null) {
        return multipleBeans;
    }
    // 处理单个类型的依赖
    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

    //....
}
public Object getSuggestedValue(DependencyDescriptor descriptor) {
    // 查找@Value注解的值, 这里是${user.id}
    Object value = findValue(descriptor.getAnnotations());
    // 跳过, 
    if (value == null) {
        MethodParameter methodParam = descriptor.getMethodParameter();
        if (methodParam != null) {
            value = findValue(methodParam.getMethodAnnotations());
        }
    }
    return value;
}

7.8 面试题

  1. 依赖注入和依赖查找的来源有哪些,是否相同?

    答:依赖查找的来源有 2 处:

    • BeanDefinition,外部包括 xml 配置,@Bean 配置,@BeanDefinitionBuilder 手动注册,Spring 内建的 BeanDefinition

    • 单例对象,Spring 内建的单例对象,外部手动注册的单例对象

    依赖注入的来源相比依赖查找多了 2 处:

    • ResolveDependency 游离对象,包括 Spring 内建的和外部手动注册的
    • 外部配置使用 @Value 依赖注入
  2. 单例对象能在 IoC 容器启动后注册吗?

    答:可以,单例对象的注册与 BeanDefinition 不同,BeanDefinition 会被 ConfigurableListableBeanFactory#freezeConfiguration 方法影响,从而冻结注册,单例对象则没有这个限制。

  3. Spring 依赖注入的来源有哪些?

    答:1. Spring BeanDefinition,外部 xml 配置,@Bean,BeanDefinition 手动注册,Spring 内建的BeanDefinition。

     2. 单例对象,外部手动注册的,Spring 内建的单例对象,如 Environment
     3. 游离对象 Resolvable Dependency,如 BeanFactory
     4. @Value 外部配置