Spring揭秘:ApplicationContextAware应用场景及实现原理!

news/2024/7/24 7:44:58 标签: spring, java, 后端

Spring揭秘:ImportBeanDefinitionRegistrar应用场景及实现原理! - 程序员古德

内容概要

ApplicationContextAware接口能够轻松感知并在Spring中获取应用上下文,进而访问容器中的其他Bean和资源,这增强了组件间的解耦,了代码的灵活性和可扩展性,是Spring框架中实现高级功能的关键接口之一。

核心概念

它能用来干啥?

为了方便理解,模拟一个业务场景。假如有一个功能模块负责处理订单,在这个模块中,有一个OrderService类,它负责订单的创建、更新和查询等操作,单在处理订单的过程中,可能需要访问其他一些服务,比如用户服务来获取用户信息,或者库存服务来检查商品库存。

在这个场景中,可以使用ApplicationContextAware接口来解决,可以创建一个类,比如叫OrderServiceContextAware,让它实现ApplicationContextAware接口,当Spring容器初始化这个类时,它会自动将应用上下文注入到这个类中,一旦注入了应用上下文,OrderServiceContextAware就能够访问到容器中的其他Bean,包括用户服务和库存服务。

可以在OrderService类中使用OrderServiceContextAware来获取用户服务和库存服务的实例,而无需直接在OrderService中注入这些服务,这样OrderService类不直接依赖于其他服务的具体实现,而是通过OrderServiceContextAware来间接访问这些服务。

它有哪些特性?

ApplicationContextAware是Spring框架提供的一个特殊的回调接口,用于帮助对象(特别是普通的Java Bean)访问到Spring的应用上下文ApplicationContext

当在自己的类中实现ApplicationContextAware接口时,可以通过Spring提供的回调机制访问到Spring的应用上下文,从而获得Spring IoC容器中的bean实例、配置信息以及进行国际化操作、事件发布等操作。

ApplicationContextAware接口中定义了一个setApplicationContext方法,当类实现了该接口之后,Spring IoC容器会自动调用该方法并将当前的ApplicationContext注入到所实现的setApplicationContext方法中,这样就可以在该类中使用Spring的应用上下文了。

在一些开源的Spring工具库中会看到这种技术的使用,因为这些库往往需要与Spring容器交互,比如读取容器的配置,访问其他的bean等等,通过实现ApplicationContextAware接口就可以非常方便地完成这些工作。

但注意,一般不推荐在的业务代码中使用,因为这样会增加代码与Spring的耦合性,违反了“依赖于抽象,而非依赖于具体实现”的面向对象设计原则。业务代码应当尽可能减少对具体框架的依赖,以提高代码的通用性和可移植性。

代码案例

下面是一个简单的代码案例,展示了如何使用ApplicationContextAware接口来创建一个能够访问Spring应用上下文的类,如下代码:

首先,创建一个实现ApplicationContextAware接口的类:

java">import org.springframework.beans.BeansException;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationContextAware;  
import org.springframework.stereotype.Component;  
  
@Component  
public class SpringContextUtil implements ApplicationContextAware {  
  
    // Spring应用上下文  
    private static ApplicationContext applicationContext;  
  
    // 当Spring容器创建该类的实例时,会自动调用此方法,注入应用上下文  
    @Override  
    public void setApplicationContext(ApplicationContext context) throws BeansException {  
        SpringContextUtil.applicationContext = context;  
    }  
  
    // 提供一个静态方法,返回应用上下文  
    public static ApplicationContext getApplicationContext() {  
        return applicationContext;  
    }  
  
    // 提供一个获取Bean的静态方法  
    public static <T> T getBean(Class<T> beanClass) {  
        if (applicationContext != null) {  
            return applicationContext.getBean(beanClass);  
        } else {  
            throw new IllegalStateException("ApplicationContext is not initialized yet!");  
        }  
    }  
}

接着,定义一个简单的服务类作为Spring Bean:

java">import org.springframework.stereotype.Service;  
  
@Service  
public class DemoService {  
      
    public String sayHello() {  
        return "Hello from DemoService!";  
    }  
}

然后,可以使用Spring的Java配置或者XML配置来定义和初始化这些Bean,假设使用Java配置:

java">import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
@Configuration  
public class AppConfig {  
  
    @Bean  
    public SpringContextUtil springContextUtil() {  
        return new SpringContextUtil();  
    }  
  
    @Bean  
    public DemoService demoService() {  
        return new DemoService();  
    }  
}

最后,在客户端代码中,我们可以通过SpringContextUtil类来获取DemoService的实例并调用其方法:

java">import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
  
public class ApplicationClient {  
  
    public static void main(String[] args) {  
        // 创建Spring上下文,注册Java配置类  
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  
          
        // 关闭Spring上下文,以便能够正常结束程序(尽管在这个示例中可能不需要)  
        context.registerShutdownHook();  
          
        // 通过SpringContextUtil获取DemoService实例  
        DemoService demoService = SpringContextUtil.getBean(DemoService.class);  
          
        // 调用DemoService的sayHello方法  
        System.out.println(demoService.sayHello());  
    }  
}

输出结果:

复制代码

Hello from DemoService!

以上代码展示了如何通过SpringContextUtil访问Spring的应用上下文,并获取其中的一个bean(DemoService)。

ApplicationClient类是客户端代码的入口点,它初始化了一个AnnotationConfigApplicationContext,通过它Spring可以创建和管理beans,随后客户端代码使用SpringContextUtil.getBean静态方法来获取DemoService的实例,并调用它的sayHello方法。

核心API

ApplicationContextAware接口是一个特殊的标记接口。

它允许实现类能够访问到ApplicationContext,即Spring的应用上下文,ApplicationContextAware中有且只有一个核心方法,如下是该方法的说明:

java">public interface ApplicationContextAware extends Aware {  
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;  
}

setApplicationContext方法的含义如下:

  • void setApplicationContext(ApplicationContext applicationContext) throws BeansException:
    这个方法会在Spring容器初始化的时候被自动调用,Spring容器会将当前的ApplicationContext作为参数传递给该方法,实现这个接口的类可以在这个方法中保存对ApplicationContext的引用,从而能够在后续的操作中使用这个上下文。

如下是实现ApplicationContextAware接口的类的代码案例,如下:

java">@Component  
public class MyApplicationContextAware implements ApplicationContextAware {  
    private ApplicationContext applicationContext;  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        this.applicationContext = applicationContext;  
    }  
  
    public ApplicationContext getApplicationContext() {  
        return this.applicationContext;  
    }  
}

技术原理

ApplicationContextAware接口是Spring框架中用于让Bean感知到Spring应用上下文ApplicationContext的存在的一个标记接口。

当Spring容器创建一个实现了ApplicationContextAware接口的Bean时,它会自动调用该Bean的setApplicationContext方法,并将当前的ApplicationContext作为参数传入,这使得Bean能够访问到Spring容器的上下文,进而可以获取容器中的其他Bean、资源、配置信息等。

实现原理

  1. 标记接口: ApplicationContextAware接口本身并没有定义任何具体的业务逻辑,它只是一个标记接口,用于告诉Spring容器这个Bean需要特殊处理。
  2. 容器回调: 当Spring容器初始化一个Bean时,如果这个Bean实现了ApplicationContextAware接口,Spring容器会调用该Bean的setApplicationContext方法,并将当前的ApplicationContext对象作为参数传入。
  3. 内部状态保存: 实现ApplicationContextAware接口的Bean通常会在其内部保存这个传入的ApplicationContext引用,以便后续使用。
  4. 使用上下文: 一旦Bean保存了ApplicationContext的引用,它就可以使用这个上下文来访问容器中的其他Bean,或者获取容器的其他服务。

工作机制

  1. Bean创建: 当Spring容器根据配置信息创建一个Bean实例时,它会检查这个Bean是否实现了任何Aware接口(包括ApplicationContextAware)。
  2. Aware接口处理: 如果Bean实现了Aware接口,Spring容器会调用相应的set*方法,注入相应的依赖,对于ApplicationContextAware,容器会调用setApplicationContext方法。
  3. 依赖注入: 在调用setApplicationContext方法时,Spring容器会将当前的ApplicationContext对象注入到Bean中,这个注入过程是通过Java反射机制实现的。
  4. Bean后处理: 在Bean的所有属性被设置之后,Spring容器会调用Bean的后处理器(如果配置了的话),进行额外的初始化工作,对于实现了InitializingBean接口的Bean,还会调用其afterPropertiesSet方法。
  5. 使用ApplicationContext: 一旦Bean被初始化并注入了ApplicationContext,它就可以使用这个上下文来执行各种操作,比如获取其他Bean、访问资源、读取配置等。

核心总结

Spring揭秘:ApplicationContextAware应用场景及实现原理! - 程序员古德

ApplicationContextAware接口允许开发者在Bean中轻松获取到应用上下文ApplicationContext,这使得Bean能够灵活地访问Spring容器中的其他Bean和资源,增强了组件间的解耦和可扩展性。

但是,过度使用ApplicationContextAware可能导致代码与Spring容器紧密耦合,降低了代码的可移植性和可测试性。

关注我,每天学习互联网编程技术 - 程序员古德

END!
END!
END!

往期回顾

精品文章

Spring揭秘:@import注解应用场景及实现原理!

Java并发基础:原子类之AtomicMarkableReference全面解析!

Java并发基础:concurrent Flow API全面解析

Java并发基础:CopyOnWriteArraySet全面解析

Java并发基础:ConcurrentSkipListMap全面解析


http://www.niftyadmin.cn/n/5412731.html

相关文章

使用docker 创建Oracle容器

文档地址 https://github.com/oracle/adb-free/pkgs/container/adb-free 数据库下载地址 Database Software Downloads | Oracle 工具下载地址 Instant Client for Microsoft Windows (x64) 64-bit 1、拉取oracle镜像 docker pull ghcr.io/oracle/adb-free:23.10.2.4 2、 运…

CSS复合选择器(三)

伪元素选择器 作用&#xff1a;选中元素中的一些特殊位置。 常用伪元素&#xff1a; ::first-letter 选中元素中的第一个文字。::first-line 选中元素中的第一行文字。::selection选中被鼠标选中的内容。::placeholder 选中输入框的提示文字。::before 在元素最开始的位置&…

IR 召回测试数据集——MS MARCO

如何评估召回系统的好坏&#xff1f;如何评估检索系统是否有提升&#xff1f;在任何人面前&#xff0c;空口无凭。 我们需要一把尺子来衡量。我们需要一个高质量的测试数据集合。每次都在相同的测试数据集上&#xff0c;进行评测。本篇文章介绍一个高质量的应为的测试数据集——…

OpenAI官方发文:对于马斯克起诉Opanai的看法

双方立场 马斯克的观点&#xff1a;投入资金将OpenAi并入特斯拉&#xff0c;实现从非盈利组织到盈利组织的转变。 OpenAi的观点&#xff1a;OpenAI 的使命是确保 AGI 惠及全人类&#xff0c;需要筹集资金搞研发&#xff0c;以非盈利组织模式运营&#xff0c;不急于实现盈利。盈…

判断连续数据同意特征的方法:插旗法

bool isMonotonic(int* nums, int numsSize) {int flag 2;for (int i 1; i < numsSize; i) {if (nums[i-1] > nums[i]) {if (flag 0)return false;flag 1;}else if (nums[i-1] < nums[i]) {if (flag 1)return false;flag 0;}}return true; }此代码较为简单&…

Java+SpringBoot+Vue+MySQL:农业管理新篇章

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

【物联网应用案例】从0到N,智慧农业的数据价值

智慧农业全方位渗透到农业的每一个环节&#xff0c;云端解决方案更推动了研究人员、农艺师及农民间的密切协作&#xff0c;为研发企业提供了既经济又具扩展性的完美方案。 据IDC预计&#xff0c;到2036年&#xff0c;农场收集的数据量将增加800%以上&#xff0c;这凸显了农业数…

python基础 — 进制转换

1、进制引导符号 在 Python 中&#xff0c;可以使用不同的前缀&#xff08;引导符号&#xff09;来表示不同的进制。 以下是各种进制的引导符号&#xff1a; 二进制&#xff08;Binary&#xff09;&#xff1a;使用0b或0B作为前缀。例如&#xff1a;0b1010 表示二进制数 101…