聊聊Spring内部的依赖管理(上)

简介: 聊聊Spring内部的依赖管理(上)

今天主要和大家分享一些在工作中可能会用到的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 BeansException
复制代码


ObjectProvider


随着Spring版本的升高,也开始出现了延迟查找的功能。当我们实际需要用到某个bean的时候才将其从容器中进行初始化并且提取出来。


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


Spring会返回一个ObjectProvider,当查询的时候才会触发bean的创建。延迟查找的好处在于,如果一个bean需要注入到spring容器中,但是不希望太过早地去进行初始化,那么可以思考使用ObjectProvider的方式来进行初始化。


集合类型查找


Bean的名称查询


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


获取同类型Bean实例列表


getBeansOfType(Class)
复制代码


按照注解去查询


String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException;
复制代码


使用依赖查找时候的一些小心得:


对于判断一个bean是否存在,可以采用判断其beandefinition是否存在,一般这样不会触发其中bean的初始化操作,例如:getBeanNamesForType。反观getBeansOfType可能回触发bean的初始化


层次性的bean查找


可能大多数人在实际使用Spring容器的时候对于层次性的bean做计算并没有太多的实战尝试,这里我举个例子:


例如说A容器中包含了Bean A,如果B容器继承了A容器,那么按道理来说也应该能够获得Bean A资源,这种设计可以减少Bean的额外存储。


如果你理解了我上边所说的这个案例之后,再来看看下边的这张图可能就会有更加深入的理解了。


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


关于层次性的bean获取,我这里给出一个小的demo供大家学习:


package org.idea.spring.look.up.factory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * 层次性的依赖查找 {@link org.springframework.beans.factory.HierarchicalBeanFactory}
 *
 * @Author idea
 * @Date created in 10:55 下午 2021/4/10
 */
public class SpringHierarchicalLookUpDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(SpringHierarchicalLookUpDemo.class);
        applicationContext.refresh();
        ParentIocContainer parentIocContainer = new ParentIocContainer();
        ApplicationContext parentApplicationContext = parentIocContainer.getAndStartApplicationContext();
        // ConfigurableListableBeanFactory -> ConfigurableBeanFactory -> HierarchicalBeanFactory
        ConfigurableListableBeanFactory configurableListableBeanFactory = applicationContext.getBeanFactory();
        System.out.println("此时的父类BeanFactory为:" + configurableListableBeanFactory.getParentBeanFactory());
        configurableListableBeanFactory.setParentBeanFactory(parentApplicationContext);
        System.out.println("此时的父类BeanFactory为:" + configurableListableBeanFactory.getParentBeanFactory());
        ParentIocContainer.ParentBean parentBean = (ParentIocContainer.ParentBean) configurableListableBeanFactory.getBean("parentBean");
        System.out.println(parentBean);
        isContainedBean(configurableListableBeanFactory, "parentBean");
        displayContainsBean(configurableListableBeanFactory, "parentBean");
    }
    /**
     * 这里是子类可以获取自己和父类层次内部的bean,如果是使用containsLocalBean方法的话就只能判断当前所在层次的容器上下文
     *
     * @param beanFactory
     * @param beanName
     */
    public static void isContainedBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.println("getBean is " + beanFactory.getBean(beanName));
        System.out.printf("contained is [%s] ,beanFactory is [%s],beanName is [%s]\n", beanFactory.containsLocalBean(beanName), beanFactory, beanName);
    }
    /**
     * 查找关于父类容器内部的bean
     *
     * @param beanFactory
     * @param beanName
     */
    private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("contained is [%s] ,beanFactory is [%s],beanName is [%s]\n", isContainedBeanInHoldApplication(beanFactory, beanName), beanFactory, beanName);
    }
    /**
     * 使用递归判断 -- 自上到下判断父类容器是否含有bean
     *
     * @param hierarchicalBeanFactory
     * @param beanName
     * @return
     */
    public static boolean isContainedBeanInHoldApplication(HierarchicalBeanFactory hierarchicalBeanFactory, String beanName) {
        BeanFactory parentBeanFactory = hierarchicalBeanFactory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
            if (isContainedBeanInHoldApplication(parentHierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return hierarchicalBeanFactory.containsBean(beanName);
    }
}
复制代码


对应的父类容器案例:


package org.idea.spring.look.up.factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * 父类ioc容器 这里面的ioc容器只包含有ParentBean这个类
 *
 * @Author idea
 * @Date created in 8:46 上午 2021/4/11
 */
public class ParentIocContainer {
    public static AnnotationConfigApplicationContext applicationContext = null;
    class ParentBean {
        int id;
        public ParentBean(){
            System.out.println("this is no arg init");
        }
        @Override
        public String toString() {
            return "ParentBean{" +
                    "id=" + id +
                    '}';
        }
    }
    public ApplicationContext getAndStartApplicationContext(){
       applicationContext = new AnnotationConfigApplicationContext();
       applicationContext.register(ParentIocContainer.class);
       //需要支持无参构造函数
       applicationContext.registerBean("parentBean",ParentBean.class);
       applicationContext.refresh();
       return applicationContext;
    }
    public static void main(String[] args) {
        ParentIocContainer parentIocContainer = new ParentIocContainer();
        ApplicationContext applicationContext = parentIocContainer.getAndStartApplicationContext();
        String[] str = applicationContext.getBeanNamesForType(ParentBean.class);
        for (String beanName : str) {
            System.out.println(beanName);
        }
    }
}
复制代码


从这段代码中可以看出,HierarchicalBeanFactory是一种常见的层次类BeanFactory,并且当我们需要判断一个bean是否存在某个容器上下文中的时候,不妨可以试试使用BeanFacoty自带的这个方法:


org.springframework.beans.factory.HierarchicalBeanFactory#containsLocalBean
/**
 * 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);
复制代码


目录
相关文章
|
6月前
|
人工智能 Java Spring
Spring Boot循环依赖的症状和解决方案
Spring Boot循环依赖的症状和解决方案
|
2月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
40 4
|
6月前
|
设计模式 Java 开发者
解密Spring:优雅解决依赖循环的神兵利器
解密Spring:优雅解决依赖循环的神兵利器
479 57
|
4月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
4月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
4月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
3月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
6月前
|
缓存 Java 开发工具
【spring】如何解决循环依赖
【spring】如何解决循环依赖
209 56
|
4月前
|
JavaScript Java Maven
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
1899 1
|
4月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决