【死磕 Spring】----- IOC 之 深入分析 Aware 接口

简介:

doCreateBean() 方法主要干三件事情:

实例化 bean 对象: createBeanInstance()

属性注入: populateBean()

初始化 bean 对象: initializeBean()

而初始化 bean 对象时也是干了三件事情:

激活 Aware 方法

后置处理器的应用

激活自定义的 init 方法

接下来三篇文章将会详细分析这三件事情,这篇主要分析 Aware 接口。

Aware 接口定义如下:

/**

 * Marker superinterface indicating that a bean is eligible to be

 * notified by the Spring container of a particular framework object

 * through a callback-style method. Actual method signature is

 * determined by individual subinterfaces, but should typically

 * consist of just one void-returning method that accepts a single

 * argument.

 *

 * <p>Note that merely implementing {@link Aware} provides no default

 * functionality. Rather, processing must be done explicitly, for example

 * in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.

 * Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}

 * and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}

 * for examples of processing {@code *Aware} interface callbacks.

 *

 * @author Chris Beams

 * @since 3.1

 */

public
 
interface
 
Aware
 
{



}

Aware 接口为 Spring 容器的核心接口,是一个具有标识作用的超级接口,实现了该接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用回调的方式。

Aware 接口是一个空接口,实际的方法签名由各个子接口来确定,且该接口通常只会有一个接收单参数的 set 方法,该 set 方法的命名方式为 set + 去掉接口名中的 Aware 后缀,即 XxxAware 接口,则方法定义为 setXxx(),例如 BeanNameAware(setBeanName),ApplicationContextAware(setApplicationContext)。

Aware 的子接口需要提供一个 setXxx 方法,我们知道 set 是设置属性值的方法,即 Aware 类接口的 setXxx 方法其实就是设置 xxx 属性值的。 Aware 的含义是感知的、感应的,那么在 Spring 容器中是如何实现感知并设置属性值得呢?我们可以从初始化 bean 中的激活 Aware 的方法 invokeAwareMethods() 中看到一点点,如下:
image
首先判断 bean 实例是否属于 Aware 接口的范畴,如果是的话,则调用实例的 setXxx() 方法给实例设置 xxx 属性值,在 invokeAwareMethods() 方法主要是设置 beanName,beanClassLoader、BeanFactory 中三个属性值。

Spring 提供了一系列的 Aware 接口,如下图(部分):
image
上面只是一部分子类,从这里我们可以看到 Spring 提供的 Aware 接口是是何其多。同时从上图我们也看到了几个比较熟悉的接口,如 BeanClassLoaderAware、BeanFactoryAware、BeanNameAware,下面就这三个接口来做一个简单的演示,先看各自的定义:

public interface BeanClassLoaderAwareextendsAware{
/**
  * 将 BeanClassLoader 提供给 bean 实例回调
  * 在 bean 属性填充之后、初始化回调之前回调,
  * 例如InitializingBean的InitializingBean.afterPropertiesSet()方法或自定义init方法
  */
void setBeanClassLoader(ClassLoader classLoader);
}
public interface BeanFactoryAware extends Aware {    
/**
  * 将 BeanFactory 提供给 bean 实例回调
  * 调用时机和 setBeanClassLoader 一样
  */ 
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
public interface BeanNameAware extends Aware { 
/**
  * 在创建此 bean 的 bean工厂中设置 beanName
  */ 
void setBeanName(String name);
}

public interface ApplicationContextAware extends Aware 
{
 
/**
  * 设置此 bean 对象的 ApplicationContext,通常,该方法用于初始化对象
  */
 
void setApplicationContext(ApplicationContext applicationContext)
 
throws
 BeansException;
}

下面简单演示下上面四个接口的使用方法:

public 
class 
MyApplicationAware
implements
BeanNameAware
,
BeanFactoryAware
,
BeanClassLoaderAware
,
ApplicationContextAware
{
  
private
 
String
 beanName
;
   
private
 
BeanFactory
 beanFactory
;
  
private
 
ClassLoader
 classLoader
;
  
private
 
ApplicationContext
 applicationContext
;
 
@Override
   
public
 
void
 setBeanClassLoader
(
ClassLoader
 classLoader
)
 
{        
System
.
out
.
println
(
"调用了 BeanClassLoaderAware 的 setBeanClassLoader 方法"
);       
this
.
classLoader 
=
 classLoader
;    
}    
@Override    
public 
void
 setBeanFactory
(
BeanFactory
 beanFactory
) 
throws 
BeansException 
{        
System
.
out
.
println
(
"调用了 BeanFactoryAware 的 setBeanFactory 方法"
);        
this
.
beanFactory 
=
 beanFactory
;    
}    
@Override    
public 
void
 setBeanName
(
String
 name
) 
{        
System
.
out
.
println
(
"调用了 BeanNameAware 的 setBeanName 方法"
);        
this
.
beanName 
=
 name
;    
}   
@Override    
public 
void
 setApplicationContext
(
ApplicationContext
 applicationContext
) 
throws 
BeansException 
{        
System
.
out
.
println
(
"调用了 ApplicationContextAware 的 setApplicationContext 方法"
);        
this
.
applicationContext 
=
 applicationContext
;    
}    
public 
void
 display
(){       
System
.
out
.
println
(
"beanName:"

+
 beanName
);        
System
.
out
.
println
(
"是否为单例:" 
+
 beanFactory
.
isSingleton
(
beanName
));        
System
.
out
.
println
(
"系统环境为:" 
+
 applicationContext
.
getEnvironment
());    
}
}

测试方法如下:

public 
static 
void
 main
(
String
[]
 args
) 
{        
ClassPathResource
 resource 
= 
new
ClassPathResource
(
"spring.xml"
);        
DefaultListableBeanFactory
 factory 
= 
new
DefaultListableBeanFactory
();       
XmlBeanDefinitionReader
 reader 
= 
new 
XmlBeanDefinitionReader
(
factory
);
        reader
.
loadBeanDefinitions
(
resource
);        
MyApplicationAware
 applicationAware 
=
(
MyApplicationAware
)
 factory
.
getBean
(
"myApplicationAware"
);
        applicationAware
.
display
();    
}

运行结果:
image
从该运行结果可以看出,这里只执行了三个 Aware 接口的 set 方法,原因就是痛 getBean() 调用时在激活 Aware 接口时只检测了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 三个 Aware 接口。如果将测试方法调整为下面:

public 
static 
void
 main
(
String
[]
 args
) 
{    
ApplicationContext
 applicationContext 
= 
new 
ClassPathXmlApplicationContext
(
"spring.xml"
);       
MyApplicationAware
 applicationAware 
= 
(
MyApplicationAware
)
 applicationContext
.
getBean
(
"myApplicationAware"
);

        applicationAware
.
display
();   
}

则运行结果如下:
image
从这了我们基本上就可以 Aware 真正的含义是什么了?感知,其实是 Spring 容器在初始化主动检测当前 bean 是否实现了 Aware 接口,如果实现了则回调其 set 方法将相应的参数设置给该 bean ,这个时候该 bean 就从 Spring 容器中取得相应的资源。最后文章末尾列出部分常用的 Aware 子接口,便于日后查询:

LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ

BeanClassLoaderAware:加载Spring Bean的类加载器

BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI

ResourceLoaderAware:底层访问资源的加载器

BeanFactoryAware:声明BeanFactory

PortletConfigAware:PortletConfig

PortletContextAware:PortletContext

ServletConfigAware:ServletConfig

ServletContextAware:ServletContext

MessageSourceAware:国际化

ApplicationEventPublisherAware:应用事件

NotificationPublisherAware:JMX通知

BeanNameAware:声明Spring Bean的名字

原文发布时间为:2018-12-24
本文作者:chenssy
本文来自云栖社区合作伙伴“ Java技术驿站”,了解相关信息可以关注“chenssy89”微信公众号

相关文章
|
2天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
8天前
|
存储 安全 Java
|
17天前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
173 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
30天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
109 9
|
25天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
114 2
|
27天前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
20 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
14天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
42 0
|
22天前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
120 0
|
22天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
37 0
|
1月前
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
29 0