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

目录
相关文章
|
2月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
74 2
|
5月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
66 4
|
7月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
7月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
6月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
7月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
|
7月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
|
Java Spring 容器
深入挖掘Spring系列 -- 依赖的来源(下)
深入挖掘Spring系列 -- 依赖的来源(下)
139 0
|
XML 存储 Java
深入挖掘Spring系列 -- 依赖的来源(上)
深入挖掘Spring系列 -- 依赖的来源(上)
170 0
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
202 17
Spring Boot 两种部署到服务器的方式