Aware接口

在Spring中有许多的Aware接口,提供给应用开发者使用,通过Aware接口,我们可以通过set的方式拿到我们需要的bean对象(包括容器中提供的一些对象,ApplicationContext等),根据需要可以将其注入到本地对象的属性中。

先来看一个Spring两个基础的接口

  • BeanPostProcessor
  • BeanFactoryPostProcessor

    简单来讲:

    BeanPostProcessor增加bean的信息,比如 autowaired 或 aware接口注入bean对象的属性

    BeanFactoryPostProcessor就用于增加BeanDefinition(配置元数据)的信息 比如添加bean的工程,提供一个带BeanFactory参数的回调接口,说通俗一些就是可以管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性。

    这两个接口,都是通过bean的形式添加到spring IOC容器中,即它们本身也是作为一种bean,而只不过这两种bean提供一些特殊的用途而已。

    有区别是的:BeanPostProcessor是在createBean的时候触发,BeanFactoryPostProcessor会被提前调用用来修改BeanDefinition。

BeanPostProcessor

bean的后置处理器接口,在依赖注入的初始化方法前后进行调用

public interface BeanPostProcessor {

	/**
* 初始化方法调用前要进行的处理逻辑
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} /**
* 在初始化方法指定后要进行的处理逻辑
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
} }

所有Aware接口本身是一种修改bean信息的功能接口,但它自身并不会被触发,既然是修改bean属性的功能接口,所以它应该被一个BeanPostProcessor调用———— *AwareProcessor(例如ApplicationContextAwarePostProcessor)

Bean在创建过程,会在实例化后,如果实现了BeanPostProcessor接口,会在实例化后统一调用BeanPostProcessor接口



在我们日常开发中,可以通过在bean中实现ApplicationContextAware的方式将ApplicationContext注入到我们的bean对象中去

在Spring中其实是有一个BeanPostProcessor类——ApplicationContextAwareProcessor,用来处理实现了ApplicationContextAware等接口的bean。

ApplicationContextAware类源码

public interface ApplicationContextAware extends Aware {

	/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

可以看到此aware接口主要是为了在实现类中注入applicationContext,提供这么一个回调方法

ApplicationContextAwareProcessor类源码

class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* 将Context注入进来
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
/**
* 接口beanPostProcessor规定的方法,会在bean创建时,实例化后,初始化前,对bean对象应用
*/
@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)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
// 检测bean上是否实现了某个aware接口,有的话进行相关的调用
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
} return bean;
} /**
* 如果某个bean实现了某个aware接口,给指定的bean设置相应的属性值
*
* @param bean
*/
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
} }

在ApplicationContextAwareProcessor中我们可以看到,其实它处理了好几个aware接口,所以当我们的Bean只要实现了以下任意的接口,都会在invokeAwareInterfaces方法中被处理

  • EnvironmentAware注入环境属性,可以拿到系统属性 环境属性 ,还有import进去的自定义的属性文件;
  • EmbeddedValueResolverAware,处理属性资源中的${}这种表达式,将当前EnvironmentAware中的属性用来处理${}表达式;
  • ResourceLoaderAware 获取资源加载器,用它可以动态地添加资源;
  • ApplicationEventPublisherAware 处理事件发布 与ApplicationEvent ApplicationListener一起使用
  • MessageSourceAware 处理资源国际化
  • ApplicationContextAware 注入ApplicationContext,可以getBean等操作

当然看到这个地方,肯定有人会疑惑,这个ApplicationContextAwareProcessor,我们并没有在代码中将其实例化为一个Bean。为什么只要实现了ApplicationContextAware接口的Bean对象,就可以注入进来ApplicationContext对象。

其实在Spring源码中在准备beanFactory的时候就已经将ApplicationContextAwareProcessor添加到beanPostProcessor List列表中了

	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks.
// 添加beanPostProcessor,ApplicationContextAwareProcessor此类用来完成某些Aware对象的注入
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

一、实现SpringUtils工具类

我们希望在自定义的SpringUtils中注入一个ApplicationContext对象,能够在代码里通过getBean的形式来获取IOC容器的中Bean对象

再次感谢spring的开发团队,因为spring的易扩展性,它为我们提供了超级简单的方法来获取applicationContext对象

不多说,看代码

@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext; /**
* 注入 applicationContext
*
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
} /**
* 获取指定类的bean对象
*
* @param clazz 类类型
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
} /**
* 获取指定类的指定名称的bean对象
*
* @param name bean名称
* @param clazz 类类型
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return applicationContext.getBean(name, clazz);
}
}

一个静态的SpringUtils就这样写好了,是不是So Easy???

二、自定义aware接口,并能够在bean中自动注入自定义对象

第1步 实现aware接口,定义好需要注入的对象及其相应方法

public interface GlobalSessionAware extends Aware {
/**
* 注入全局的session
*
* @param session
*/
public void setGlobalSession(GlobalSession session);
}

第2步 实现BeanPostProcessor,并回调aware接口中的set方法,将BeanPostProcessor作为一个bean对象,加入到spring容器中

@Component
public class GlobalSessionAwarePostProcessor implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Object session = this.applicationContext.getBean("globalSession");
if (session == null) {
return bean;
}
if (session instanceof GlobalSession && bean instanceof GlobalSessionAware) {
((GlobalSessionAware) bean).setGlobalSession((GlobalSession) session);
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

第3步 使用GlobalSession,在需要注入GlobalSession的bean中实现GlobalSessionAware接口

@Component
public class WebTT implements GlobalSessionAware {
private GlobalSession session; /**
* GlobalSession
*
* @param session
*/
@Override
public void setGlobalSession(GlobalSession session) {
this.session = session;
}
}

到此已经实现了aware接口的自定义,这个地方我们需要知道的是,Awarer接口本身属于修改bean信息的一种功能性接口,但它需要在创建bean时能够被调用到,所以这个地方BeanPostProcessor就能够实现这个功能

Spring扩展——Aware接口的更多相关文章

  1. 学习 Spring (五) Aware 接口

    Spring入门篇 学习笔记 Spring 中提供了一些以 Aware 结尾的接口,实现了 Aware 接口的 bean 在被初始化之后可以获取相应资源 通过 Aware 接口,可以对 Spring ...

  2. Spring 学习——Aware接口

    Aware 作用 Spring中提供了一些以Aware结尾的接口,实现了Aware接口的Bean在初始化后,可以通过一些接口获取相应的资源. 通过Aware接口,可以对Spring的资源进行一些操作( ...

  3. spring(五):spring中Aware接口的使用

    spring中自定义组件需要使用spring的底层组件时,可以通过自定义组件实现相关XxxAware接口,重写其中的方法进而实现 例如:自定义一个组件,该组件中需要使用ApplicationConte ...

  4. spring4.1.8扩展实战之二:Aware接口揭秘

    Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就 ...

  5. spring扩展点之四:Spring Aware容器感知技术,BeanNameAware和BeanFactoryAware接口,springboot中的EnvironmentAware

    aware:英 [əˈweə(r)] 美 [əˈwer] adj.意识到的;知道的;觉察到的 XXXAware在spring里表示对XXX感知,实现XXXAware接口,并通过实现对应的set-XXX ...

  6. Spring扩展之五:Aware接口等

    ApplicationContextAwareProcessor 1.介绍 ApplicationContextAwareProcessor是一个Spring内部工具,它实现了接口BeanPostPr ...

  7. spring中的aware接口

    1.实现了相应的aware接口,这个类就获取了相应的资源. 2.spring中有很多aware接口,包括applicationContextAware接口,和BeanNameAware接口. 实现了这 ...

  8. spring学习六----------Bean的配置之Aware接口

    © 版权声明:本文为博主原创文章,转载请注明出处 Aware Spring提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化后,可以获取相应的资源 通过Aware接口,可以对S ...

  9. spring源码:Aware接口(li)

    一.spring容器中的aware接口介绍 Spring中提供了各种Aware接口,比较常见的如BeanFactoryAware,BeanNameAware,ApplicationContextAwa ...

  10. Spring中Aware相关接口原理

    Spring中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.ServletContextA ...

随机推荐

  1. 5年磨一剑|优酷Android包瘦身治理思路全解

    简介: 稳定性.性能.包大小,在移动端基础用户体验领域"三分天下",是app承载业务获得稳定.高效.低成本.快速增长的重要基石.其中,包大小对下载转化率.拉新拉活成本等方面的影响至 ...

  2. Quick BI电子表格: 新手亦可表格自由

    ​简介: 随着企业业务快速增长,单纯的表或交叉表展现的数据模式相对固定,已不能满足企业中不同角色用户.不同业务场景数据可视化分析展现的诉求.在满足业务人员可视化需求层面,Quick BI不仅提供了丰富 ...

  3. [FE] 实时视频流库 hls.js 重载切换资源的方式

    hls 播放需要先 attachMedia,然后 loadSource. 如果切换 resource,需要先执行 hls.destroy(),否则会出现混乱. destroy 之后再依次进行 hls ...

  4. dotnet OpenXML 读取 PPT 形状边框定义在 Style 的颜色画刷

    本文来和大家聊聊在 PPT 形状使用了 Style 样式的颜色画刷读取方法 在开始之前,期望大家已了解如何在 dotnet 应用里面读取 PPT 文件,如果还不了解读取方法,请参阅 C# dotnet ...

  5. LSP(Language Server Protocol)简介

    概述 Language Server Protocol(LSP)是微软2016年提出的一项通讯协议方案.该方案定义了一套协议,用于在IDE或编辑器和提供代码补全.转到定义等功能的Language Se ...

  6. 一个库帮你轻松的创建漂亮的.NET控制台应用程序

    前言 做过.NET控制台应用程序的同学应该都知道原生的.NET控制台应用程序输出的内容都比较的单调,假如要编写漂亮且美观的控制台输出内容或者样式可能需要花费不少的时间去编写代码和调试.今天大姚给大家分 ...

  7. 入门Semantic Kernel:OneApi集成与HelloWorld

    引言 从这一章节开始正式进入我们的 Semantic Kernel 的学习之旅了. 什么是Semantic Kernel? Semantic Kernel是一个轻量级的开源框架,通过 Semantic ...

  8. ansible系列(24)--ansible的loop循环语句

    目录 1. loop循环语句 1.1 使用循环批量安装软件 1.2 使用循环批量启动服务 1.3 使用循环批量创建用户 1.4 使用循环批量拷贝文件 1. loop循环语句 在写 playbook 的 ...

  9. DNS(5) -- 主从dns

    目录 1. DNS主从服务器 1.1 主从服务器概述 1.2 主从DNS实现 1.2.1 主从DNS实现要点 1.2.2 主DNS服务器配置 1.2.3 从DNS服务器配置 1.2.4 测试主从同步 ...

  10. SpringBoot连接redis报错:exception is io.lettuce.core.RedisException: java.io.IOException: 远程主机强迫关闭了一个现有的连接

    一.解决思路 (1).检查redis的配置是否正确 spring redis: host: localhost port: 6379 password: 123456 database: 0 time ...