0
点赞
收藏
分享

微信扫一扫

Spring依赖查找

Brose 2023-06-14 阅读 95

所谓的依赖查找,简单来说,依赖查找就是从容器中通过api的方式获取我们想要的或者想得到的资源,在Spring中,IOC指的是容器,容器中存储着Bean对象,而所谓的依赖查找就是使用名称或者类型从这个IOC容器中获得我们想要读取的Bean对象的过程。

大纲:

Spring依赖查找_xml

Spring IOC依赖查找

概述

现在在大多数情况下,我们提到依赖查找第一时间都会想到Spring的IOC,但是其实依赖查找的思想最开始并不是Spring提出来的,也并不仅仅是Spring独有的,在Java原生Api中java.beans以及JNDI中都提供了依赖查找的相关实现。

  • • JavaBeans: java.beans.beancontext.BeanContext
  • • JNDI: javax.naming.Context#lookup(javax.naming.Name)

依赖查找的方式

依赖查找一般分为三种情况:

  • • 单一类型依赖查找
  • • 所谓的单一类型查找就是通过名称或者类型返回一个单一的一个对象
  • • 集合类型依赖查找
  • • 集合类型查找指的是通过名称或者类型返回的是一个集合,这个集合可以是Collection类型的,也可以map类型的

  • • 层次性依赖查找

  • • 如同双亲委派一样,IOC容器也是可以存在上下级的,而层次性依赖查找就是指查找可以在当前容器的上级或者上上级一次递归查到

Spring的依赖查找

定义好统一的注入实体:

public class User {

  private String name;

  private Integer age;
  
  // 省略get、set和toString方法
}

// 继承于User
public class SupperUser extends User{

  private String phone;
  
  // 省略get、set和toString方法
}

单一类型查找

Spring中单一类型查找的核心接口是BeanFactory,他也是IOC容器的最顶级接口,内部提供了一堆实时查找以及延迟查找单一类型的接口方法。

Spring依赖查找_xml_02

image-20230319132502392

简单实用方式:

  • • xml配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean name="user" class="cn.phshi.entity.User">
    <property name="name" value="张三"/>
    <property name="age" value="33"/>
  </bean>

  <!--  设置supperUser为primary,避免按照类型查找时报错  -->
  <bean name="supperUser" class="cn.phshi.entity.SupperUser"
        parent="user" scope="prototype" primary="true">
    <property name="phone" value="13311111111"/>
  </bean>
</beans>

  • • Java代码:

public class DependencySingleLookupDemo {

  public static void main(String[] args) {
    // 启动 Spring 应用上下文
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
    findByName(beanFactory);
    // 通过类型查找 上下文存在多个相同对象时候会报错
    findByType(beanFactory);
  }

  private static void findByName(BeanFactory beanFactory) {
    // 为什么要有通过名称查找的方式?为了支持之前的jdk没有泛型
    User user = (User) beanFactory.getBean("user");
    System.out.println("通过名称查询的结果方式1:" + user);
    // 指明要强转的对象
    user = beanFactory.getBean("user", User.class);
    System.out.println("通过名称查询的结果方式2:" + user);
  }

  private static void findByType(BeanFactory beanFactory) {
    // 查找出来的是设置了primary="true"的对象
    User user = beanFactory.getBean(User.class);
    System.out.println("通过类型查询的结果:" + user);
  }
}

集合类型查找

Spring中集合类型查找的核心接口是 ListableBeanFactory,他继承了BeanFactory,内部提供了一通过名称或者类型返回Map对象的接口方法。

Spring依赖查找_User_03

image-20230319134321896

简单实用方式:

  • • xml配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean name="user" class="cn.phshi.entity.User">
    <property name="name" value="张三"/>
    <property name="age" value="33"/>
  </bean>

  <!--  SupperUser对象被Supper注解标注  -->
  <bean name="supperUser" class="cn.phshi.entity.SupperUser"
        parent="user" scope="prototype" primary="true">
    <property name="phone" value="13311111111"/>
  </bean>
</beans>

  • • Java代码:
  • • 前置工作

// 声明修饰注解
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Supper {
   String name() default "";
 }
 
 // 标注在SupperUser上
 @Supper(name = "我是SupperUser")
 public class SupperUser extends User{
   private String phone;
 
   // 省略get、set和toString方法
 }

  • • 查找方式

public class DependencyCollectionLookupDemo {
 
   public static void main(String[] args) {
     // 启动 Spring 应用上下文
     BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
     // 查找集合
     findCollectionByType(beanFactory);
     // 通过指定注解加载
     findByAnnotationType(beanFactory);
     // 查找指定Bean上面的注解
     findAnnotationByBean(beanFactory);
   }
 
   private static void findCollectionByType(BeanFactory beanFactory) {
     // ClassPathXmlApplicationContext实现了ListableBeanFactory接口,所以可以强转
     if (beanFactory instanceof ListableBeanFactory) {
       Map<String, User> users = ((ListableBeanFactory) beanFactory).getBeansOfType(User.class);
       System.out.println("查询到的User对象为:" + users);
     }
   }
 
   private static void findByAnnotationType(BeanFactory beanFactory) {
     if (beanFactory instanceof ListableBeanFactory) {
       Map<String, Object> users = ((ListableBeanFactory) beanFactory).getBeansWithAnnotation(Supper.class);
       System.out.println("查询到被Supper修饰的对象为:" + users);
     }
   }
 
   private static void findAnnotationByBean(BeanFactory beanFactory) {
     if (beanFactory instanceof ListableBeanFactory) {
       Supper supper = ((ListableBeanFactory) beanFactory).findAnnotationOnBean("supperUser", Supper.class);
       System.out.println("supperUser对象的注解标注的name值是:" + supper.name());
     }
   }
 }

层次性查找

Spring中层次性类型查找的核心接口是 HierarchicalBeanFactory,他也是继承了BeanFactory,他存在一个接口getParentBeanFactory用来获得他的父级容器对象。一个HierarchicalBeanFactory只能存在唯一一个直接上级BeanFactory

Spring依赖查找_xml_04

image-20230319142051278

简单实用方式:

  • • Java对象:

public class DependencyHierarchicalLookupDemo {

  // 注意: SupperUser对象是在ClassPathXmlApplicationContext中定义的
  public static void main(String[] args) {
    // 1、通过AnnotationConfigApplicationContext创建一个上下文对象
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class);
    annotationConfigApplicationContext.refresh();
    // 获得AnnotationConfigApplicationContext上下文中的BeanFactory对象,
    // 其实真正的BeanFactory是内部包装的DefaultListableBeanFactory,改步骤就是获取真正的BeanFactory对象
    ConfigurableListableBeanFactory beanFactory = annotationConfigApplicationContext.getBeanFactory();

    // 2、通过xml配置创建一个上下文对象
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
    // 3、设置 Parent BeanFactory
    beanFactory.setParentBeanFactory(classPathXmlApplicationContext);

    // 先从本身判断,打印结果:当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory]是否包含[supperUser]对象:false
    containsLocalBean(beanFactory, "supperUser");
    // 从父对象中判断,打印结果:当前BeanFactory[org.springframework.context.support.ClassPathXmlApplicationContext]是否包含[supperUser]对象:true
    containsLocalBean((HierarchicalBeanFactory) beanFactory.getParentBeanFactory(), "supperUser");

    annotationConfigApplicationContext.close();
  }

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

从上面的判断结果可以看出,beanFactory.containsLocalBean方法只能获得当前对象本身是否包含指定的对象,想要判断上级beanFactory是否包含必须先从当前通过getParentBeanFactory获得父级的beanFactory再进行判断,所以通常通过递归的方式去校验当前是否包含指定的Bean,不包含再从父对象中判断。

BeanFactoryUtils工具类中提供了个递归查找的方式:

public class DependencyHierarchicalLookupDemo {

  // 注意: SupperUser对象是在ClassPathXmlApplicationContext中定义的
  public static void main(String[] args) {
    // 1、通过AnnotationConfigApplicationContext创建一个上下文对象
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class);
    annotationConfigApplicationContext.refresh();
    ConfigurableListableBeanFactory beanFactory = annotationConfigApplicationContext.getBeanFactory();

    // 2、通过xml配置创建一个上下文对象
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
    // 3、设置 Parent BeanFactory
    beanFactory.setParentBeanFactory(classPathXmlApplicationContext);


    // 通过BeanFactoryUtils中的beansOfTypeIncludingAncestors方法获取对象
    Map<String, SupperUser> userMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, SupperUser.class);
    System.out.println("得到的对象为:" + userMap);

    annotationConfigApplicationContext.close();
  }
}

我们再来看一下这个方法的实现,便是实用递归来进行层次性查找的:

Spring依赖查找_xml_05

image-20230319150431308

延迟依赖查找

Spring中延迟依赖查找的核心接口是 ObjectFactory,他属于延迟依赖查找的顶级接口,但是通常情况下我们使用延迟依赖查找并不会直接使用ObjectFactory,而是使用他的一个子接口ObjectProvider,ObjectProvider实现了ObjectFactory的同时,还实现了Iterable接口,支持返回集合,并支持Java8的函数式编程,提供安全返回的方式。在Spring内部,该方式使用广泛。

Spring依赖查找_spring_06

image-20230319151343892

简单实用方式:

  • • xml配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean name="user" class="cn.phshi.entity.User">
    <property name="name" value="张三"/>
    <property name="age" value="33"/>
  </bean>

  <bean name="supperUser" class="cn.phshi.entity.SupperUser"
        parent="user" scope="prototype" primary="true">
    <property name="phone" value="13311111111"/>
  </bean>

  <!--  延迟加载  -->
  <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="user"/>
  </bean>
</beans>

  • • Java代码:

public class DependencyLazyLookupDemo {

  public static void main(String[] args) {
    // 启动 Spring 应用上下文
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
    // 通过ObjectFactory延迟加载
    findInLazyByObjectFactory(beanFactory);
    // 通过ObjectFactory延迟加载
    findInLazyByObjectProvider(beanFactory);
    // 安全的延迟加载
    findInLazyBySafetyObjectProvider(beanFactory);
    findInLazyByDefaultObjectProvider(beanFactory);
  }

    // 输出结果:通过getBean延迟加载出来的对象为:User{name='张三', age=33}
  private static void findInLazyByObjectFactory(BeanFactory beanFactory) {
    // ObjectFactory对象需要在xml中配置
    ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");

    User user = objectFactory.getObject();
    System.out.println("通过getBean延迟加载出来的对象为:" + user);
  }

  // 输出结果:User{name='张三', age=33}
    //  SupperUser{phnotallow='13311111111'} User{name='张三', age=33}
  private static void findInLazyByObjectProvider(BeanFactory beanFactory) {
    // ObjectFactory对象需要在xml中配置
    ObjectProvider<User> objectFactory = beanFactory.getBeanProvider(User.class);

    // 继承了Iterable,所以可以使用流
    objectFactory.forEach(System.out::println);
  }
  
  // 输出结果:安全获取结果为:null
  private static void findInLazyByDefaultObjectProvider(BeanFactory beanFactory) {
    //  当前上下文中不存在String的Bean
    ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);
    String str = provider.getIfAvailable(() -> "这是默认值");
    System.out.println("获取得到默认结果为:" + str);
  }

  // 输出结果:获取得到默认结果为:这是默认值
  private static void findInLazyBySafetyObjectProvider(BeanFactory beanFactory) {
    // 当前上下文中不存在String的Bean
    ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);
    String str = provider.getIfAvailable();
    System.out.println("安全获取结果为:" + str);
  }
}

Spring内建依赖

AbstractApplicationContext内建可查找的依赖

Bean名称

实例

描述

environment

Environment 对象

外部化配置以及 Profiles

systemProperties

java.util.Properties 对象

Java 系统属性

systemEnvironment

java.util.Map 对象

操作系统环境变量

messageSource

MessageSource 对象

国际化文案

lifecycleProcessor

LifecycleProcessor 对象

Lifecycle Bean 处理器

applicationEventMulticaster

ApplicationEventMulticaster 对象

Spring 事件广播器

查找方式:

public class DependencyBuiltInLookupDemo {

  public static void main(String[] args) {
    // 创建一个上下文对象
    AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
    beanFactory.register(DependencyBuiltInLookupDemo.class);
    beanFactory.refresh();

    System.out.println("Environment对象为:"+beanFactory.getBean("environment"));
    System.out.println("java.util.Properties对象为:"+beanFactory.getBean("systemProperties"));
    System.out.println("java.util.Map对象为:"+beanFactory.getBean("systemEnvironment"));
    System.out.println("MessageSource对象为:"+beanFactory.getBean("messageSource"));
    System.out.println("LifecycleProcessor对象为:"+beanFactory.getBean("lifecycleProcessor"));
    System.out.println("ApplicationEventMulticaster对象为:"+beanFactory.getBean("applicationEventMulticaster"));

    beanFactory.close();
  }
}

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

该对应的所有Bean注册信息可以在AnnotationConfigUtils对象中找到。

Bean名称

实例

描述

org.springframework.context.annotation.

internalConfigurationAnnotationProcessor

ConfigurationClassPostProcessor 对象

处理 Spring 配置类

org.springframework.context.annotation.

internalAutowiredAnnotationProcessor

AutowiredAnnotationBeanPostProce

ssor 对象

处理 @Autowired 以及 @Value 注解

org.springframework.context.annotation.

internalCommonAnnotationProcessor

CommonAnnotationBeanPostProce

ssor 对象

(条件激活)处理 JSR-250 注解, 如 @PostConstruct 等

org.springframework.context.

event.internalEventListenerProcessor

EventListenerMethodProcessor 对象

处理标注 @EventListener 的 Spring 事件监听方法

org.springframework.context.

event.internalEventListenerFactory

DefaultEventListenerFactory 对象

@EventListener 事件监听方法适 配为 ApplicationListener

org.springframework.context.annotation.

internalPersistenceAnnotationProcessor

PersistenceAnnotationBeanPostProce

ssor 对象

(条件激活)处理 JPA 注解场景

查找方式:

public class DependencyBuiltInLookupDemo {

    public static void main(String[] args) {
        // 创建一个上下文对象
        AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
        beanFactory.register(DependencyBuiltInLookupDemo.class);
        beanFactory.refresh();
      
        System.out.println("ConfigurationClassPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));
        System.out.println("AutowiredAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalAutowiredAnnotationProcessor"));
        System.out.println("CommonAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalCommonAnnotationProcessor"));
        System.out.println("EventListenerMethodProcessor对象为:"+beanFactory.getBean("org.springframework.context.event.internalEventListenerProcessor"));
        System.out.println("EventListenerMethodProcessor对象为:"+beanFactory.getBean("org.springframework.context.event.internalEventListenerFactory"));
        // 需要加入orm等相关依赖对JPA进行支持才会有该对象
        // System.out.println("PersistenceAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalPersistenceAnnotationProcessor"));

        beanFactory.close();
    }
}

AnnotationConfigUtils中对内建依赖注入的部分代码截图:

Spring依赖查找_xml_07

image-20230319161539276

依赖查找中的经典异常

Spring中所有依赖查找中可能会抛出的异常所有都是BeansException子类,并且属于uncheck类型异常。

  • • NoSuchBeanDefinitionException: 当查找 Bean 不存在于 IoC 容器 时
  • • NoUniqueBeanDefinitionException: 类型依赖查找时,IoC 容器存在多 个 Bean 实例
  • • BeanInstantiationException: 当 Bean 所对应的类型非具体类时
  • • BeanCreationException: 当 Bean 初始化过程中
  • • BeanDefinitionStoreException: 当BeanDefinition配置元信息非法时

参考文章

小马哥讲 Spring 核心编程思想

举报

相关推荐

0 条评论