spring扩展点之四:Spring Aware容器感知技术,BeanNameAware和BeanFactoryAware接口,springboot中的EnvironmentAware
aware:英 [əˈweə(r)] 美 [əˈwer] adj.意识到的;知道的;觉察到的
XXXAware在spring里表示对XXX感知,实现XXXAware接口,并通过实现对应的set-XXX方法,然后就可以使用XXX了。
通俗的解释:如果在某个类里面想要使用spring的一些东西,就可以通过实行XXXAware接口告诉spring,spring会到最后给你送过来,而接收的方式是通过实现接口唯一的方法set-XXX。比如,有一个类想要使用当前的ApplicationContext,那么我们只需要让它实现ApplicationContextAware接口,然后实现接口中唯一的方法void setApplicationContext(ApplicationContext applicationContext)就可以了,spring会自动调用这个方法将applicationContext传给我们,我们只需要接收就可以了。
-----spring的回调过程分析----------------------------------------------------------
这个setApplicationContext方法的回调是容器自动完成的,容器调用该方法的时候,我们就可以将容器传入的参数applicationContext保存起来以供使用
setApplicationContext方法被容器的自动调用是在BeanPostProcessor接口的方法postProcessBeforeInitialization中完成的,实现是在ApplicationContextAwareProcessor类中,具体代码如下:
spring-context-4.3.14.RELEASE-sources.jar
package org.springframework.context.support;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
//... //这里就是容器自动调用set方法的地方,其实就是调用自定义的类实现的setApplicationContext方法,这样类就获取到了applicationContext
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
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);
}
}
}
//...
}
-----spring的回调过程分析----------------------------------------------------------
一. 点睛
Spring
的依赖注入的最大亮点就是你所有的Bean
对Spring
容器的存在是没有意识的。即你可以将你的容器替换成别的容器,例如Goggle Guice
,这时Bean
之间的耦合度很低。
但是在实际的项目中,我们不可避免的要用到Spring
容器本身的功能资源,这时候Bean
必须要意识到Spring
容器的存在,才能调用Spring
所提供的资源,这就是所谓的Spring
Aware
。其实Spring
Aware
本来就是Spring
设计用来框架内部使用的,若使用了Spring
Aware
,你的Bean
将会和Spring
框架耦合。
Spring
提供的Aware
接口如下表所示:
Spring提供的Aware接口
BeanNameAware | 获得到容器中Bean的名称 |
---|---|
BeanFactoryAware | 获得当前bean factory,这样可以调用容器的服务 |
ApplicationContextAware* | 获得当前application context,这样可以调用容器的服务 |
MessageSourceAware | 获得message source这样可以获得文本信息 |
ApplicationEventPublisherAware | 应用事件发布器,可以发布事件 |
ResourceLoaderAware | 获得资源加载器,可以获得外部资源文件 |
Spring
Aware
的目的是为了让Bean
获得Spring
容器的服务。因为ApplicationContext
接口集成了MessageSource
接口,ApplicationEventPublisherAware
接口和ResourceLoaderAware
接口,所以Bean
继承ApplicationContextAware
可以获得Spring
容器的所有服务,但原则上我们还是用到什么接口就实现什么接口。
二. 示例
1. 准备
在org.light4j.sping4.senior.aware
包下新建一个test.txt
,内容随意,给下面的外部资源加载使用。
2. Spring Aware演示Bean
package org.light4j.sping4.senior.aware; import java.io.IOException; import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service; @Service
public class AwareService implements BeanNameAware,ResourceLoaderAware{//① private String beanName;
private ResourceLoader loader; @Override
public void setResourceLoader(ResourceLoader resourceLoader) {//②
this.loader = resourceLoader;
} @Override
public void setBeanName(String name) {//③
this.beanName = name;
} public void outputResult(){
System.out.println("Bean的名称为:" + beanName); Resource resource =
loader.getResource("classpath:org/light4j/sping4/senior/aware/test.txt");
try{ System.out.println("ResourceLoader加载的文件内容为: " + IOUtils.toString(resource.getInputStream())); }catch(IOException e){
e.printStackTrace();
}
}
}
代码解释:
① 实现
BeanNameAware
,ResourceLoaderAware
接口,获得Bean
名称和资源加载的服务。
② 实现ResourceLoaderAware
需要重写setResourceLoader
方法。
③ 实现BeanNameAware
需要重写setBeanName
方法。
3. 配置类
package org.light4j.sping4.senior.aware; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("org.light4j.sping4.senior.aware")
public class AwareConfig { }
4. 运行
package org.light4j.sping4.senior.aware; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main {
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class); AwareService awareService = context.getBean(AwareService.class);
awareService.outputResult(); context.close();
}
}
运行结果如下图所示:
三、Spring的BeanNameAware和BeanFactoryAware接口
BeanNameAware
作用:让Bean获取自己在BeanFactory配置中的名字(根据情况是id或者name)。
示例:(spring自动调用setBeanName的方法,但是,Bean之中一定要有个String类型变量来保存BeanName的值,这个是在编写Bean代码时有程序员手工完成的,而不是通过什么特殊的配置。)
public class LogginBean implements BeanNameAware {
private String beanName = null;
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Spring自动调用。并且会在Spring自身完成Bean配置之后,且在调用任何Bean生命周期回调(初始化或者销毁)方法之前就调用这个方法。换言之,在程序中使用BeanFactory.getBean(String beanName)之前,Bean的名字就已经设定好了。
BeanFactoryAware
作用:让Bean获取配置他们的BeanFactory的引用。
这个方法可能是在根据某个配置文件创建了一个新工厂之后,Spring才调用这个方法,并把BeanFactory注入到Bean中。
让bean获取配置自己的工厂之后,当然可以在Bean中使用这个工厂的getBean()方法,但是,实际上非常不推荐这样做,因为结果是进一步加大Bean与Spring的耦合,而且,能通过DI注入进来的尽量通过DI来注入。
当然,除了查找bean,BeanFactory可以提供大量其他的功能,例如销毁singleton模式的Bean。
factory.preInstantiateSingletons();方法。preInstantiateSingletons()方法立即实例化所有的Bean实例,有必要对这个方法和Spring加载bean的机制做个简单说明。
方法本身的目的是让Spring立即处理工厂中所有Bean的定义,并且将这些Bean全部实例化。因为Spring默认实例化Bean的情况下,采用的是lazy机制,换言之,如果不通过getBean()方法(BeanFactory或者ApplicationContext的方法)获取Bean的话,那么为了节省内存将不实例话Bean,只有在Bean被调用的时候才实例化他们。
示例:(非spring容器的对象,调用spring bean)
@Component
public class SpringUtil implements ApplicationContextAware { private static Logger logger = LoggerFactory.getLogger(SpringUtil.class); /**
* 当前IOC
*/
private static ApplicationContext applicationContext; /*
* @param arg0
*
* @throws BeansException
*
* @see
* org.springframework.context.ApplicationContextAware#setApplicationContext
* (org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
applicationContext = arg0;
} /**
* 通过name获取 Bean.
* @param name
* @return
*/
public static Object getBean(String name){
return applicationContext.getBean(name);
} public static <T>T getBean(String id,Class<T> type){
return applicationContext.getBean(id,type);
}
}
四、EnvironmentAware接口的作用
在SpringBoot中的应用
凡注册到Spring容器内的bean,实现了EnvironmentAware接口重写setEnvironment方法后,在工程启动时可以获得application.properties的配置文件配置的属性值。
demo演示
直接上代码,比如我的application.properties文件有如下配置(这里说明一下SpringBoot应用默认的配置文件名就叫做application.properties,可以直接放在当前项目的根目录下,或者一个名叫config的子目录下)
再建一个类实现EnvironmentAware接口,其中@Configuration注解在SpringBoot里面相当于Spring的XML文件里的beans标签一样,而@Bean注解相当于XML文件里的bean标签,代表该类会被加载到Spring的IOC容器内。具体代码如下
/**
* *
* @ClassName MyProjectc.java
* @author 沉鱼
* @date 2017年11月28日 下午4:35:39
*/
@Configuration
public class MyProjectc implements EnvironmentAware { @Override
public void setEnvironment(Environment environment) {
String projectName = environment.getProperty("project.name");
System.out.println(projectName);
}
}
启动SpringBoot后,在控制台会打印
具体SpirngBoot整合Mybatis应用
application.properties文件配置
datasource.driverClassName=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost:3306/myproject?characterEncoding=utf8&serverTimezone=UTC
datasource.username=chenyu
datasource.password=123456
具体javaConfig代码
@Configuration
public class MyBatisConfig implements EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
/**
* 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名 称,该名称也就是数据源的名称)
*/
@Bean
public DataSource druidDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", environment.getProperty("datasource.driverClassName"));
props.put("url", environment.getProperty("datasource.url"));
props.put("username", environment.getProperty("datasource.username"));
props.put("password", environment.getProperty("datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
// 指定数据源(这个必须有,否则报错)
fb.setDataSource(druidDataSource());
fb.setTypeAliasesPackage("com.tf56.pushService.dal.domain");
// 指定mapper文件
fb.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
return fb.getObject();
} }
五、从spring源码中看aware接口在bean加载时的调用情况
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
spring扩展点之四:Spring Aware容器感知技术,BeanNameAware和BeanFactoryAware接口,springboot中的EnvironmentAware的更多相关文章
- Spring Aware容器感知技术
Spring Aware是什么 Spring提供Aware接口能让Bean感知Spring容器的存在,即让Bean可以使用Spring容器所提供的资源. Spring Aware的分类 几种常用的Aw ...
- Spring 读书笔记-----使用Spring容器(一)
pring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spri ...
- (转)Spring 读书笔记-----使用Spring容器(一)
Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spr ...
- Spring IOC源代码具体解释之容器依赖注入
Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...
- 死磕Spring之IoC篇 - Spring 应用上下文 ApplicationContext
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)
本章是<spring4.1.8扩展实战>的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再 ...
- Spring扩展之五:Aware接口等
ApplicationContextAwareProcessor 1.介绍 ApplicationContextAwareProcessor是一个Spring内部工具,它实现了接口BeanPostPr ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- 【spring源码分析】IOC容器初始化——查漏补缺(一)
前言:在[spring源码分析]IOC容器初始化(十一)中提到了初始化bean的三个步骤: 激活Aware方法. 后置处理器应用(before/after). 激活自定义的init方法. 这里我们就来 ...
随机推荐
- PHP开发环境搭建(转载)
转载自:http://blog.csdn.net/rosetta/article/details/53967215 前言 最近学了n种语言,学每种语言的套路无非就是先搭建一个开发环境,再找本书或者 ...
- 特性,批次特性建立的BAPI函數
[转http://taijizhang.blog.163.com/blog/static/176071381201442225514453/] SAP特性,物料特性,批次特性建立的BAPI函數 类的T ...
- linux shell 基础 使用日志与心得
linux shell 基础 使用日志与心得 1.#!/bin/bash 第一行就出现#!/bin/bash是指此脚本使用/bin/bash来解释执行.其中,#!是一个特殊的表示符,其后,跟着解释此脚 ...
- PAT 天梯赛 L2-017. 人以群分 【排序】
题目链接 https://www.patest.cn/contests/gplt/L2-017 思路 第一个条件是 人群的规模尽可能接近 那么 N 为偶数的时候 就是 一半 一半 N 为奇数的时候 就 ...
- 每天一个Linux命令(36)ps命令
Linux中的ps命令是Process Status的缩写. ps命令用于报告当前系统的进程状态.可以搭配kill指令随时中断.删除不必要的程序. (1)用法: ...
- Eclipse cdt mingw配置记录
本人下载的是Eclipse C/C++ IDE for Neon.3,下载页面是:http://www.eclipse.org/cdt/downloads.php. 1. 运行eclipse后,在He ...
- 苹果企业账号发布APP详解——通过自己网站分发应用
一.通过企业账号申请证书 1 Certificate Signing Request (CSR)文件 在Mac系统中进入“钥匙串访问”,选择“钥匙串访问”-“证书助理”-“从证书颁发机构请求证书…”, ...
- P2487 [SDOI2011]拦截导弹
题目 P2487 [SDOI2011]拦截导弹 做\(SDOI\)有种想评黑的感觉,果然还是太弱了 做法 独立写(调)代码三个小时祭 简化题目:求二维最长不上升子序列及每个点出现在最长不上升子序列概率 ...
- 前端绘图方式Canvas和SVG的区别
Canvas和SVG是html5中支持2种可视化技术,都是可以在画布上绘制图形和放入图片.下面来介绍和分析一下他们. 一.Canvas 和 SVG 简介 1.什么是Canvas? Canvas 是H5 ...
- jquery获取表单元素与回显
一.获取哦表单元素 dcoument表单文本对象的集合 all[] 对所有html元素的访问 forms 返回对文档中所有form对象的引用 forms[1] 对所有form对象引用 <scri ...