深入挖掘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容器都已经进行了上下文的初始化,那我觉得其实用谁都可以。

目录
相关文章
|
4天前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
13 4
|
3月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
2月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
3月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
3月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
3月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
|
3月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
|
Java Spring 容器
深入挖掘Spring系列 -- 依赖的来源(下)
深入挖掘Spring系列 -- 依赖的来源(下)
120 0
|
XML 存储 Java
深入挖掘Spring系列 -- 依赖的来源(上)
深入挖掘Spring系列 -- 依赖的来源(上)
145 0
|
7天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
下一篇
无影云桌面