当前位置: 首页 > news >正文

Spring 依赖查找知识点总结

前言

源码在我github的guide-spring仓库中,可以克隆下来 直接执行。

我们本文主要来介绍依赖查找的使用示例

依赖查找

什么是依赖查找

依赖查找并不是 Spring 框架特有的概念,它是一种在软件开发中获取依赖对象的方式。它通常用于获取运行时需要的服务、组件或其他对象的引用。在面向对象编程中,依赖通常体现为一个对象需要另一个对象的服务或功能。

在不同的编程框架和容器中,依赖查找的方式可能会有所不同。我们简单罗列一些常见的依赖查找的例子:

Java中的依赖查找

  • 在纯 Java 环境中,依赖查找通常通过构造函数、方法参数或其他手段来获得依赖对象的引用。
  • 例如,通过在一个对象的构造函数中传递另一个对象的引用:
public class MyClass {private DependencyClass dependency;public MyClass(DependencyClass dependency) {this.dependency = dependency;}// ...
}

Spring框架中的依赖查找

  • 在Spring框架中,依赖查找通常通过 Spring 容器来实现。你可以使用ApplicationContextor BeanFactory 来获取所需的 Bean 。

    public class UseDependencyLookupDemo {public static void main(String[] args) throws Exception {BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");// 1. 实时查找realtimeLookup(beanFactory);}private static void realtimeLookup(BeanFactory beanFactory) {// 名称+类型User user = beanFactory.getBean("user", User.class);System.out.println("实时查找: " + user);}
    }
    
  • 或者,通过在类中使用@Autowired注解来自动注入依赖:

    @Service
    public class MyService {@Autowiredprivate DependencyClass dependency;// ...
    }
    

Java EE中的依赖查找:

  • 在Java EE环境中,你可以使用JNDI(Java Naming and Directory Interface)进行依赖查找。通过JNDI,你可以在运行时查找和获取命名对象。

    public class JNDIDependencyLookupDemo {public static void main(String[] args) throws NamingException {// 设置JNDI环境属性Properties properties = new Properties();properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");properties.put(Context.PROVIDER_URL, "file:/META-INF/jndi");// 初始化InitialContextContext initialContext = new InitialContext(properties);// 在文件系统上查找一个名为 "user" 的对象User user = (User) initialContext.lookup("user");System.out.println("JNDI Lookup Result: " + user);}
    }
    

依赖查找的方式

依赖查找的方式有很多,我们先看下 BeanFactory 的 接口定义:

public interface BeanFactory {Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);}

可以看出上述定义,我们可以通过 Bean 名称、Bean 名称 + 类型、类型等方式进行依赖查找 Bean。下面我们分别从单一类型、集合类型、层次类型、延迟等方式依次展示依赖查找的示例。

单一类型的依赖查找

单一类型的查找,需要要求容器中同一类型的Bean只能有一个为 primary (BeanDefinition中的概念),我们可以看下 xml 配置示例

<?xml version="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.markus.spring.ioc.overview.domain.User"><property name="id" value="1"/><property name="username" value="markus zhang"/></bean><!-- 当有多个 User 时,需要指出 其中一个 Bean 的 primary属性为 true 否则会出现 NoUniqueBeanDefinitionException --><bean id="user2" class="com.markus.spring.ioc.overview.domain.User" lazy-init="true" primary="true"><property name="id" value="2"/><property name="username" value="markus zhang"/></bean></beans>

我们来看下使用示例

public class UseDependencyLookupDemo {public static void main(String[] args) throws Exception {BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");typeLookup(beanFactory);}/*** ========================按照 Bean 类型查找========================*//*** 单个Bean类型查找** @param beanFactory*/private static void typeLookup(BeanFactory beanFactory) {User user = beanFactory.getBean(User.class);System.out.println(user);}
}
集合类型的依赖查找

与单一类型查找的区别在于,它不需要指定 primary 并且 返回一个 Map<String,T> 对象,key 为 Bean 的名称,value 为 Bean 实例。

public class UseDependencyLookupDemo {public static void main(String[] args) throws Exception {BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");// 4. 按照类型查找多个BeancollectionLookup(beanFactory);}/*** 根据集合类型查找*/private static void collectionLookup(BeanFactory beanFactory) {if (beanFactory instanceof ListableBeanFactory) {ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;Map<String, User> userMap = listableBeanFactory.getBeansOfType(User.class);userMap.forEach((beanName, user) -> System.out.println("Bean name: " + beanName + ", User: " + user));}}
}
层次类型的依赖查找

层次性依赖查找,体现在父子容器中,我们一般可能体会不到,实际上 Spring MVC 的底层就涉及父子容器的概念,即 Root ApplicationContext 和 Dispatcher-Servlet ApplicationContext。这里不展开了。我们通过一个简单的示例来展示层次性依赖查找 Bean

package com.markus.spring.dependency.lookup;import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @author: markus* @date: 2023/12/17 10:23 PM* @Description: {@link HierarchicalBeanFactory}* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class HierarchicalBeanFactoryDependencyDemo {public static void main(String[] args) {ConfigurableListableBeanFactory subBeanFactory = new DefaultListableBeanFactory();// 设置父容器subBeanFactory.setParentBeanFactory(createParent());// 展示 仅在当前 Bean 容器中是否 存在System.out.println(displayContainBean(subBeanFactory, "user", true));// 展示 父子 Bean 容器中是否 存在(体现出 可继承 BeanFactory 的示例 即 HierarchicalBeanFactory)System.out.println(displayContainBean(subBeanFactory, "user", false));}private static boolean displayContainBean(ConfigurableListableBeanFactory beanFactory, String beanName, boolean onlyLocal) {boolean result = beanFactory.containsLocalBean(beanName);if (!onlyLocal) {if (!result) {BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();if (parentBeanFactory != null) {result = parentBeanFactory.containsBean(beanName);}}}return result;}private static ConfigurableListableBeanFactory createParent() {ConfigurableListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) parentBeanFactory);String location = "classpath:/META-INF/dependency-lookup.xml";// 加载 父容器 的 Bean 实例beanDefinitionReader.loadBeanDefinitions(location);return parentBeanFactory;}
}
延迟依赖查找

延迟依赖查找通常体现在懒加载 Bean 的场景,比如一些大资源的Bean希望在使用到的时候才会触发初始化以达到降低服务启动时间的目的,这个时候就可以使用懒加载模式,而在我们依赖查找的时候,使用延迟依赖查找的时候,也不会触发 Bean 的初始化,只有在真正使用到对象的时候才会触发初始化。(ps 比较绕,我们直接看例子)

因为 Bean 元信息配置比较特殊,我把 xml 配置也展示出来

<?xml version="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user2" class="com.markus.spring.ioc.overview.domain.User" lazy-init="true" primary="true"><property name="id" value="2"/><property name="username" value="markus zhang"/></bean><bean id="factoryBean" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"><property name="targetBeanName" value="user2"/></bean></beans>

使用示例

public class UseDependencyLookupDemo {public static void main(String[] args) throws Exception {BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");lazyLookup(beanFactory);}/*** 延迟查找*/private static void lazyLookup(BeanFactory beanFactory) throws Exception {@SuppressWarnings("unchecked")ObjectFactory<User> factoryBean = (ObjectFactory<User>) beanFactory.getBean("factoryBean");System.out.println("延迟生效中....");System.out.println("延迟查找: " + factoryBean.getObject());}}

Spring内建可查找的依赖

除了我们自己配置的Bean,我们还可以查找 Spring 框架内 注册的单例 Bean。具体如下:

  • environment Environment 外部化配置以及Profiles
  • systemProperties Properties Java系统属性
  • systemEnvironment Map<String,String> 操作系统环境变量
  • messageSource MessageSource 国家化文案
  • lifecycleProcessor LifecycleProcessor Lifecycle Bean 处理器
  • applicationEventMulticaster ApplicationEventMulticaster Spring 事件广播器
  • internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor 处理 Spring 的配置类
  • internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor 处理 @Autowired 以及 @Value、@Inject注解
  • internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor 处理 JSR-250 注解,如 @Resource、@PostConstruct等
  • internalEventListenerProcessor EventListenerMethodProcessor 处理标注 @EventListener 的 Spring 事件监听方法
  • internalEventListenerFactory DefaultEventListenerFactory 处理@EventListener 事件监听方法适配为 ApplicationListener

我们来看下示例:

package com.markus.spring.dependency.lookup;import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.LifecycleProcessor;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.DefaultEventListenerFactory;
import org.springframework.context.event.EventListenerMethodProcessor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;import java.util.Properties;/*** @author: markus* @date: 2023/12/17 10:54 PM* @Description: Spring 内建依赖的 依赖查找示例* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class SpringInternalBeanDependencyLookDemo {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");displaySpringInternalBean(context, Environment.class);displaySpringInternalBean(context, Properties.class);displaySpringInternalBeanByName(context, "systemEnvironment");displaySpringInternalBean(context, MessageSource.class);displaySpringInternalBean(context, LifecycleProcessor.class);displaySpringInternalBean(context, ApplicationEventMulticaster.class);// 关闭 Spring 容器上下文context.close();// 基于 注解驱动 的应用上下文AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();annotationConfigApplicationContext.register(SpringInternalBeanDependencyLookDemo.class);annotationConfigApplicationContext.refresh();displaySpringInternalBean(annotationConfigApplicationContext, ConfigurationClassPostProcessor.class);displaySpringInternalBean(annotationConfigApplicationContext, AutowiredAnnotationBeanPostProcessor.class);displaySpringInternalBean(annotationConfigApplicationContext, CommonAnnotationBeanPostProcessor.class);displaySpringInternalBean(annotationConfigApplicationContext, EventListenerMethodProcessor.class);displaySpringInternalBean(annotationConfigApplicationContext, DefaultEventListenerFactory.class);annotationConfigApplicationContext.close();}private static void displaySpringInternalBean(ApplicationContext context, Class<?> type) {Object bean = context.getBean(type);System.out.println(bean);}private static void displaySpringInternalBeanByName(ApplicationContext context, String beanName) {Object bean = context.getBean(beanName);System.out.println(bean);}
}

可以看到上面我们引入了基于 Xml 驱动的Spring应用上下文以及基于 注解 驱动的Spring应用上下文来实现 Spring 内建 Bean 的依赖查找。

ps: 如果一个默认的 ClassPathXmlAppplicationContext 不会包含ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等这些注解的依赖,需要我们在 xml 配置文件中开启 注解启动,才会注册进 Spring IoC容器中。

大家可能会有疑问,这些 Spring 内建的 Bean 是什么时候被注册进去呢?这里给下源码位置,感兴趣的可以自行查看:

  • org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
  • org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)

依赖查找的常见异常

下面这些就是场景的在使用依赖查找的时候可能会触发的异常,都是 BeansException 的子类型。场景比较清晰,这里就不写具体的示例了。

异常类型触发条件场景举例
NoSuchBeanDefinitionException当查找 Bean 不存在于 IoC 容器 时BeanFactory#getBean
NoUniqueBeanDefinitionException类型依赖查找时,IoC 容器存在多 个 Bean 实例BeanFactory#getBean(Class)
BeanInstantiationException当 Bean 所对应的类型非具体类时BeanFactory#getBean
BeanCreationException当 Bean 初始化过程中Bean 初始化方法执行异常时
BeanDefinitionStoreException当 BeanDefinition 配置元信息非 法时XML 配置资源无法打开时

本文总结

好了,到这就基本上把 Spring 依赖查找相关的知识点就总结完了,本文我们主要总结了依赖查找的几种方式,包括单一类型、集合类型、层次性、延迟性以及 Spring 内建 Bean 的依赖查找,并给出了 Spring 内建 Bean 注册的源码位置,最后提到了依赖查找的几个常见的异常,并给出了常见场景触发的条件。

关于 Spring 依赖 还有依赖注入、依赖来源等知识 后面会跟进梳理。

以上也是我在学习 Spring 时参考的资料并加上了自己的理解,如有错误,欢迎交流。

http://www.lryc.cn/news/262714.html

相关文章:

  • html5新增特性
  • 4、APScheduler: 详解Scheduler种类用法、常见错误与解决方法【Python3测试任务管理总结】
  • 微服务实战系列之ZooKeeper(实践篇)
  • C++ 开发中为什么要使用继承
  • 2020蓝桥杯c组纸张大小
  • 【Image】图像处理
  • JAVA对文档加密
  • EmbedAI:一个可以上传文件训练自己ChatGPT的AI工具,妈妈再也不用担心我的GPT不会回答问题
  • runCatching异常捕获onSuccess/onFailure返回函数,Kotlin
  • IDEA报错处理
  • 使用动画曲线编辑器打造炫酷的3D可视化ACE
  • 使用 React 和 ECharts 创建地球模拟扩散和飞线效果
  • http状态码(一)400报错
  • 【深度学习目标检测】五、基于深度学习的安全帽识别(python,目标检测)
  • 芒果RT-DETR改进实验:深度集成版目标检测 RT-DETR 热力图来了!支持自定义数据集训练出来的模型
  • c语言实验八
  • ArcGIS Pro SDK文件选择对话框
  • ACT、NAT、NATPT和EASY-IP
  • HTML实现每天单词积累
  • 【ECMAScript笔记二】运算符分类,流程控制(顺序结构、分支结构、循环结构)
  • ShenYu网关注册中心之Zookeeper注册原理
  • 高级C#技术(二)
  • 【性能测试】基础知识篇-压力模型
  • springboot-redis设置定时触发任务详解
  • Video anomaly detection with spatio-temporal dissociation 论文阅读
  • svn 安装
  • slurm 23.11.0集群 debian 11.5 安装
  • ffmpeg可以做什么
  • 一种缩小数据之间差距的算法
  • 【Axure RP9】动态面板使用------案例:包括轮播图和多方式登入及左侧菜单栏案例