写在前面

最近,很多小伙伴出去面试都被问到了Spring问题,关于Spring,细节点很多,面试官也非常喜欢问一些很细节的技术点。所以,在 Spring 专题中,我们尽量把Spring的每个技术细节说清楚,将透彻。

关注 冰河技术 微信公众号,回复 “ Spring注解 ” 关键字领取源码。

如果文章对你有所帮助,欢迎大家留言、点赞、在看和转发,大家的支持是我持续创作的动力!

概述

自定义组件要想使用Spring容器底层的一些组件(比如:ApplicationContext、BeanFactory等),此时,只需要让自定义组件实现XxxAware接口即可。此时,Spring在创建对象的时候,会调用XxxAware接口定义的方法,注入相关的组件。

XxxAware接口概览

其实,我们之前使用过XxxAware接口,例如,我们之前创建的Employee类,就实现了ApplicationContextAware接口,Employee类的源码如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 测试ApplicationContextAware
*/
@Component
public class Employee implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

从Employee类的源码可以看出,实现ApplicationContextAware接口的话,需要实现setApplicationContext()方法。在IOC容器启动并创建Employee对象时,Spring会调用setApplicationContext()方法,并且会将ApplicationContext对象传入到setApplicationContext()方法中,我们只需要在Employee类中定义一个ApplicationContext类型的成员变量来接收setApplicationContext()方法的参数,就可以使用ApplicationContext对象了。

其实,在Spring中,类似于ApplicationContextAware接口的设计有很多,本质上,Spring中类似XxxAware接口都继承了Aware接口,我们来看下Aware接口的源码,如下所示。

package org.springframework.beans.factory;
/**
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public interface Aware { }

可以看到,Aware接口是Spring 3.1版本中引入的接口,在Aware接口中,并未定义任何方法。

接下来,我们看看都有哪些接口继承了Aware接口,如下所示。

XxxAware接口案例

接下来,我们就挑选几个常用的XxxAware接口来进行简单的说明。

ApplicationContextAware接口使用的比较多,我们先来说说这个接口,通过ApplicationContextAware接口我们可以获取到IOC容器。

首先,我们创建一个Blue类,并实现ApplicationContextAware接口,在实现的setApplicationContext()中将ApplicationContext输出,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; /**
* @author binghe
* @version 1.0.0
* @description 测试ApplicationContextAware接口
*/
public class Blue implements ApplicationContextAware {
private ApplicationContext applicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
}
}

我们也可以为Blue类同时实现几个XxxAware接口,例如,使Blue类再实现一个BeanNameAware接口,我们可以通过BeanNameAware接口获取到当前bean在Spring容器中的名称,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; /**
* @author binghe
* @version 1.0.0
* @description 测试ApplicationContextAware接口
*/
public class Blue implements ApplicationContextAware, BeanNameAware {
private ApplicationContext applicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
} @Override
public void setBeanName(String name) {
System.out.println("当前bean的名字");
}
}

接下来,我们再实现一个EmbeddedValueResolverAware接口,我们通过EmbeddedValueResolverAware接口能够获取到StringValue解析器。如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;
/**
* @author binghe
* @version 1.0.0
* @description 测试ApplicationContextAware接口
*/
public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
} @Override
public void setBeanName(String name) {
System.out.println("当前bean的名字");
} @Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String resolveStringValue = resolver.resolveStringValue("你好${os.name} 年龄:#{20*18}");
System.out.println("解析后的字符串为:" + resolveStringValue);
}
}

接下来,我们需要在Blue类上标注@Component注解将Blue类添加到IOC容器中,如下所示。

@Component
public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

运行AutowiredTest类的testAutowired02()方法,输出的结果信息如下所示。

postProcessBeforeInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
postProcessAfterInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
postProcessBeforeInitialization...personDao=>PersonDao{remark='1'}
postProcessAfterInitialization...personDao=>PersonDao{remark='1'}
postProcessBeforeInitialization...personDao2=>PersonDao{remark='2'}
postProcessAfterInitialization...personDao2=>PersonDao{remark='2'}
postProcessBeforeInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
postProcessAfterInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
postProcessBeforeInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
postProcessAfterInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
执行了Animal类的无参数构造方法
postProcessBeforeInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
执行了Animal类的初始化方法。。。。。
postProcessAfterInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
当前bean的名字:blue
解析后的字符串为:你好Windows 10 年龄:360
传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020
postProcessBeforeInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
postProcessAfterInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
Cat类的构造方法...
postProcessBeforeInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
Cat的postConstruct()方法...
postProcessAfterInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
调用了Dog的有参构造方法
postProcessBeforeInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessAfterInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessBeforeInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
postProcessAfterInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
postProcessBeforeInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessAfterInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
Cat的preDestroy()方法...
执行了Animal类的销毁方法。。。。。

输出结果中有如下信息。

当前bean的名字:blue
解析后的字符串为:你好Windows 10 年龄:360
传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020

说明正确的输出了结果信息。

XxxAware原理

XxxAware的底层原理是由XxxAwareProcessor类实现的, 例如,我们这里以ApplicationContextAware接口为例,ApplicationContextAware接口的底层原理就是由ApplicationContextAwareProcessor类实现的。从ApplicationContextAwareProcessor类的源码可以看出,其实现了BeanPostProcessor接口,本质上都是后置处理器。

class ApplicationContextAwareProcessor implements BeanPostProcessor

接下来,我们就以分析ApplicationContextAware接口的原理为例,看看Spring是怎么将ApplicationContext对象注入到Blue类中的。

我们在Blue类的setApplicationContext()方法上打一个断点,如下所示。

接下来,我们以debug的方式来运行AutowiredTest类的testAutowired02()方法,

这里,我们可以看到,实际上ApplicationContext对象已经注入到Blue类中的setApplicationContext()方法中了。我们在IDEA的方法调用栈中选择postProcessBeforeInitialization()方法,如下所示。

我们双击IDEA中的postProcessBeforeInitialization()方法的调用栈,会在IDEA中自动定位到postProcessBeforeInitialization()方法中,如下所示。

其实,postProcessBeforeInitialization()方法所在的类就是ApplicationContextAwareProcessor。postProcessBeforeInitialization()方法的逻辑比较简单。

我们来看下在postProcessBeforeInitialization()方法中调用的invokeAwareInterfaces()方法,如下所示。

看到这里,大家是不是有种豁然开朗的感觉!Blue类实现了ApplicationContextAware接口后,Spring为啥会将ApplicationContext对象自动注入到setApplicationContext()方法中就不用说了吧!

其实就是这么简单!

重磅福利

关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!

好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!!

写在最后

如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!

【Spring注解驱动开发】自定义组件如何注入Spring底层的组件?看了这篇我才真正理解了原理!!的更多相关文章

  1. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

  2. 【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则

    写在前面 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则.如果Spring内置的过滤规则不能够满足我们的 ...

  3. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  4. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  5. 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!

    写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...

  6. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  7. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  8. 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

    写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...

  9. 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean

    写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...

随机推荐

  1. 自已动手作图搞清楚AVL树

    @ 目录 一.背景 二.平衡二分搜索树---AVL树 2.1 AVL树的基本概念 结点 高度 平衡因子 2.2 AVL树的验证 三.旋转操作 3.1 L L--需要通过右旋操作 3.2 R R--需要 ...

  2. 三面拿杭州研究院50offer:面对这些问题,你都能做到嘛??

    5G的到来证明了互联网行业发展一如既往的快,作为一名开发人员(Java岗)梦想自然是互联网行业的大厂,这次我的一个学员拿到了杭州的一个研究院offer,透露一下,年薪50多万,保底16,在这里分享一拨 ...

  3. 数据库(十三):MySQL内置方法

    进击のpython ***** 数据库--MySQL内置方法 目录 数据库--MySQL内置方法 视图 增加 修改 删除 触发器 创建 使用 删除 存储过程 无参 有参 事务 代码实现 视图 视图是一 ...

  4. CMD运行JAVA出现“错误:编码GBK的不可映射字符”

    问题: 原因: 字符编码问题.由于java文件中有中文字符,而cmd在编译时解码默认使用GBK,所以导致无法解码出正确的中文字符. 解决办法: 使用-encoding指令指定运行编码为UTF-8.

  5. Kaggle 入门题-泰坦尼克号灾难存活预测

    这个题目的背景概况来讲就是基于泰坦尼克号这个事件,然后大量的人员不幸淹没在这个海难中,也有少部分人员在这次事件之中存活,然后这个问题提供了一些人员的信息如姓名.年龄.性别.票价,所在客舱等等一些信息, ...

  6. Python pass语句

    Python pass语句:空语句,主要用于保持程序结构的完整性 或者 函数想要添加某种功能,但是还没有想好具体应该怎么写. 在 for 循环中使用 pass: lst = [7,8,9,4] for ...

  7. java反序列化——apache-shiro复现分析

    本文首发于“合天智汇”公众号 作者:Fortheone 看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了.分析调试的shiro也是直接使用了cc链.首先先了解一些 ...

  8. fpdf fpdi 操作pdf文件 写入中文汉字

      今天给公司做一个线上合同签约功能,用户签约后 生成pdf版的文件 .网上搜了搜大概都是用fpdf进行操作的  , 通过fpdi 可以进行读取pdf模板. 在通过继承fpdf 操作现有的pdf文档 ...

  9. 解析Class文件

    类文件解析的入口是ClassFileParser类中定义的parseClassFile()方法.上一小节得到了文件字节流stream后,接着会在ClassLoader::load_classfile( ...

  10. 学习使用CompletableFuture

    CompletableFuture 一.前言 1.JDK5的异步处理方式 2.JDK8的异步处理方式 二.学习CompletableFuture 1.结果获取方式 2.创建CompletableFut ...