[源码系列:手写spring] IOC第十一节:Aware接口
内容介绍
Aware简洁
在Spring框架中,Aware接口是一个非常有用的工具,用于实现Bean与Spring容器及其他资源之间的集成。Aware接口是一个标记接口,其中定义了各种Aware子接口,每个子接口对应一种资源。这些子接口的命名约定为"XXXAware",其中XXX表示资源的名称。
Aware接口的核心思想是将依赖注入的能力扩展到Bean,使其可以主动获取特定类型的资源或信息,而不需要在Bean定义中显式配置这些资源。这种方式有助于提高代码的可维护性,减少Bean之间的耦合度,使得Bean的配置更加灵活。
使用案例
当在生产中使用Spring框架时,经常会遇到需要访问应用程序环境配置信息的情况。这可以通过实现`EnvironmentAware`接口来实现。以下是一个更常见且实用的示例,说明了如何使用`EnvironmentAware`接口以及如何访问应用程序的环境配置信息。
EnvironmentAware 接口介绍:
EnvironmentAware是Spring框架中的一个Aware接口,用于允许Bean访问应用程序的环境配置信息。这包括了属性文件、系统属性、环境变量等。通过实现EnvironmentAware接口,Bean可以轻松地获取这些配置信息而无需显式读取它们。
案例:
假设我们正在开发一个在线商店应用程序,我们希望配置一些与支付相关的属性,如支付网关的URL、商户ID等。这些属性通常会在应用程序的配置文件中定义。我们可以使用`EnvironmentAware`接口来实现此目标。
首先,定义一个PaymentService类并实现`org.springframework.context.EnvironmentAware`接口:
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
public class PaymentService implements EnvironmentAware {
private Environment environment;
public void processPayment(double amount) {
String paymentGatewayUrl = environment.getProperty("payment.gateway.url");
String merchantId = environment.getProperty("payment.merchant.id");
// 使用获取到的配置信息进行支付处理
System.out.println("Processing payment of $" + amount + " using gateway URL: " + paymentGatewayUrl);
System.out.println("Merchant ID: " + merchantId);
// 具体的支付逻辑
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
接下来,在Spring配置文件中定义Bean,并设置相应的属性:
<!-- 配置PaymentService Bean -->
<bean id="paymentService" class="com.example.PaymentService" />
<!-- 定义应用程序环境配置 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:payment.properties" />
</bean>
在上面的示例中,我们定义了一个PaymentService,该Bean实现了EnvironmentAware接口以获得Environment实例。在processPayment方法中,我们使用environment.getProperty方法获取了支付网关URL和商户ID的值。这些值在应用程序的配置文件(例如,payment.properties)中定义。
现在,我们可以轻松地访问应用程序的环境配置信息,而无需硬编码这些值。这种方式使得配置信息的管理更加灵活,可以根据不同的部署环境轻松切换配置文件,而不需要修改Bean的代码。
常见Aware接口
Spring框架中有许多Aware接口,用于帮助Bean与Spring容器及其他资源进行集成。以下是几个常见的Aware接口及其简单介绍:
| 接口名 | 用途 |
|---|---|
| BeanNameAware | 允许Bean获取自己在Spring容器中的Bean名称。这可以用于在Bean内部使用其名称进行一些自定义的逻辑处理。 |
| ApplicationContextAware | 允许Bean获取对Spring应用程序上下文(容器)的引用。这使得Bean能够访问容器中的其他Bean和资源。 |
| EnvironmentAware | 允许Bean获取对应用程序环境配置的访问权限。通过此接口,Bean可以轻松访问属性文件、系统属性、环境变量等配置信息。 |
| ServletContextAware | 允许Bean获取对Servlet上下文的引用。这对于与Web应用程序相关的Bean非常有用,例如访问Servlet上下文中的资源和属性。 |
| ResourceLoaderAware | 允许Bean获取对Spring资源加载器(ResourceLoader)的引用。通过ResourceLoader,Bean可以加载类路径、文件系统等各种资源。 |
| MessageSourceAware | 允许Bean获取对Spring的国际化(i18n)消息源的引用。这对于在Bean中处理本地化的消息非常有用。 |
| BeanFactoryAware | 允许Bean获取对Bean工厂的引用。虽然ApplicationContext通常用于创建Bean,但在某些情况下,BeanFactory可能更合适,因此此接口可以用来获取BeanFactory的引用。 |
| EmbeddedValueResolverAware | 允许Bean获取对属性值解析器(PropertyPlaceholderHelper)的引用,用于解析属性占位符(如${property})。 |
| ApplicationEventPublisherAware | 允许Bean获取对应用程序事件发布器的引用。Bean可以使用此接口来发布自定义的应用程序事件。 |
| ServletConfigAware | 允许Bean获取对Servlet配置的引用。这在Web应用程序中非常有用,例如获取Servlet初始化参数。 |
这些Aware接口提供了不同类型的访问权限,帮助Bean与Spring容器及其他资源进行集成,提高了应用程序的灵活性和可维护性。根据具体的需求,可以选择实现适当的Aware接口以满足应用程序要求。
目前为止,bean的生命周期如下:

当前bean的生命周期
在本节文章中,我们主要实现两个Aware接口
- BeanFactoryAware接口:
实现对BeanFactory的感知,具体实现查看AbstractAutowireCapableBeanFactory#initializeBean前三行。
- ApplicationContextAware接口:
实现对ApplicationContex的感知,是通过BeanPostProcessor。由当前bean的生命周期可以看出,bean实例化后会经过BeanPostProcessor的前置处理和后置处理。
定义一个BeanPostProcessor的实现类ApplicationContextAwareProcessor,在AbstractApplicationContext#refresh方法中加入到BeanFactory中,在前置处理中为bean设置所属的ApplicationContext,然后通过调用Bean的setApplicationContext方法,将ApplicationContext设置给这个Bean。这样,Bean就可以在运行时感知到其所在的ApplicationContext了。
代码分支
核心代码
AbstractAutowireCapableBeanFactory
....
protected Object initializeBean(String beanName, Object bean,BeanDefinition beanDefinition){
if(bean instanceof BeanFactoryAware){
((BeanFactoryAware)bean).setBeanFactory(this);
}
//执行BeanPostProcessor的前置处理
....
AbstractApplicationContext
....
@Override
public void refresh() throws BeansException {
//创建BeanFactory,并加载BeanDefinition
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//添加ApplicationContextAwareProcessor,让继承自ApplicationContextAware的bean能感知bean
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
....
测试
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="helloService" class="service.HelloService"/>
</beans>
HelloService
public class HelloService implements ApplicationContextAware, BeanFactoryAware {
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
public String hello() {
System.out.println("hello word!");
return "hello word!";
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
@Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
AwareInterfaceTest
public class AwareInterfaceTest {
@Test
public void test() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
HelloService helloService = applicationContext.getBean("helloService", HelloService.class);
System.out.println("ApplicationContext: "+helloService.getApplicationContext());
System.out.println("BeanFactory: "+helloService.getBeanFactory());
}
}
测试结果
ApplicationContext: org.springframework.context.support.ClassPathXmlApplicationContext@11531931
BeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@5e025e70
[源码系列:手写spring] IOC第十一节:Aware接口的更多相关文章
- Spring源码分析 手写简单IOC容器
Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...
- 《四 spring源码》手写springioc框架
手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- Spring 源码 (1)Spring IOC Bean 创建的整体流程
Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...
- 从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...
- 框架源码系列六:Spring源码学习之Spring IOC源码学习
Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的 1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...
- 《四 spring源码》手写springmvc
手写SpringMVC思路 1.web.xml加载 为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...
- Spring源码 20 手写模拟源码
参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...
- 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下
再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...
- Spring源码剖析3:Spring IOC容器的加载过程
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
随机推荐
- 使用Redis防止重复发送RabbitMQ消息
问题 今天遇到一个问题,发送MQ消息的时候需要保证不会重复发送,注意不是可靠到达(可靠到达可以通过消息确认机制和回调接口保证),这里保证的是不会生产多条一样的消息. 方法 综合讨论下来决定使用Redi ...
- 小程序分享pdf文件(uniapp)
share(){ wx.downloadFile({ url: '', // 下载url success (res) {// 下载完成后转发 wx.shareFileMessage({ filePat ...
- 给DevOps加点料:融入安全性的DevSecOps
从前,安全防护只是特定团队的责任,在开发的最后阶段才会介入.当开发周期长达数月.甚至数年时,这样做没什么问题:但是现在,这种做法现在已经行不通了.采用 DevOps 可以有效推进快速频繁的开发周期(有 ...
- CDS标准视图:会计员 I_AccountingClerk
视图名称:会计员 I_AccountingClerk 视图类型:基础 视图代码: 点击查看代码 @AbapCatalog: { sqlViewName: 'IFIACCCLERK', // compi ...
- Synchronized的各场景使用方法(多窗口售票例子接上篇)
同步锁机制:在<Thinking in Java>中,是这么说的:对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争). 防止这种冲突的方法就是当资源被一个 ...
- 工具推荐 | Xshell全版本解密工具(包括Xshell7)——SharpXDecrypt
声明 本程序仅供个人恢复密码使用! 用户滥用造成的一切后果与作者无关! 使用者请务必遵守当地法律! 本程序不得用于商业用途,仅限学习交流! 请在下载后24小时内删除!如果代码中存在侵权行为,请联系我们 ...
- FastReport实现遍历Dataset数据集计算
delphi在使用fastreport进行打印时,需要对数据进行计算求和. 在打印文本框的OnBeforePrint事件中进行以下代码即可实现效果 procedure Memo7OnBeforePri ...
- Linux部署Python项目
项目部署 项目部署对于操作通常是由运维人员来进行统一管理装配的,但是对于一个开发人员来讲,基础的项目部署还是要会的. 这里我主要讲解python的项目是如何部署的,至于项目哪里来的,这就要看观众如何抉 ...
- 7.1 闲话-Erdős–Gallai 定理和哈基米算法
Erdős–Gallai 定理 前几天考试有一个建出最大流模型,转为最小割,然后模拟最小割的套路. 这一个套路并不是少见的.在 Gale-Ryser 定理和 Erdős–Gallai 定理的证明都体现 ...
- docker官网镜像无法下载问题解决
亲测可用,这个方法是由技术爬爬虾大佬提供,简单地说就是通过github上的docker_image_pusher项目,将国外docker镜像转存到阿里云私人仓库. 此方法需要你有一个github账号, ...
https://github.com/yihuiaa/little-spring/tree/aware-interface