当前位置: 首页 > 图灵资讯 > 技术篇> Spring依赖查找

Spring依赖查找

来源:图灵教育
时间:2023-06-17 13:49:21

所谓依赖搜索,简单来说,依赖搜索就是通过api从容器中获取我们想要的或想要的资源。在Spring中,IOC指的是存储Bean对象的容器,而所谓依赖搜索就是利用名称或类型从IOC容器中获取我们想要读取的Bean对象的过程。

大纲:

Spring依赖查找_User

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和tosring方法}// 继承Userpublic 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) {     // ClaspathXmlaplicationContext实现了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依赖查找_User_04

image-20230319142051278

简单实用的方法:

  • •Java对象:
public class DependencyHierarchicalLookupDemo {  // 注意: Supperuser对象是在ClasspathXmlaplicationcontext中定义的  public static void main(String[] args) {    // 1、通过Annnotationconfigaplicationcontext创建上下文对象    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();    annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class);    annotationConfigApplicationContext.refresh();    // 在Annnotationconfigaplicationcontext上下文中获得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对象是在ClasspathXmlaplicationcontext中定义的  public static void main(String[] args) {    // 1、通过Annnotationconfigaplicationcontext创建上下文对象    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);    // 通过beansoftypeincludingAncestors获取对象    Map<String, SupperUser> userMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, SupperUser.class);    System.out.println(”得到的对象是:" + userMap);    annotationConfigApplicationContext.close();  }}

让我们来看看这种方法的实现,即实用递归进行分层搜索:

Spring依赖查找_User_05

image-20230319150431308

延迟依赖搜索

延迟依赖搜索在Spring中的核心接口是 ObjectFactory,他是延迟依赖搜索的顶级接口,但通常我们不会直接使用延迟依赖搜索ObjectFactory,相反,使用他的子接口ObjectProvider,ObjectProvider实现了ObjectFactory同时,也实现了Iterable接口,支持返回集合,支持Java8函数编程,提供安全返回的方式。该方法广泛应用于Spring。

Spring依赖查找_xml_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的Beang    ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);    String str = provider.getIfAvailable(() -> “这是默认值”);    System.out.println(“获得默认结果为:” + str);  }  // 输出结果:默认结果为:这是默认值  private static void findInLazyBySafetyObjectProvider(BeanFactory beanFactory) {    // 目前上下文中没有String的Beang    ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);    String str = provider.getIfAvailable();    System.out.println(“安全获取结果为:” + str);  }}
Spring内部建设依赖于Abstractaplicationcontext内部建设可以找到的依赖

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(“LifecycleProcesor对象为:”+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(“ConfigurationClaspostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));        System.out.println(“AutowiredAnotationBeanPostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalAutowiredAnnotationProcessor"));        System.out.println(“CommonannnotationBeanPostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalCommonAnnotationProcessor"));        System.out.println(“EventlistenermethodProcesor对象为:”+beanFactory.getBean("org.springframework.context.event.internalEventListenerProcessor"));        System.out.println(“EventlistenermethodProcesor对象为:”+beanFactory.getBean("org.springframework.context.event.internalEventListenerFactory"));        // 需要加入orm等相关依赖来支持JPA        // System.out.println(“PersistenceanotationBeanPostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalPersistenceAnnotationProcessor"));        beanFactory.close();    }}

AnnotationConfigUtils部分代码截图依赖于注入内部建筑:

Spring依赖查找_User_07

image-20230319161539276

依赖搜索中的经典异常

所有依赖搜索的Spring中可能抛出的异常都是BeansException子类,属于uncheck类型异常。

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