SpringIoc容器之Aware | 京东云技术团队

news2024/11/25 14:58:36

1 前言

Aware是Spring提供的一个标记超接口,指示bean有资格通过回调样式的方法由Spring容器通知特定的框架对象,以获取到容器中特有对象的实例的方法之一。实际的方法签名由各个子接口确定,但通常只包含一个接受单个参数的void返回方法。

2 Spring中9个Aware内置实现

|--Aware
    |--BeanNameAware
    |--BeanClassLoaderAware
    |--BeanFactoryAware
    |--EnvironmentAware
    |--EmbeddedValueResolverAware
    |--ResourceLoaderAware
    |--ApplicationEventPublisherAware
    |--MessageSourceAware
    |--ApplicationContextAware



9个内置实现又分两类,前三个为直接调用,后6个通过ApplicationContextAwareProcessor后置处理器,间接回调

2.1 BeanNameAware

public interface BeanNameAware extends Aware {

       /**
        *设置创建此bean的bean工厂中的bean的名称。
        *在普通bean属性填充之后但在
        *初始化之前回调,如{@link InitializingBean#afterPropertiesSet()}
        *或自定义初始化方法。
        * @param name工厂中bean的名称。
        *注意,此名称是工厂中使用的实际bean名称,这可能
        *与最初指定的名称不同:特别是对于内部bean
        * names,实际的bean名称可以通过添加
        *“#…”后缀。使用{@link BeanFactoryUtils#originalBeanName(String)}
        *方法提取原始bean名称(不带后缀),如果需要的话。
        * /
	void setBeanName(String name);

}



实现BeanNameAware接口需要实现setBeanName()方法,这个方法只是简单的返回我们当前的beanName,这个接口表面上的作用就是让实现这个接口的bean知道自己在spring容器里的名字,而且官方的意思是这个接口更多的使用在spring的框架代码中,实际开发环境应该不建议使用,因为spring认为bean的名字与bean的联系并不是很深,(的确,抛开spring API而言,我们如果获取了该bean的名字,其实意义不是很大,我们没有获取该bean的class,只有该bean的名字,我们也无从下手,相反,因为bean的名称在spring容器中可能是该bean的唯一标识,也就是说再beanDefinitionMap中,key值就是这个name,spring可以根据这个key值获取该bean的所有特性)所以spring说这个不是非必要的依赖。

2.2 BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware {

   /**
    *提供bean {@link ClassLoader}类加载器的回调
    *一个bean实例在属性的填充之后但在初始化回调之前调用
    * {@link InitializingBean
    * {@link InitializingBean#afterPropertiesSet()}
    *方法或自定义初始化方法。
    * @param类加载器拥有的类加载器;可能是{@code null}在例如,必须使用默认的{@code ClassLoader}
    * 获取的{@code ClassLoader}
    * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
    * /
   void setBeanClassLoader(ClassLoader classLoader);

}



在bean属性填充之后初始化之前,提供类加制器的回调。让受管Bean本身知道它是由哪一类装载器负责装载的。

2.3 BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   /**
    * 为bean实例提供所属工厂的回调。
    * 在普通bean属性填充之后调用但在初始化回调之前,如
    * {@link InitializingBean#afterPropertiesSet()}或自定义初始化方法。
    * @param beanFactory拥有beanFactory(非空)。bean可以立即调用工厂上的方法。
    * @在初始化错误时抛出BeansException
    * @参见BeanInitializationException
    * /
   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}



在bean属性填充之后初始化之前,提bean工厂的回调。实现 BeanFactoηAware 接口的 bean 可以直接访问 Spring 容器,被容器创建以后,它会拥有一个指向 Spring 容器的引用,可以利用该bean根据传入参数动态获取被spring工厂加载的bean

2.4 EnvironmentAware

public interface EnvironmentAware extends Aware {

   /**
    * 设置该对象运行的{@code环境}。
    */
   void setEnvironment(Environment environment);

}



设置该对象运行的。所有注册到 Spring容器内的 bean,只要该bean 实现了 EnvironmentAware接口,并且进行重写了setEnvironment方法的情况下,那么在工程启动时就可以获取得 application.properties 的配置文件配置的属性值,这样就不用我们将魔法值写到代码里面了。

2.5 EmbeddedValueResolverAware

public interface EmbeddedValueResolverAware extends Aware {

   /**
    * 设置StringValueResolver用于解析嵌入的定义值。
    */
   void setEmbeddedValueResolver(StringValueResolver resolver);

}



在基于Spring获取properties文件属性值的时候,一般使用@Value的方式注入配置文件属性值,但是@Value必须要在Spring的Bean生命周期管理下才能使用,比如类被@Controller、@Service、@Component等注解标注。如有的抽象类中,基于Spring解析@Value的方式,使用EmbeddedValueResolverAware解析配置文件来实现。

2.6 ResourceLoaderAware

public interface ResourceLoaderAware extends Aware {

   /**
    *设置该对象运行的ResourceLoader。这可能是一个ResourcePatternResolver,它可以被检查
    *通过{@code instanceof ResourcePatternResolver}。另请参阅
    * {@code ResourcePatternUtils。getResourcePatternResolver}方法。
    * <p>在填充普通bean属性之后但在init回调之前调用
    *像InitializingBean的{@code afterPropertiesSet}或自定义初始化方法。
    *在ApplicationContextAware的{@code setApplicationContext}之前调用。
    * @param resourceLoader该对象使用的resourceLoader对象
    * @ @ springframework.core. io.support.resourcepatternresolver
    * @ @ resourcepatternutils #获取resourcepatternresolver
    * /
   void setResourceLoader(ResourceLoader resourceLoader);

}



ResourceLoaderAware 是特殊的标记接口,它希望拥有一个 ResourceLoader 引用的对象。当实现了 ResourceLoaderAware接口的类部署到application context(比如受Spring管理的bean)中时,它会被application context识别为 ResourceLoaderAware。 接着application context会调用setResourceLoader(ResourceLoader)方法,并把自身作为参数传入该方法(记住,所有Spring里的application context都实现了ResourceLoader接口)。

既然 ApplicationContext 就是ResourceLoader,那么该bean就可以实现 ApplicationContextAware接口并直接使用所提供的application context来载入资源,但是通常更适合使用特定的满足所有需要的 ResourceLoader 实现。 这样一来,代码只需要依赖于可以看作辅助接口的资源载入接口,而不用依赖于整个Spring ApplicationContext 接口。

2.7 ApplicationEventPublisherAware

public interface ApplicationEventPublisherAware extends Aware {

   /**
    *设置该对象运行的ApplicationEventPublisher。
    * <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。
    *在ApplicationContextAware的setApplicationContext之前调用。
    *该对象使用的事件发布者
    * /
   void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}



ApplicationEventPublisherAware 是由 Spring 提供的用于为 Service 注入 ApplicationEventPublisher 事件发布器的接口,使用这个接口,我们自己的 Service 就拥有了发布事件的能力。

2.8 MessageSourceAware

public interface MessageSourceAware extends Aware {

   /**
    *设置该对象运行的MessageSource。
    * <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。
    *在ApplicationContextAware的setApplicationContext之前调用。
    * @param messageSource消息源
    * /
   void setMessageSource(MessageSource messageSource);

}



获得message source这样可以获得文本信息,使用场景如为了国际化。

2.9 ApplicationContextAware

public interface ApplicationContextAware extends Aware {

   /**
    *设置该对象运行的ApplicationContext。通常这个调用将用于初始化对象。
    * <p>在普通bean属性填充之后但在init回调之前调用
    *作为{@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
    *或自定义初始化方法。在{@link ResourceLoaderAware#setResourceLoader}之后调用,
    * {@link ApplicationEventPublisherAware#setApplicationEventPublisher}和
    * {@link MessageSourceAware},如果适用。
    * @param applicationContext该对象将使用的applicationContext对象
    * @在上下文初始化错误时抛出ApplicationContextException如果由应用程序上下文方法抛出,则抛出BeansException
    * @see org.springframework.beans.factory.BeanInitializationException
    * /
   void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}



ApplicationContextAware的作用是可以方便获取Spring容器ApplicationContext,从而可以获取容器内的Bean。ApplicationContextAware接口只有一个方法,如果实现了这个方法,那么Spring创建这个实现类的时候就会自动执行这个方法,把ApplicationContext注入到这个类中,也就是说,spring 在启动的时候就需要实例化这个 class(如果是懒加载就是你需要用到的时候实例化),在实例化这个 class 的时候,发现它包含这个 ApplicationContextAware 接口的话,sping 就会调用这个对象的 setApplicationContext 方法,把 applicationContext Set 进去了。

3 Spring中调用时机

Aware接口由Spring在AbstractAutowireCapableBeanFactory.initializeBean(beanName, bean,mbd)方法中通过调用invokeAwareMethods(beanName, bean)方法和applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)触发Aware方法的调用

image.png

3.1 invokeAwareMethods

private void invokeAwareMethods(final String beanName, final Object 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);
      }
   }
}



判断并直接回调

3.2 applyBeanPostProcessorsBeforeInitialization

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      result = beanProcessor.postProcessBeforeInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}



通过ApplicationContextAwareProcessor.postProcessBeforeInitialization(Object bean, String beanName)间接调用,并在方法invokeAwareInterfaces中进行回调。

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
   AccessControlContext acc = null;

   if (System.getSecurityManager() != null &&
         (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
               bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
               bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
      acc = this.applicationContext.getBeanFactory().getAccessControlContext();
   }

   if (acc != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareInterfaces(bean);
            return null;
         }
      }, acc);
   }
   else {
      invokeAwareInterfaces(bean);
   }

   return bean;
}

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(
               new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
      }
      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);
      }
   }
}



4 总结

通过上面的分析,可以知道Spring生命周期中的初始化方法里,在真正执行初始化方法之前,分别通过invokeAwareMethods方法和后置处理器ApplicationContextAwareProcessor来触发Aware的调用,那么,Spring为什么要使用两种方式而不使用其中之一呢?

通过本章我们了解了9中内置接口的作用,以及它们能够获取到的不同上下文信息。

作者:京东零售 曾登均

来源:京东云开发者社区

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

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

相关文章

【接口测试】Postman —— 接口测试知识准备

1.0 前言 ​应用程序编程接口&#xff08;Application Programming Interface, API&#xff09;是这些年来最流行的技术之一&#xff0c;强大的Web应用程序和领先的移动应用程序都离不开后端强大的API。API技术的应用给系统开发带来了便利&#xff0c;但也对测试人员提出了更高…

[JAVA数据结构]HashMap

目录 1.HashMap 1.1Map的常用方法 1.2HashMap的使用案例 1.HashMap 基于哈希表的实现的Map接口。 Map底层结构HashMap底层结构哈希桶插入/删除/查找时间复杂度O(1)是否有序无序线程安全不安全插入/删除/查找区别通过哈希函数计算哈希地址比较与覆写自定义类型需要覆写equal…

leetcode.1504. 统计全 1 子矩形(单调栈-java)

统计全 1 子矩形 leetcode.1504. 统计全 1 子矩形题目描述单调栈解题代码演示 单调栈专题 leetcode.1504. 统计全 1 子矩形 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/count-submatrices-with-all-ones 题目描述 给…

pytest之配置文件pytest.ini

前言&#xff1a; pytest.ini文件是pytest的主配置文件&#xff0c;可以改变pytest的运行方式&#xff0c;它是一个固定的文件pytest.ini文件&#xff0c;读取配置信息&#xff0c;按指定的方式去运行。 pytest.ini文件的位置一般放在项目的根目录下&#xff0c;不能随便放&a…

Vant入门介绍

Vant 介绍 Vant是有赞开源的一套基于Vue2.0的Mobile组件库。Vant 是一套轻量、可靠的移动端组件库。通过 Vant&#xff0c;可以搭建出风格统一的&#xff0c;提升开发效率。通过Vant,可以搭建出风格统一的,提升开发效率。目前已有近50个组件,这些组件被广泛使用于有赞的各个移…

LVS-DR排错思路

一.LVS-DR实验排错思路 1.案例架构图 DR服务器&#xff1a;192.168.27.11 web服务器1&#xff1a;192.168.27.12 web服务器2&#xff1a;192.168.27.13 vip&#xff1a;192.168.27.180 客户端&#xff1a;192.168.27.2 1&#xff1a;配置负载调度器&#xff1a;&#xff…

【集群】脑裂是什么?Zookeeper是如何解决的?

文章目录 什么是脑裂Zookeeper集群中的脑裂场景过半机制 什么是脑裂 脑裂(split-brain)就是“大脑分裂”&#xff0c;也就是本来一个“大脑”被拆分了两个或多个“大脑”&#xff0c;我们都知道&#xff0c;如果一个人有多个大脑&#xff0c;并且相互独立的话&#xff0c;那么…

[HDCTF2019]MFC

前言 mfc逆向&#xff0c;有一个VM壳一看到它就头疼&#xff0c;好在这道题用不到&#xff0c;可以直接通过xspy获取mfc自定义消息 分析 工具下载&#xff1a; https://bbs.kanxue.com/thread-170033.htm 开始时完全没有头绪&#xff0c;有虚拟壳&#xff0c;用ida打开也看…

AMEYA360:Panasonic松下HF系列压敏电阻器

Panasonic HF系列压敏电阻器符合AEC-Q200标准&#xff0c;最大允许额定电压为16VDC&#xff0c;钳位电压高达43A。这些紧凑型SMD压敏电阻器适合用于汽车应用&#xff0c;采用模制结构&#xff0c;因此能够耐受很强的“焊接热冲击”。这些压敏电阻器符合ISO7637-2和ISO16750-2负…

迪赛智慧数——饼图(玫瑰饼图):抑郁症发病群体年龄

效果图 痛心&#xff0c;震惊了全网&#xff0c;著名歌手李玟&#xff0c;抑郁症自杀离世&#xff01; 为什么看起来阳光开朗的人&#xff0c;也会得抑郁症&#xff1f;据数据调查显示&#xff0c;15-30岁为抑郁症的高发年纪&#xff0c;由于思想不够成熟&#xff0c;经验少&a…

QT开发技巧之QComboBox通过qss设置item高度,增加间隔

1.问题描述 QComboBox默认的下拉item间距太小&#xff0c;字挤在一起不好看&#xff0c;直接qss设置item高度但是没效果 2.解决后效果 可通过qss设置item的最小高度&#xff0c;增加间距&#xff0c;不同字体大小的combobox都能使用&#xff0c;简单方便 3.代码实现 &#xf…

葡萄牙语翻译,北京哪个公司比较好?

近年来&#xff0c;随着中国与各葡语系国家&#xff0c;特别是与巴西经贸、科技、文化交流的不断扩大&#xff0c;葡萄牙语翻译的需求也越来越大&#xff0c;但是专业葡语翻译人才紧缺。那么&#xff0c;如何做好葡萄牙语翻译&#xff0c;北京葡语翻译公司哪家好&#xff1f; 我…

分布式消息服务设计

分布式消息服务设计 背景 为了解决当A系统的一个“操作”需要发送一个通知&#xff08;生产者&#xff09;&#xff0c;由关心这个操作的业务&#xff08;消费者&#xff09;订阅消息并处理时&#xff0c;实现业务解耦&#xff0c;并适合分布式。本文主要讲解以消息中间件Rab…

Debug_性能分析工具_Perf +

目录 1. perf 作用 2. perf 安装 3. perf 使用示例 3.1 耗时统计&#xff0c;画出火焰图 1. 下载绘制火焰图的开源pl代码 2. 执行以下命令 3. 用浏览器打开svg文件&#xff0c;看当前进程各子模块耗时占比 1. perf 作用 Perf 是Linux kernel自带的系统性能优化工具。 P…

HCIA-HarmonyOS Application Developer学习笔记

目录 一、HarmonyOS 介绍二、HarmonyOS 应用开发流程HarmonyOS 系统架构HarmonyOS 子系统集DevEco StudioHarmonyOS 应用包结构使用资源文件的方法权限管理分布式能力 三、Ability 设计与开发Ability 的概念和分类页面生命周期Intent载体页面间导航Particle Ability 开发 四、U…

使用Vue脚手架2

ref属性 src/components/SchoolName.vue <template><div class"school"><h2>学校名称&#xff1a;{{name}}</h2><h2>学校地址&#xff1a;{{address}}</h2></div> </template><script>export default {name:…

【C++修炼之路】31.异常

每一个不曾起舞的日子都是对生命的辜负 异常 一.C语言传统的处理错误的方式二.C异常概念三.异常的使用3.1 异常的抛出和捕获3.2 异常的重新抛出3.3 异常安全3.4 异常规范 四.自定义异常体系五.C标准库的异常体系六.异常的优缺点 一.C语言传统的处理错误的方式 传统的错误处理机…

Idea运行springboot项目(保姆级教学)

**大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯…

PHP:对比数据库,隐藏不满足条件的标签;对比数据库,将标签中的内容更改为数据库中的对应项

一、查询数据库数据&#xff0c;A列数据如果等于前端div中的值&#xff0c;那么将div中的值给其赋值为对应数据库中B列的值 如下案例&#xff1a;以第一个为例&#xff0c;如果id为append1&#xff0c;并且此id等于满足条件的数据库中colum_name列中的append1&#xff0c;就将…

Windows安装 PostgreSQL数据库并进行简单数据操作

介绍&#xff1a; 在开发和管理数据库应用程序时&#xff0c;选择合适的数据库管理系统是至关重要的。PostgreSQL 是一个强大且广受欢迎的开源关系型数据库管理系统&#xff0c;它具有稳定性、可靠性和丰富的功能。本文将介绍如何在 Windows 操作系统上下载、安装 PostgreSQL&a…