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<String> 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<? extends Annotation>)

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

      • getBeansWithAnnotation(Class<? extends Annotation>)

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

      • findAnnotationOnBean(String, Class<? extends Annotation>)

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.
     * <p>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<String> provider = context.getBeanProvider(String.class);
        provider.stream().forEach(System.out::println);
    }
​
    private static void lookupIfAvailable(AnnotationConfigApplicationContext context) {
        ObjectProvider<User> 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<String> 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 实例使用场景
environmentEnvironment 对象外部化配置以及 Profiles
SystemPropertiesjava.util.Properties 对象Java 系统属性
systemEnvironmentjava.util.Map 对象操作系统环境变量
messageSourceMessageSource 对象国际化文案
lifecycleProcessorLifecycleProcessor 对象Lifecycle Bean 处理器
applicationEventMulticasterApplicationEventMulticaster 对象Spring 事件广播器

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

Bean 名称Bean 实例使用场景
org.springframework.context.annotation.internalConfigurationClassPostProcessor(BeanFactory生命周期)ConfigurationClassPostProcessor对象处理 Spring 配置类
org.springframework.context.annotation.internalAutoWiredAnnotationBeanPostProcessorAutoWiredAnnotationBeanPostProcessor 对象处理 @Autowired 以及 @ Value 注解
org.springframework.context.annotation.internalCommonAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 对象(条件激活)处理 JSR-250 注解,如 @PostConstructor 等
org.springframework.context.annotation.internalEventListenerMethodProcessorEventListenerMethodProcessor 对象处理标注 @EventListener 的 Spring 事件监听方法
org.springframework.context.annotation.internalDefaultEventListenerFactoryDefaultEventListenerFactory@EventListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation.internalPersistenceAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor(条件激活)处理 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 提供单一类型、集合类型以及层次性等多种依赖查找功能

从实用也可以看出:

<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="user"/>
</bean>
public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean<ObjectFactory<Object>> {
​
    @Nullable
    private String targetBeanName;
​
​
    /**
     * Set the name of the target bean.
     * <p>The target does not <i>have</i> 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<Object> 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<Object>, 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 对象(内建依赖)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值