IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring IoC 依赖查找的那些事儿 -> 正文阅读

[Java知识库]Spring IoC 依赖查找的那些事儿

依赖查找的前生

单一类型依赖查找

JNDI- javax.naming.Context#lookup(javax.naming.Name);

JNDI- javax.naming.Context#lookup(String);

JavaBeans - java.beans.beancontext.BeanContext

BeanContext 继承了 Collection 接口,BeanContext 中的所有成员都是 bean

集合类型依赖查找

通过一个 key,找到对应的多个实例

java.beans.beancontext.BeanContextServices 的 Iterator getCurrentServiceSelectors(Class serviceClass);

层级性依赖查找

img

单一类型依赖查找

用到了 BeanFactory接口

根据 Bean 名称查找

getBean(String)

Spring 2.5 覆盖默认参数:getBean(String,Object…)

根据 Bean 类型查找

Bean 实时查找

Spring 3.0 getBean(Class)

Spring 4.1 覆盖默认参数:getBean(Class,Object…)

Spring 5.1 Bean 延时查找

getBeanProvider(Class)

getBeanProvider(ResolvableType)

相关代码:

/**
 * 通过 ObjectProvider 依赖查找
 *
 * @author mindartisan.blog.csdn.net
 * @date
 *
 * @commet @Bean 定义的类在当前类,当前类默认就是配置类,不用写 「@Configuration」注解,
 *
 * @Configuration 非必需注解:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
 */
public class ObjectProviderDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();
        lookupByObjectProvider(applicationContext);

        applicationContext.close();
    }

    @Bean
    // 此时的 Bean 名称就是方法名称
    public String helloWorld() {
        return "Hello,World";
    }

    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }
}

根据 Bean 名称+类型查找

getBean(String,Class)

集合类型依赖查找

用到了ListableBeanFactory 接口,推荐使用 bean 名称判断 bean 是否存在。

根据 Bean 类型查找

Bean 名称可以先定义,但是实例必须得初始化

获取同类型 Bean 名称列表

getBeanNamesForType(Class)

Spring 4.2 getBeanNameForType(ResolvableType)

获取同类型的 Bean 实例列表

getBeansOfType(Class) 及其重载方法

根据注解类型查找

Spring 3.0 获取标注类型 Bean 名称列表

getBeanNamesForAnnotation(Class<?extends Annotation>)

Spring 3.0 获取标注类型 Bean 实例列表

getBeansWithAnnotation(Class<?extends Annotation>)

Spring 3.0 获取指定名称+标注类型 Bean 实例

findAnnotationOnBean(String,Class<?extends Annotation>)

层次性依赖查找

用到了HierarchicalBeanFactory接口,方法为 getParentBeanFactory()

注意下图中 ConfigurableListBeanFactory 继承 ConfigurableBeanFactory 以及 ListableBeanFactory 接口。

img

根据 Bean 名称查找

基于 containsLocalBean()实现

根据 Bean 类型查找实例列表

单一类型:使用BeanFactoryUtils#beanOfTypeIncludingAncestors

集合类型:使用BeanFactoryUtils#beansOfTypelncludingAncestors

相关代码:

HierarchicalDependencyLookupDemo.class

/**
 * 层次性依赖查找示例
 *
 * @author mindartisan.blog.csdn.net
 * @date
 */
public class HierarchicalDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类,此处不用也行,因为当前类里没有 @Bean
        applicationContext.register(HierarchicalDependencyLookupDemo.class);

        // 1. 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListBeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        System.out.println("当前 BeanFactory 的 parent BeanFactory:" + beanFactory.getParentBeanFactory());

        // 2. 设置 parent BeanFactory
        ConfigurableListableBeanFactory parentBeanFactory = createParentBeanFactory();
        beanFactory.setParentBeanFactory(parentBeanFactory);
        System.out.println("当前 BeanFactory 的 parent BeanFactory:" + beanFactory.getParentBeanFactory());

        System.out.println("------------------------");

        displayContainsLocalBean(beanFactory,"user");
        displayContainsLocalBean(parentBeanFactory,"user");

        displayContainsBean(beanFactory,"user");
        displayContainsBean(parentBeanFactory,"user");

        // 启动应用上下文
        applicationContext.refresh();

        applicationContext.close();
    }

    private static void displayContainsBean(HierarchicalBeanFactory beanFactory,String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name: %s] : %s\n", beanFactory, beanName,
                containsBean(beanFactory, beanName));
    }

    private static boolean containsBean(HierarchicalBeanFactory beanFactory,String beanName) {
        BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
            if (containsBean(hierarchicalBeanFactory, beanName)) {
                return true;
            }
        }

        return beanFactory.containsLocalBean(beanName);
    }

    private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 LocalBean[name: %s] : %s\n", beanFactory, beanName,
                beanFactory.containsLocalBean(beanName));
    }

    private static ConfigurableListableBeanFactory createParentBeanFactory() {
        // 创建 BeanFactory 容器
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();

        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
        String location = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加载配置
        reader.loadBeanDefinitions(location);
        return defaultListableBeanFactory;
    }
}

注意 pom.xml 的依赖要「 复用 ioc-container-overview 」

根据 Java 注解查找名称列表

使用 BeanFactoryUtils#beanNamesForTypeIncludingAncestors

延迟依赖查找

通过 org.springframework.beans.factory.ObjectFactory接口以及org.springframework.beans.factory.ObjectProvider接口实现,后者继承了前者,可以调用前者的getObject()获取当前关联的 bean。

img

其中使用到了 Java 8 的新特性:

  1. 函数式接口
  2. Stream

相关代码:

ObjectProviderDemo.class

/**
 * 通过 ObjectProvider 依赖查找
 *
 * @author mindartisan.blog.csdn.net
 * @date
 *
 * @commet @Bean 定义的类在当前类,当前类默认就是配置类,不用写 「@Configuration」注解,
 *
 * @Configuration 非必需注解:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
 */
public class ObjectProviderDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();
        lookupByObjectProvider(applicationContext);
        lookupIfAvaiable(applicationContext);
        lookupByStreamOps(applicationContext);

        applicationContext.close();
    }

    private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        // Iterable<String> stringIterable = objectProvider;
        // for (String string : stringIterable) {
        //     System.out.println(string);
        // }
        objectProvider.stream().forEach(System.out::println);

    }

    private static void lookupIfAvaiable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        // 这里演示的是如果 User 不存在的话,进行兜底:
        // 此时「getIfAvailable(User::createUser)」会创建一个新的 User,如果是 getIfAvailable(),则会输出 null
        User user = userObjectProvider.getIfAvailable(User::createUser);
        System.out.println("当前 User 对象:" + user);
    }

    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }

    @Bean
    @Primary
    // 此时的 Bean 名称就是方法名称
    public String helloWorld() {
        return "Hello,World";
    }

    @Bean
    public String message() {
        return "Message";
    }

}

安全依赖查找

此处的安全是指是否抛出异常

单一类型查找

BeanFactory#getBean -> 不安全

ObjectFactory#getObject -> 不安全

ObjectProvider#getIfAvailable -> 安全

集合类型查找

ListableBeanFactory#getBeansOfType -> 安全

ObjectProvider#stream -> 安全

层级类型依赖

依赖于其扩展的单一或者集合类型的接口。

相关代码

TypeSafetyDependencyLookupDemo.class:

**
 * 类型安全依赖查找示例
 *
 * @author mindartisan.blog.csdn.net
 * @date
 */
public class TypeSafetyDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类
        applicationContext.register(TypeSafetyDependencyLookupDemo.class);
        // 启动应用上下文
        applicationContext.refresh();

        // 演示 beanFactory#getBean 安全性
        displayBeanFactoryGetBean(applicationContext);
        // 演示 ObjectFactory#getObject 安全性
        displayObjectFactoryGetBean(applicationContext);
        // 演示 ObjectProvider#getIfAvaiable 方法的安全性
        displayObjectProviderIfAvailable(applicationContext);

        // 演示 ListableBeanFactory#getBeansOfType 方法的安全性
        displayListableBeanFactoryGetBeansOfType(applicationContext);
        // 演示 ObjectProvider Stream 操作的安全性
        displayObjectProviderStreamOps(applicationContext);

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

    /**
     * 演示 ObjectProvider Stream 操作的安全性
     *
     * @param beanFactory bean工厂
     */
    private static void displayObjectProviderStreamOps(BeanFactory beanFactory) {
        // 不抛异常
        ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
        printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.forEach(System.out::println));
    }

    /**
     * 演示 ListableBeanFactory#getBeansOfType 方法的安全性
     *
     * @param beanFactory bean工厂
     */
    private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
        // 不抛异常
        printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
    }

    /**
     * 演示 ObjectProvider#getIfAvaiable 方法的安全性
     *
     * @param beanFactory bean工厂
     */
    private static void displayObjectProviderIfAvailable(BeanFactory beanFactory) {
        // 不抛异常
        ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
        printBeansException("displayObjectProviderIfAvailable", () -> userObjectProvider.getIfAvailable());
    }

    /**
     * 演示 ObjectFactory#getObject 安全性
     *
     * @param beanFactory bean工厂
     */
    private static void displayObjectFactoryGetBean(BeanFactory beanFactory) {
        // 抛出「org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com
        // .mindartisan.spring.geek.ioc.overview.domain.User' 」异常
        ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
        printBeansException("displayObjectFactoryGetBean", userObjectProvider::getObject);
    }

    /**
     * 演示 beanFactory#getBean 安全性
     *
     * @param beanFactory bean工厂
     */
    public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
        // 此时并无 User 对应的 bean,抛出「org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mindartisan.spring.geek.ioc.overview.domain.User' available」异常
        printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
    }

    public static void printBeansException(String source, Runnable runnable) {
        System.err.println("==========================================");
        System.err.println("Source from :" + source);
        try {
            runnable.run();
        } catch (BeansException exception) {
            exception.printStackTrace();
        }
    }
}

内建可查找的依赖

基本上所有的上下文的实现都是基于AbstractApplicationContext此抽象类实现,会在上下文启动的过程中会初始化一些内部的依赖,称为内建的可查询依赖。

AbstractApplicationContext 内建可查找的依赖

Bean 名称Bean 实例使用场景
environmentEnvironment 对象外部化配置:Java 系统的属性,比如 -D 参数Profiles:比如 dev、test
systemPropertiesjava.util.Properties 对象Java 系统属性、系统路径或目录
systemEnvironmentjava.util.Map 对象操作系统的环境变量(主要指当前用户环境变量)
messageSourceMessageSource 对象国际化文案
lifecycleProcessorLifecycleProcessor 对象Lifecycle Bean 处理器(Bean 生命周期)
applicationEventMulticasterApplicationEventMulticaster 对象事件广播

注解驱动 Spring 应用上下文内建可查找的依赖

AnnotationConfigUtils 里的常量

Bean 名称Bean 实例使用场景
org.springframework.context.annotation.internalConfigurationAnnotationProcessorConfigurationClassPostProcessor 对象用来处理 Spring 配置类
org.springframework.context.annotation.internalAutowiredAnnotationProcessorAutowiredAnnotationBeanPostProcessor 对象处理 @Autowired 以及 @Value注解(构造器代码里 add 了上述两个注解)
org.springframework.context.annotation.internalCommonAnnotationProcessorCommonAnnotationBeanPostProcessor 对象(条件激活)处理 JSR-250 注解,如 @PostConstruct 等
org.springframework.context.event.internalEventListenerProcessorEventListenerMethodProcessor 对象处理标注 @EventListener 的Spring 事件监听方法(直接标注在方法上就行,无需实现 ApplicationListener 接口)
org.springframework.context.event.internalEventListenerFactoryDefaultEventListenerFactory 对象@EventListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation.internalPersistenceAnnotationProcessorPersistenceAnnotationBeanPostProcessor 对象(条件激活)处理 JPA 注解场景

依赖查找中的经典异常

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

相关代码:

NoUniqueBeanDefinitionExceptionDemo.class

/**
 * 没有具体的 bean 定义异常示例
 *
 * @author mindartisan.blog.csdn.net
 * @date
 */
public class NoUniqueBeanDefinitionExceptionDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类
        applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
        // 启动应用上下文
        applicationContext.refresh();
        try {
            applicationContext.getBean(String.class);
        } catch (NoUniqueBeanDefinitionException e) {
            System.err.printf("Spring 应用上下文当前存在 %d 个 %s 类型的 Bean,具体原因:%s",
                    e.getNumberOfBeansFound(),
                    String.class.getName(),
                    e.getMessage());
        }

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

    @Bean
    public String bean1() {
        return "bean1";
    }

    @Bean
    public String bean2() {
        return "bean2";
    }

}

BeanInstantiationException.class

/**
 * bean 实例化异常示例
 *
 * @author mindartisan.blog.csdn.net
 * @date
 */
public class BeanInstantiationExceptionDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Character.class);

        // 下面的不会有异常
        // BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(String.class);
        applicationContext.registerBeanDefinition("errorBean",beanDefinitionBuilder.getBeanDefinition());
        // 启动应用上下文
        applicationContext.refresh();

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

BeanCreationExceptionDemo.class

**
 * bean 创建异常示例
 *
 * @author mindartisan.blog.csdn.net
 * @date
 */
public class BeanCreationExceptionDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册 BeanDefinition Bean Class 是一个 POJO 普通类,不过初始化方法回调时抛出异常
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
        applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());

        // 启动应用上下文
        applicationContext.refresh();

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

    static class POJO implements InitializingBean {

        @PostConstruct // CommonAnnotationBeanPostProcessor
        public void init() throws Throwable {
            throw new Throwable("init() : For purposes...");
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            throw new Exception("afterPropertiesSet() : For purposes...");
        }
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:04:09  更:2022-05-05 11:04:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:47:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码