深入挖掘Spring系列 -- 依赖查找背后的细节(上)

简介: 深入挖掘Spring系列 -- 依赖查找背后的细节(上)

依赖查找是什么



使用过Spring框架的同学应该都清楚,Spring会将我们所需要使用到的Bean按照一定规则存储到一个应用上下文中(ApplicationContext)。所谓的依赖查找就是根据规则从Spring容器中获取对应的Bean。


流程有点类似于下图:


网络异常,图片无法展示
|


客户端程序希望获取一个BeanA,那么就向Spring中发起一个请求,从容器中获取对应的BeanA。


其实在Spring发明之前,jdk内部也有类似依赖查找的这种功能。下边我们来看看这几个类的属性。


java.beans.beancontext.BeanContext
java.beans.beancontext.BeanContextServices
复制代码


在这两个类里面存有着和Spring设计类似的一些管理bean的基本接口和功能,这一点上可以看出,其实Spring的依赖查找设计是有对Jdk内部实现做了一定借鉴的。


依赖查找归纳


按照自己的经验和一些资料帮助,下边大体将依赖查找划分为了三种类型:


  • 单一类型查找
  • 集合类型查找
  • 层次类型查找


接下来便是相关代码的实战案例,通过代码实践来深入理解这些依赖查找。


Spring内部的单一类型查找



关于spring内部的单一查找这里我将常见的几种类型给大家罗列出来。


Object getBean(String name) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansExceptio
复制代码


在高版本的Spring中还提供了对于Spring内部Bean的延迟查找功能


<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
复制代码


ResolvableType解释下:因为jdk内部的类不仅仅只有Class类型,还有例如说Type类型,FIeld类型,所以Spring又增加了一种类型参数作为暴露的Api服务。


实践案例:


配置Bean:Pig


package org.idea.spring.look.up.factory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @Author linhao
 * @Date created in 9:26 下午 2021/4/10
 */
@Configuration
public class Config {
    public class Pig{
        int id;
        public Pig() {
            System.out.println("this is pig");
        }
        @Override
        public String toString() {
            return "Pig{" +
                    "id=" + id +
                    '}';
        }
    }
    @Bean(name = "pig")
    public Pig getPig(){
        return new Pig();
    }
}
复制代码


测试入口:


package org.idea.spring.look.up;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
/**
 * @Author linhao
 * @Date created in 9:02 下午 2021/4/10
 */
public class SpringSingleLookUpDemo {
    @Bean
    public String HelloWorld(){ //bean 注解里面的name或者value如果没有定义的话,这里默认就是 "helloWorld"
        System.out.println("THIS IS INIT");
        return "Hello.World";
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext();
        //此时相当于声明当前这个类是一个配置类,所以不需要进行额外注解的声明
        applicationContext.register(SpringSingleLookUpDemo.class);
        applicationContext.refresh();
        //单一查找的一种实现方式
        String bean = (String) applicationContext.getBean("Pig");
        System.out.println(bean);
        //spring 4.1 高版本中的一种实现方式 这里只是支持单一查找类型
        ObjectProvider<Config.Pig> objectProvider = applicationContext.getBeanProvider(Config.Pig.class);
        System.out.println(objectProvider.getObject());
        applicationContext.close();
    }
}
复制代码


Spring内部的集合类型查找



根据 Bean 类型查找

获取同类型 Bean 名称列表


String[] getBeanNamesForType(@Nullable Class<?> type);
String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
复制代码


获取同类型 Bean 实例列表


getBeansOfType(Class) 以及重载方法 //不建议使用
通过注解类型查找
//Spring 3.0 - 获取标注类型 Bean 名称列表
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
//Spring 3.0 - 获取标注类型 Bean 实例列表
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
//Spring 3.0 - 获取指定名称+标注类型 Bean 实例
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
      throws NoSuchBeanDefinitionException;
复制代码


实验案例:


package org.idea.spring.look.up;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * @Author linhao
 * @Date created in 10:03 下午 2021/4/10
 */
class Text {
    int id;
    public Text(int id) {
        System.out.println("init");
        this.id = id;
    }
    public Text(){
        System.out.println("no arg init");
    }
    @Override
    public String toString() {
        return "Text{" +
                "id=" + id +
                '}';
    }
}
class A {
   void doJob(){
       System.out.println("do  Job  A");
   }
}
class B extends A {
    @Override
    void doJob() {
        System.out.println("do Job B");
    }
}
public class SpringListableLookUpDemo {
    @Bean(name = "text")
    public Text getText() {
        return new Text(1);
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(SpringListableLookUpDemo.class);
        annotationConfigApplicationContext.register(B.class);
        annotationConfigApplicationContext.register(A.class);
        annotationConfigApplicationContext.refresh();
        ListableBeanFactory listableBeanFactory = annotationConfigApplicationContext;
        listableBeanFactory.getBeansWithAnnotation(Component.class);
        Text text = (Text) listableBeanFactory.getBean("text");
        Map<String, Text> map = listableBeanFactory.getBeansOfType(Text.class);
        System.out.println(map);
        //如果查找的是一个父类,会顺便将其子类也查询出来
        String[] beanNameArr = listableBeanFactory.getBeanNamesForType(A.class);
        for (String beanName : beanNameArr) {
            System.out.println(beanName);
        }
    }
}
复制代码


在这段代码程序中,有几个点需要注意一下:bean的依赖查找一般是需要结合BeanDefinition来使用,这一步的操作是发生在了Spring容器的上下文启动之前进行的。

对于判断某个bean是否存在,建议可以使用(一般是借助BeanDefinition这种关于类的元信息辅助类判断,此时可以避免提早实现bean的初始化)


例如:


listableBeanFactory.getBeanNamesForType(Text.class);
复制代码


而不是以下方法(可能会涉及到bean的提前初始化操作)


listableBeanFactory.getBeansOfType(Text.class);
复制代码


getBeanNamesForType其中的底层逻辑是从BeanDefinitionNames 集合中去遍历查询,并不会触发bean的初始化步骤;


网络异常,图片无法展示
|


getBeansOfType的底层内部可能会涉及到bean的初始化操作。


网络异常,图片无法展示
|


不过如果在使用这两个方法之前,Spring容器都已经进行了上下文的初始化,那我觉得其实用谁都可以。

目录
相关文章
|
17天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
3月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
46 4
|
5月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
5月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
4月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
5月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
|
5月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
|
Java 关系型数据库 MySQL
06_spring_ 依赖注入| 学习笔记
快速学习 06_spring_ 依赖注入
110 0
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
224 2
|
4天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
42 14
下一篇
DataWorks