5. 依赖查找

5.1 依赖查找前世今生

  • 单一类型依赖查找

    • JNDI ——javax.naming.Context#lookup(javax.naming.Name)

    • JavaBeans—— java.beans.beancontext.BeanContext

  • 集合类型依赖查找

    • java.beans.beancontext.BeanContext

  • 层次依赖查找

    • java.beans.beancontext.BeanContext

public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility {
    // ...
}

java.beans.beancontext.BeanContext 针对 GUI 程序以及普通计算程序,Spring 的实现很大程度上参考了其实现,其实现了 Collection 接口,因此其内部所有成员都是 Bean,BeanContext 负责对其进行 crud 操作

5.2 单一类型依赖查找

Spring 中单一类型依赖查找依赖 BeanFactory 接口,可以根据名称、类型(实时和延迟查找)、名称+类型进行查找。

延迟查找举例如下:

public class ObjectProviderDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ObjectProviderDemo.class);
​
        lookupByObjectProvider(context);
​
        context.close();
    }
​
    @Bean
    public String helloWorld() {
        return "Hello World";
    }
​
    private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) {
        ObjectProvider beanProvider = context.getBeanProvider(String.class);
        System.out.println(beanProvider.getObject());
    }
}

5.3 集合类型依赖查找(ListableBeanFactory)

Spring 中集合类型依赖查找依赖 ListableBeanFactory 接口,查找方式如下:

  • 根据 Bean 类型查找

    • 获取同类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化

      • getBeanNamesForType(Class)

      • Spring 4.2 getBeanNamesForType(ResolvableType)

    • 获取同类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)

      • getBeanOfType(Class)以及重载方法

  • 根据注解查找

    • Spring 3.0 获取标注类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化

      • getBeanNamesForAnnotation(Class)

    • Spring 3.0 获取标注类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)

      • getBeansWithAnnotation(Class)

    • Spring 3.0 获取指定名称+标注类型 Bean 实例(Bean 已经实例化或者会触发 Bean 的实例化)

      • findAnnotationOnBean(String, Class)

5.4 层次性依赖查找

层次型依赖查找依赖 HierarchicalBeanFactory 接口

  • 父 BeanFactory:getParentBenaFactory

  • 层次性查找

    • 根据 Bean 名称查找

      • 基于 containsLocalBean 方法实现(当前 BeanFactory 查找,不包含父类 BeanFactory)

    • 根据 Bean 类型查找实例列表

      • 单一类型:BeanFactoryUtils#beanOfType

      • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors

    • 根据 Java 注解查找名称列表

      • BeanFactoryUtils#beanNamesForTypeIncludingAncestors

真正使用时通过使用 ConfigurableListableBeanFactory 来使用,接口间继承关系如下:

// 父子级联容器接口(类似双亲委派,优先在父容器中查找,找不到才在子容器中查找)
public interface HierarchicalBeanFactory extends BeanFactory {
​
    /**
     * Return the parent bean factory, or {@code null} if there is none.
     */
    @Nullable
    BeanFactory getParentBeanFactory();
​
    /**
     * Return whether the local bean factory contains a bean of the given name,
     * ignoring beans defined in ancestor contexts.
     * 

This is an alternative to {@code containsBean}, ignoring a bean * of the given name from an ancestor bean factory. * @param name the name of the bean to query * @return whether a bean with the given name is defined in the local factory * @see BeanFactory#containsBean */ boolean containsLocalBean(String name); ​ } ​ // 可配置容器接口,包含父子级联功能以及单例注册功能,自己新增的功能有配置BeanFacory的属性,比如BeanPostProcessor、PropertyEditor等 public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {    // ... } ​ // 可配置容器接口,包含集合容器功能、自动注入(非Spring管理的Bean实现依赖注入,被注入的对象必须是Spring容器内的对象)、可配置容器接口(父子级联、单例注册) public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {    // ... }

根据名称查找举例:

public class HierarchicalDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(HierarchicalDependencyLookupDemo.class);
​
        // 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory factory = context.getBeanFactory();
        System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory());
​
        // 添加 parentBeanFactory
        HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
        factory.setParentBeanFactory(parentBeanFactory);
        System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory());
​
        displayContainsBean(factory, "user");
        displayLocalBean(factory, "user");
        displayLocalBean(parentBeanFactory, "user");
​
        // 启动上下文
        context.refresh();
​
        // 关闭上下文
        context.close();
    }
​
    // 创建父容器
    private static HierarchicalBeanFactory createParentBeanFactory() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
        return factory;
    }
​
    private static void displayLocalBean(HierarchicalBeanFactory factory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name: %s] : %s\n", factory, beanName, factory.containsLocalBean(beanName));
    }
​
    private static void displayContainsBean(HierarchicalBeanFactory factory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name: %s] : %s\n", factory, beanName, containsBean(factory, beanName));
    }
​
    // 类似双亲委派,优先从父容器中查找,递归查找
    private static boolean containsBean(HierarchicalBeanFactory factory, String beanName) {
        BeanFactory parentBeanFactory = factory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory;
            if (containsBean(hierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return factory.containsLocalBean(beanName);
    }
}
​
// 输出结果:
当前 BeanFactory 的 parent :null
    
当前 BeanFactory 的 parent :org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: 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,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Bean[name: user] : true
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: 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,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Local Bean[name: user] : false
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy] 是否包含 Local Bean[name: user] : true

5.5 延迟依赖查找

延迟查找依赖的接口:

  • org.springframework.beans.factory.ObjectFactory

  • org.springframework.beans.factory.ObjectProvider

    • Spring 5 对 Java 8 特性扩展

      • 函数式接口

        • getIfAvailable(Supplier)

        • ifAvailable(Consumer)

      • Stream 扩展——stream()

思考:非延迟初始化的 bean 是否能够实现延迟查找?

public class ObjectProviderDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ObjectProviderDemo.class);
        context.refresh();
​
        lookupByObjectProvider(context);
        lookupIfAvailable(context);
        lookupByStreamOps(context);
​
        context.close();
    }
​
    private static void lookupByStreamOps(AnnotationConfigApplicationContext context) {
        ObjectProvider provider = context.getBeanProvider(String.class);
        provider.stream().forEach(System.out::println);
    }
​
    private static void lookupIfAvailable(AnnotationConfigApplicationContext context) {
        ObjectProvider beanProvider = context.getBeanProvider(User.class);
        User user = beanProvider.getIfAvailable(User::user);
        System.out.println("当前 User 对象:" + user);
    }
​
    @Bean
    @Primary
    public String helloWorld() {
        return "Hello World";
    }
​
    @Bean
    public String message() {
        return "Message";
    }
​
    private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) {
        ObjectProvider beanProvider = context.getBeanProvider(String.class);
        System.out.println(beanProvider.getObject());
    }
}

5.6 安全依赖查找

依赖查找安全性对比:

依赖查找类型 代表实现 是否安全
单一类型查找 BeanFactory#getBean
ObjectFactory#getObject
ObjectProvider#getIfAvailable
集合类型查找 ListableBeanFactory#getBeansOfType
ObjectProvider#stream

安全与否在于调用方法进行依赖查找时是否会触发异常。

层次类型依赖查找安全性依赖于单一或者集合类型依赖查找安全性

推荐使用 ObjectProvider 进行依赖查找,因为可以实现单一和集合类型查找,并且查找是安全的

5.7 内建可查找依赖

AbstractApplicationContext 内建可查找的依赖

Bean名称 Bean 实例 使用场景
environment Environment 对象 外部化配置以及 Profiles
SystemProperties java.util.Properties 对象 Java 系统属性
systemEnvironment java.util.Map 对象 操作系统环境变量
messageSource MessageSource 对象 国际化文案
lifecycleProcessor LifecycleProcessor 对象 Lifecycle Bean 处理器
applicationEventMulticaster ApplicationEventMulticaster 对象 Spring 事件广播器

注解驱动 Spring 应用上下文内建可查找依赖(部分)(componentScan或者使用ApplicaitonContext激活这些内部 Bean)(名称很长也比较难记,可以在AnnotationConfigUtils类中进行查找)

Bean 名称 Bean 实例 使用场景
org.springframework.context.annotation.internalConfigurationClassPostProcessor(BeanFactory生命周期) ConfigurationClassPostProcessor对象 处理 Spring 配置类
org.springframework.context.annotation.internalAutoWiredAnnotationBeanPostProcessor AutoWiredAnnotationBeanPostProcessor 对象 处理 @Autowired 以及 @ Value 注解
org.springframework.context.annotation.internalCommonAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor 对象 (条件激活)处理 JSR-250 注解,如 @PostConstructor 等
org.springframework.context.annotation.internalEventListenerMethodProcessor EventListenerMethodProcessor 对象 处理标注 @EventListener 的 Spring 事件监听方法
org.springframework.context.annotation.internalDefaultEventListenerFactory DefaultEventListenerFactory @EventListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation.internalPersistenceAnnotationBeanPostProcessor PersistenceAnnotationBeanPostProcessor (条件激活)处理 JPA 注解场景

5.8 依赖查找经典异常

BeansException 子类型(非 check 异常,Runtime 异常)

异常类型 触发条件(举例) 场景举例
NoSuchBeanDefinitionException 当查找 Bean 不存在于 IoC 容器时 BeanFactory#getBean ObjectFactory#getObject
NoUniqueBeanDefinitionException 类型依赖查找时,IoC 容器存在多个 Bean 实例 BeanFactory#getBean
BeanInstantiationException 当 Bean 所对应的类型非具体类型时 BeanFactory#getBean
BeanCreationException 当 Bean 初始化过程出错 Bean 初始化方法执行异常时
BeanDefinitionStoreException 当 BeanDefinition 配置元信息非法时 XML 配置资源无法打开时

5.9 面试题

5.9.1 ObjectFactory 和 BeanFactory 区别

ObjectFactory 是 Spring 比较早期的接口,ObjectFactory 和 BeanFactory 均提供依赖查找功能,不同点:

  • BeanFactory 仅关注一个或一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力由 BeanFactory 支撑

  • BeanFactory 提供单一类型、集合类型以及层次性等多种依赖查找功能

从实用也可以看出:


    
public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean> {
​
    @Nullable
    private String targetBeanName;
​
​
    /**
     * Set the name of the target bean.
     * 

The target does not have to be a non-singleton bean, but realistically * always will be (because if the target bean were a singleton, then said singleton * bean could simply be injected straight into the dependent object, thus obviating * the need for the extra level of indirection afforded by this factory approach). */ public void setTargetBeanName(String targetBeanName) { this.targetBeanName = targetBeanName; } ​ @Override public void afterPropertiesSet() throws Exception { Assert.hasText(this.targetBeanName, "Property 'targetBeanName' is required"); super.afterPropertiesSet(); } ​ ​ @Override public Class getObjectType() { return ObjectFactory.class; } ​ @Override protected ObjectFactory createInstance() { BeanFactory beanFactory = getBeanFactory(); Assert.state(beanFactory != null, "No BeanFactory available"); Assert.state(this.targetBeanName != null, "No target bean name specified"); return new TargetBeanObjectFactory(beanFactory, this.targetBeanName); } ​ ​ /** * Independent inner class - for serialization purposes. * 此处生成 ObjectFactory 对象,当通过 Spring 容器获取对象时,其实获取的是 ObjectFactory#getObject方法返回的对象 * 此处是从容器中获取对象的 */ @SuppressWarnings("serial") private static class TargetBeanObjectFactory implements ObjectFactory, Serializable { ​ private final BeanFactory beanFactory; ​ private final String targetBeanName; ​ public TargetBeanObjectFactory(BeanFactory beanFactory, String targetBeanName) { this.beanFactory = beanFactory; this.targetBeanName = targetBeanName; } ​ @Override public Object getObject() throws BeansException {            // 从 Spring 容器中根据 name 获取 bean 返回 return this.beanFactory.getBean(this.targetBeanName); } } ​ }

5.9.2 BeanFactory#getBean 方法是否线程安全

是线程安全的,操作过程中会通过 synchronized 加锁,jdk 5 update 6 中新增偏向锁功能,该功能可以大大降低 synchronized 关键字的重量,在只有一个线程访问时,可以将只看为没有锁(只有加锁时会执行一个 CAS 操作将锁偏向当前线程,后续该线程运行到加锁位置只会比较偏向锁线程是否为当前线程,是的话就通过 CAS 替换一次 lock record,后续不再加锁),因此不建议将启动过程放在子线程执行,就在 main 主线程执行性能是最好的

5.9.4 Spring 的依赖查找和依赖注入在来源上有什么区别

依赖查找只能查询容器中有的 Bean(手动注入或者框架内建),但是依赖注入除了容器中有的 Bean 以外,还支持注入非 Bean 对象(内建依赖)

你可能感兴趣的:(Spring,spring,java)