如何让 Bean 深度感知 Spring 容器

news2024/11/28 19:30:45

Spring 有一个特点,就是创建出来的 Bean 对容器是无感的,一个 Bean 是怎么样被容器从一个 Class 整成一个 Bean 的,对于 Bean 本身来说是不知道的,当然也不需要知道,也就是 Bean 对容器的存在是无感的。

但是有时候我们可能会遇到一些场景,这些场景让我们去感知容器的存在,松哥举几个例子:

  1. Spring 容器提供的功能不止 IoC、AOP 这些,常见的 I18N 也是 Spring 的能力之一,如果我们想要在自己的 Bean 中去使用 I18N,那就得去找 Spring,这样就感知到了 Spring 容器的存在了。
  2. Spring 提供了资源加载器,如果我们想要使用这个资源加载器去加载配置,那就得去找 Spring 要,这样就感知到了 Spring 容器的存在了。
  3. 想根据 beanName 去 Spring 容器中查找 Bean,那不用多说,肯定得知道 Spring 容器的存在。

也就是说,虽然 Spring 中的 Bean 可以不用去感知 Spring 容器的存在,但是在实际开发中,我们往往还是需要 Spring 容器提供的各种能力,这样就迫使我们的 Bean 不得不去感知到 Spring 容器的存在。

那么 Spring 中的 Bean 如何感知到 Spring 容器的存在呢?

1. Aware

Aware 本身就有感知的意思。

Spring Aware 是 Spring 框架中的一个特性,它允许我们的应用程序或组件与 Spring 容器进行交互。当一个类实现了 Spring Aware 接口并注册到 Spring 容器中时,该类就能够感知到 Spring 容器的存在,并且可以获取容器的一些资源或进行一些特定的操作。

Spring Aware 接口包括了多个子接口,每个子接口对应于不同的 Spring 容器资源或功能。

Aware 的实现有很多,大的方向来说主要有如下一些:

每一个 Aware 的作用如下:

  • ApplicationEventPublisherAware:实现该接口的对象可以获取事件发布的能力。
  • ServletContextAware:实现该接口的对象可以获取到 ServletContext 对象。
  • MessageSourceAware:实现该接口的对象可以获取到 MessageSource 对象,MessageSource 支持多消息源,主要用于主要用于国际化。
  • ResourceLoaderAware:实现该接口的对象可以获取到一个 ResourceLoader,Spring ResourceLoader 则为我们提供了一个统一的 getResource() 方法来通过资源路径检索外部资源,例如文本文件、XML 文件、属性文件或图像文件等。
  • ApplicationStartupAware:实现该接口的对象可以获取到一个 ApplicationStartup 对象,这个比较新,是 Spring 5.3 中新推出的,通过 ApplicationStartup 可以标记应用程序启动期间的步骤,并收集有关执行上下文或其处理时间的数据。
  • NotificationPublisherAware:实现该接的对象可以获取到一个 NotificationPublisher 对象,通过该对象可以实现通知的发送。
  • EnvironmentAware:实现该接口的对象可以获取到一个 Environment 对象,通过 Environment 可以获取到容器的环境信息。
  • BeanFactoryAware:实现该接口的对象可以获取到一个 BeanFactory 对象,通过 BeanFactory 可以完成 Bean 的查询等操作。
  • ImportAware:实现该接口的对象可以获取到一个 AnnotationMetadata 对象,ImportAware 接口是需要和 @Import 注解一起使用的。在 @Import 作为元注解使用时,通过 @Import 导入的配置类如果实现了 ImportAware 接口就可以获取到导入该配置类接口的数据配置。
  • EmbeddedValueResolverAware:实现该接口的对象可以获取到一个 StringValueResolver 对象,通过 StringValueResolver 对象,可以读取到 Spring 容器中的 properties 配置的值(YAML 配置也可以)。
  • ServletConfigAware:实现该接口的对象可以获取到一个 ServletConfig 对象,不过这个似乎没什么用,我们很少自己去配置 ServletConfig。
  • LoadTimeWeaverAware:实现该接口的对象可以获取到一个 LoadTimeWeaver 对象,通过该对象可以获取加载 Spring Bean 时织入的第三方模块,如 AspectJ 等。
  • BeanClassLoaderAware:实现该接口的对象可以获取到一个 ClassLoader 对象,ClassLoader 能干嘛不需要我多说了吧。
  • BeanNameAware:实现该接口的对象可以获取到一个当前 Bean 的名称。
  • ApplicationContextAware:实现该接口的对象可以获取到一个 ApplicationContext 对象,通过 ApplicationContext 可以获取容器中的 Bean、环境等信息。

通过实现这些接口,我们可以在应用程序中获取 Spring 容器提供的各种资源,并与容器进行交互,以实现更灵活和可扩展的功能。

2. 实践

举两个例子小伙伴们来感受下 Aware 的具体用法。

2.1 案例

例如我想在 Bean 中感知到当前 Bean 的名字,那么我们可以按照如下方式来使用:

@Service
public class UserService implements BeanNameAware {
    private String beanName;
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

    @Override
    public String toString() {
        return "UserService{" +
                "beanName='" + beanName + '\'' +
                '}';
    }
}

让当前 bean 实现 BeanNameAware 接口,并重写 setBeanName 方法,这个方法会在 Spring 容器初始化 Bean 的时候自动被调用,我们就可以据此获取到 bean 的名称了。

再比如我想做一个工具 Bean,用来查找其他 Bean,那么我可以使用如下方式:

@Component
public class BeanUtils implements BeanFactoryAware {
    private static BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public static  <T> T getBean(Class<T> clazz) {
        return (T) beanFactory.getBean(clazz);
    }
}

让当前 Bean 实现 BeanFactoryAware 接口并重写 setBeanFactory 方法,在系统初始化当前 Bean 的时候,会自动调用 setBeanFactory 方法,进而将 beanFactory 变量传进来。

2.2 原理

当 Spring 容器创建一个 Bean 的时候,大致的流程是创建实例对象 -> 属性填充 -> Bean 初始化

最后这个 Bean 的初始化,就是调用 init 方法、afterPropertiesSet 方法以及 BeanPostProcessor 中的方法的,如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	invokeAwareMethods(beanName, bean);
	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}
	try {
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	return wrappedBean;
}

在这个方法一进来,首先有一个 invokeAwareMethods,这个就是用来触发 Aware 的,来看下:

private void invokeAwareMethods(String beanName, Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof BeanNameAware beanNameAware) {
			beanNameAware.setBeanName(beanName);
		}
		if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
			ClassLoader bcl = getBeanClassLoader();
			if (bcl != null) {
				beanClassLoaderAware.setBeanClassLoader(bcl);
			}
		}
		if (bean instanceof BeanFactoryAware beanFactoryAware) {
			beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
		}
	}
}

小伙伴们可以看到,BeanNameAware、BeanClassLoaderAware 以及 BeanFactoryAware 这三种类型的 Aware 是在这里触发的。

每种 Aware 因为功能不同,因此作用的时机也不同。

invokeAwareMethods 方法执行完毕之后,接下来是执行 applyBeanPostProcessorsBeforeInitialization 方法,这个我们之前分析过,这个方法最终会触发 BeanPostProcessor#postProcessBeforeInitialization 方法的执行,而 BeanPostProcessor 有一个子类专门处理 Aware 的,就是 ApplicationContextAwareProcessor:

@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
			bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
			bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
			bean instanceof ApplicationStartupAware)) {
		return bean;
	}
	invokeAwareInterfaces(bean);
	return bean;
}
private void invokeAwareInterfaces(Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof EnvironmentAware environmentAware) {
			environmentAware.setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
			embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware resourceLoaderAware) {
			resourceLoaderAware.setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware applicationEventPublisherAware) {
			applicationEventPublisherAware.setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware messageSourceAware) {
			messageSourceAware.setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationStartupAware applicationStartupAware) {
			applicationStartupAware.setApplicationStartup(this.applicationContext.getApplicationStartup());
		}
		if (bean instanceof ApplicationContextAware applicationContextAware) {
			applicationContextAware.setApplicationContext(this.applicationContext);
		}
	}
}

大家看下,这七种类型的 Aware 是在这里被触发的。

另外像 ImportAware 是在 ImportAwareBeanPostProcessor#postProcessBeforeInitialization 方法中处理的;LoadTimeWeaverAware 是在 、LoadTimeWeaverAwareProcessor#postProcessBeforeInitialization 方法中处理的。

基本上,大部分的 Aware 接口都是在 BeanPostProcessor 中处理的。

好啦,现在小伙伴们理解 Aware 了吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1166372.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mac matplotlib显示中文

以下默认字体&#xff0c;在mac ventura上测试能成功显示中文&#xff1a; import matplotlib.pyplot as plt import matplotlib#from matplotlib import font_manager #plt.rcParams[font.sans-serif] [Heiti TC]#plt.rcParams[font.sans-serif] [Songti SC]#plt.rcParams[f…

goquery库编写程序

goquery库的爬虫程序&#xff0c;该程序使用Go来爬取视频。。 package main ​ import ("fmt""net/http""net/http/httputil""io/ioutil""log""strings""golang.org/x/net/proxy""golang.org/x/n…

C++失传千年经典系列(二):类

C失传千年经典系列(一):基础语法认知 忙着去耍帅,后期补充完整..............

Clion 下载、安装、使用教程,附详细图文(2023年亲测可用)

文章目录 一、下载Clion二、安装教程三、安装MinGW方法一、直接下载MinGW安装① 下载MinGW② 配置Clion 方法二、使用Dev cpp安装① 安装Dev cpp② 配置Clion 四、常用快捷键 大家好&#xff0c;今天为大家带来的是 Clion 的下载&#xff0c;安装&#xff0c;使用教程&#xff…

3.vue3项目(三):路由配置,登录页面搭建:登录功能调用,登录后的消息提示,登录时的表单校验

一、模板的路由的配置 首先我们需要登录页,首页,404页面,任意路由。 1.安装依赖 pnpm install vue-router 2.新建三个页面 新建登录页面、首页、404页面。 在src下面新建views文件夹,然后分别新建login,home,404三个文件夹,然后每个文件夹内新建一个index.vue。我们这…

社区智能奶柜,未来市场新机遇

我们无法左右大局&#xff0c;但可以通过对时代趋势的深入理解&#xff0c;精准把握机遇&#xff0c;乘势而上&#xff01;未来优秀的商业项目&#xff0c;将遵循以下几个标准&#xff1a;产品具有高频需求、刚性需求、高毛利空间和低人力成本。社区智能奶柜之所以能在当前市场…

【入门Flink】- 02Flink经典案例-WordCount

WordCount 需求&#xff1a;统计一段文字中&#xff0c;每个单词出现的频次 添加依赖 <properties><flink.version>1.17.0</flink.version></properties><dependencies><dependency><groupId>org.apache.flink</groupId><…

enum和Collection.stream()你这样用过么

最近在做一个数据图表展示的功能&#xff0c;显示订单近七天或者近半月的数量和金额。可以理解成下图所示的样子&#xff1a; 我是用枚举和集合的stream方法实现的数据初始化和组装&#xff0c;枚举用来动态初始化时间范围&#xff0c;集合的stream方法来将初始化的数据转换成…

《自制编程语言基于c语言》读书笔记

前言&#xff1a; 很久之前&#xff0c;我在双十一的时候入手了一本《自制编程语言基于c语言》。这本书是写《操作系统真象还原》的作者。我当时看他的关于操作系统的这本书&#xff0c;非常不错&#xff0c;就连着这本书一起入了。但是后面&#xff0c;因为各种事情&#xff…

龙芯浏览器是哪家公司开发的?支持信创吗?

最近看到不少小伙伴在问&#xff0c;龙芯浏览器是哪家公司开发的&#xff1f;支持信创吗&#xff1f;这里我们小编就跟大家一起来看看&#xff0c;仅供参考哈&#xff01; 龙芯浏览器是哪家公司开发的&#xff1f; 龙芯浏览器是由龙芯中科牵头&#xff0c;基于主流的渲染引擎G…

学习笔记|多独立样本秩和检验|克鲁斯卡尔-沃利斯检验|多个组间的多重比较|规范表达|《小白爱上SPSS》课程:SPSS第十四讲 | 多独立样本秩和检验如何做?

目录 学习目的软件版本原始文档多独立样本秩和检验一、实战案例读数据&#xff1a; 二、统计策略三、SPSS操作1、正态性检验2、多个独立样本的秩和检验3、多个组间的多重比较 四、结果解读第一&#xff0c;描述性统计结果。 第二 &#xff0c;给出的是不同训练年限各自的样本量…

了解数据库设计,轻轻松松提高工作效率

每个应用程序&#xff0c;无论大小&#xff0c;最终都需要一个数据库来持久保存所有重要数据。对此没有任何争论&#xff01; 什么是数据库设计&#xff1f; 数据库设计是帮助创建、实施和维护企业数据管理系统的一系列步骤的集合。设计数据库的主要目的是为所建议的数据库系统…

拓世法宝 | 数字经济崛起,美业如何抓住流量风口?

爱美之心&#xff0c;人皆有之。无论男女&#xff0c;都会很自然地对美好事物燃起兴致&#xff0c;跟高颜值相关的事物总能聚集注意力。例如直播平台里的美女网红收割流量赚得盆满钵满&#xff0c;面庞俊俏的年轻偶像吸引万千粉丝&#xff0c;还有“央视最美记者”王冰冰、“最…

R语言爬虫代码模版:技术原理与实践应用

目录 一、爬虫技术原理 二、R语言爬虫代码模板 三、实践应用与拓展 四、注意事项 总结 随着互联网的发展&#xff0c;网络爬虫已经成为获取网络数据的重要手段。R语言作为一门强大的数据分析工具&#xff0c;结合爬虫技术&#xff0c;可以让我们轻松地获取并分析网络数据。…

驱动大健康行业数字化转型升级,开利网络与艾博盾达成战略合作

在当今这个数字化飞速发展的时代&#xff0c;各行各业都在寻求数字化转型的创新突破口&#xff0c;以适应日新月异的变化。近日&#xff0c;开利网络科技与广东艾博盾举行战略合作签约仪式&#xff0c;期望基于开利网络在数字化营销领域多年沉淀的经验&#xff0c;为企业数字化…

【hcie-cloud】【1】华为云Stack解决方案介绍、华为文档获取方式 【上】

文章目录 华为文档获取方式前言云计算发展背景国家政策、社会发展驱动数字经济开启新时代深化数字化转型提升效率&#xff0c;国家数字主权云进入落地阶段从Cloud-Based到Cloud-Native&#xff0c;两种模式长期并存适合政企智能升级的云华为云Stack&#xff0c;政企智能升级首选…

Spring Security使用总结一,简单的引用Spring Security,坐着火箭就入门了

最近因为一些不能言语的原因&#xff0c;研究了一下Spring Security。因为感觉在使用上来说难度不大&#xff0c;所以把我研究的使用过程总结一下&#xff0c;放上来供大家学习研究思考进步的。我的思路大致就是&#xff0c;一个东西拿过来&#xff0c;先研究怎么使用&#xff…

数据库进阶教学——事务

目录 一、事务简介 二、事务操作方式 1、设置事务提交方式 1.1、命令 1.2、示例 2、开启事务 2.1、命令 2.2、示例 三、事务四大特性 四、并发事务问题 五、事务隔离级别 5.1、命令 5.2、示例 5.2.1、脏读 5.2.2、不可重复读 5.2.3、幻读 一、事务简介 事务是…

亚马逊、美客多卖家测评:如何建立养号团队实现运营化式测评?

大家好&#xff0c;我是跨境电商测评养号7年从事经验的珑哥。养号环境软件开发&#xff0c;深度解决各跨境平台矩阵养号防关联、砍单、F号问题。关注珑哥解决更多跨境养号测评问题。 测评&#xff0c;相信这个词对于大部分跨境卖家来说&#xff0c;想必都不陌生&#xff0c;因…

voronoi diagram(泰森多边形) 应用 - Good Manners

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 voronoi 图求解点击前往 题目链接&#xff1a;https://vjudge.net/problem/URAL-1504 题目大意 有一个桌子&#xff0c;形状是圆形。 桌上放着很多蛋糕&#xff0c…