7.1 依赖查找来源

7.1.1 来源

来源

配置元数据

Spring DeanDefinition

<bean id = "user" class="com.gcl.bean.User"/>

@Bean public User user {...}

BeanDefinitionBuilder

单例对象

API实现

7.1.2 Spring 内建 BeanDefinition 以及内建单例对象

详情见 5.7 小结

如何通过 XML 开启注解功能?首先注解功能是由 AnnotationConfigUtils#registerAnnotationConfigProcessors 方法注册启动的,而该方法在 AnnotationConfigBeanDefinitionParser、ComponentScanBeanDefinitionParser、AnnotationBeanDefinitionReader、ClasspathBeanDefinitionScanner 中被调用,启动注解方式有两种:
 
<context:annotation-config/>
<!-- 不存在的路径也可以,只要有该元素即可 -->
<context:component-scan base-package="xxx"/>


7.2 依赖注入来源

来源

配置元数据

Spring DeanDefinition

<bean id = "user" class="com.gcl.bean.User"/>

@Bean public User user {...}

BeanDefinitionBuilder

单例对象

API实现

非 Spring 容器管理对象

public class DependencySourceDemo {

    // 注入在 postProcessorProperties 方法执行,早于 Setter 注入,也早于 @PostConstructor
    // 这四个对象在 AbstractApplicationContext 中进行注册,但是无法从容器中获取
    @Resource
    private BeanFactory factory;
    @Resource
    private ResourceLoader loader;
    @Resource
    private ApplicationContext context;
    @Resource
    private ApplicationEventPublisher publisher;

    @PostConstruct
    public void init() {
        System.out.println("beanFactory == applicationContext : " + (factory == context));
        System.out.println("beanFactory == applicationContext.getBeanFactory : " + (factory == context.getAutowireCapableBeanFactory()));
        System.out.println("loader == applicationContext : " + (loader == context));
        System.out.println("publisher == applicationContext : " + (publisher == context));
    }

    @PostConstruct
    public void getBean() {
        getBean(BeanFactory.class);
        getBean(ApplicationEventPublisher.class);
        getBean(ApplicationContext.class);
        getBean(ResourceLoader.class);
    }

    private <T> T getBean(Class<T> type) {
        try {
            return factory.getBean(type);
        } catch (NoSuchBeanDefinitionException e) {
            System.err.println("当前类型" + type.getName() + "无法在 BeanFactory 中查找到!");
        }
        return null;
    }

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

        context.refresh();

        System.out.println(context.getBean(User.class));

        context.close();
    }

    @Bean
    public User user() {
        User user = new User();
        user.setName("foo");
        user.setAge(18);
        return user;
    }
}

// 运行结果:
beanFactory == applicationContext : false
beanFactory == applicationContext.getBeanFactory : true
loader == applicationContext : true
publisher == applicationContext : true
当前类型org.springframework.beans.factory.BeanFactory无法在 BeanFactory 中查找到!
当前类型org.springframework.context.ApplicationEventPublisher无法在 BeanFactory 中查找到!
当前类型org.springframework.context.ApplicationContext无法在 BeanFactory 中查找到!
当前类型org.springframework.core.io.ResourceLoader无法在 BeanFactory 中查找到!
User{name='foo', age=18}


protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

    // ...
    
    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // ...
}


7.3 Spring 容器管理和游离对象

来源

Spring Bean 对象

生命周期管理

配置元信息

使用场景

Spring BeanDefinition




依赖查找、依赖注入

单体对象




依赖查找、依赖注入

Resolvable Dependency(游离对象)




依赖注入

7.4 Spring BeanDefinition 作为依赖来源

  • 元数据:BeanDefinition
  • 注册:BeanDefinitionRegistry#registerBeanDefinition
  • 类型:延迟和非延迟
  • 顺序:Bean 生命周期顺序按照注册顺序

注册时会使用 Map 保存 name 和 BeanDefiniton 的映射,并且还会用一个 ArrayList 保存 name 顺序(Map无序),供后续实例化使用,并且需要注意在 Springboot 2.1 以后不允许注入同名 bean

7.5 单例对象作为依赖来源

  • 要素
  • 来源:外部普通 Java 对象(不一定是 POJO)
  • 注册:SingletonBeanRegistry#registerSingleton
  • 限制
  • 无生命周期管理
  • 无法实现延迟初始化 Bean
public class SingletonBeanRegistrationDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 创建外部单体对象
        DefaultUserFactory factory = new DefaultUserFactory();
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册外部单体对象
        beanFactory.registerSingleton("userFactory", factory);
        context.refresh();
        // 依赖查找
        UserFactory factory1 = context.getBean(UserFactory.class);
        System.out.println(factory == factory1);
    }
}


7.6 非 Spring 容器管理对象作为依赖来源

  • 要素
  • 注册:ConfigurableListableBeanFactory#registerResolvableDependency
  • 限制
  • 无生命周期管理
  • 无法实现延迟初始化
  • 无法通过依赖查找
public class ResolvableDependencySourceDemo {

    @Resource
    private String value;

    @Resource
    private BeanFactory factory;

    @PostConstruct
    public void init() {
        System.out.println(value);
        try {
            factory.getBean(String.class);
        } catch (Exception e) {
            System.out.println("err");
        }
    }


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        ConfigurableListableBeanFactory factory = context.getBeanFactory();
        factory.registerResolvableDependency(String.class, "Hello world");

        context.register(ResolvableDependencySourceDemo.class);

        context.refresh();

        context.close();
    }
}


7.7 外部化配置作为依赖来源(@Value)

  • 要素
  • 类型:非常规 Spring 对象依赖来源
  • 限制
  • 无生命周期管理
  • 无法实现延迟初始化 Bean
  • 无法通过依赖查找
@Configuration
@PropertySource(value = "default.properties", encoding = "UTF-8")
public class ExternalConfigurationDependencySourceDemo {

    @Value("${usr.name:text}")
    private String name;

    @Value("${user.age:18}")
    private int age;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ExternalConfigurationDependencySourceDemo.class);
        context.refresh();
        ExternalConfigurationDependencySourceDemo demo = context.getBean(ExternalConfigurationDependencySourceDemo.class);
        System.out.println(demo.name);
        System.out.println(demo.age);
        context.close();
    }
}


7.8 面试题

7.8.1 依赖注入和依赖查找来源是否相同

不同,依赖查找来源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的来源还包括 Resolvable Dependency 以及 @Value 所标注的外部化配置

7.8.2 单例对象能在 IoC 容器启动后注册吗?

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

7.8.3 Spring 依赖注入有哪些来源

Spring BeanDefinition

单例对象

Resolvable Dependency

@Value 外部化配置