Spring中Aware接口 -【Spring底层原理】

一、概述

不管是我们平时开发中,还是在看spring源码中,都会遇到Aware这个接口,Aware的英文意思:意识到,察觉到,发觉,发现。从英文翻译来看,Aware做的事情应该是发现某一个东西。

注释的大致意思是:Aware是一个标记性的超接口(顶级接口),指示了一个Bean有资格通过回调方法的形式获取Spring容器底层组件。实际回调方法被定义在每一个子接口中,而且通常一个子接口只包含一个接口一个参数并且返回值为void的方法。

说白了:只要实现了Aware子接口的Bean都能获取到一个Spring底层组件。

自定义组件时,想要使用spring容器底层的一些组件,比如ApplicationContext、Beanfactory,xxx等,只需要让自定义组件实现xxxAware,在对象实例化的时候,会把spring底层的一些组件注入到自定义的bean中。通过查看源码,可以看到有这么多的接口,每个接口都有都对应spring相应的底层,比如:

  • 实现BeanNameAware接口的bean:获取BeanName
  • 实现BeanFactoryAware接口的bean:取到BeanFactory组件对象
  • 实现EnvironmentAware接口的bean:获取到Environment组件对象
  • 实现XXXAware接口的bean:通过实现的setXXX方法就可以获取到XXX组件对象

image-20210314154105087

二、实例分析

这里就以一些常用的接口进行举例,实现其接口,通过这些接口使用spring底层的一些组件

// 启动类
@Test
public void TestMain() {
    // 创建IOC容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
}

// 待注入的bean
@Component
public class User implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    private ApplicationContext applicationContext;
    // 通过上下文环境对象得到Spring容器中的Bean
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的IOC:" + applicationContext);
        this.applicationContext = applicationContext;
    }
    // 获取bean的名字
    public void setBeanName(String s) {
        System.out.println("当前bean的名字:" + s);
    }
    // 解析一些字符串、占位符等
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        String resolveStringValue = stringValueResolver.resolveStringValue("你好${os.name}");
        System.out.println("解析的字符串是:" + resolveStringValue);
    }
}

// 配置类
@Configuration
public class AppConfig {
    @Bean
    public User User(){
        return new User();
    }
}

运行启动类,可以看到输出结果如下:

image-20210314225234698

这里对Aware的三个接口进行了举例,分别是ApplicationContextAware、BeanNameAware、EmbeddedValueResolverAware

  • ApplicationContextAware:通过上下文环境对象得到Spring容器中的Bean
  • BeanNameAware:获取bean的名字
  • EmbeddedValueResolverAware:解析一些字符串、占位符等

三、源码追踪

其实每一个子接口,都是利用相应的xxxProcess来进行处理的,也就是相应的后置处理器,而这些xxxProcess都是BeanPostProcess的接口,比如ApplicationContextAware就是通过ApplicationContextAwareProcess来进行处理的,ApplicationContextAwareProcess实现了BeanPostProcess

这里就以ApplicationContextAware,通过Debug调试进行源码追踪,看看是如何给User把ApplicationContext给注入进来的:

在setApplicationContext方法进行断点调试:

image-20210314232448824

通过方法调用栈,可以看到ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法,

image-20210314232914353

  • 判断是否是xxxAware接口,判断是,则继续往下
  • 通过invokeAwareInterfaces里面进行注入
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 判断是否是xxxAware接口,判断是,则继续往下
    if (!(bean instanceof EnvironmentAware) && !(bean instanceof EmbeddedValueResolverAware) && !(bean instanceof ResourceLoaderAware) && !(bean instanceof ApplicationEventPublisherAware) && !(bean instanceof MessageSourceAware) && !(bean instanceof ApplicationContextAware) && !(bean instanceof ApplicationStartupAware)) {
        return bean;
    } else {
        AccessControlContext acc = null;
        if (System.getSecurityManager() != null) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged(() -> {
                this.invokeAwareInterfaces(bean);
                return null;
            }, acc);
        } else {
            // 在这个方法进行相应的注入
            this.invokeAwareInterfaces(bean);
        }

        return bean;
    }
}

点开invokeAwareInterfaces方法,这个bean参数就是User对象,将需要注入的bean传入,做如下处理:

  1. 判断是否是xxxAware接口
  2. 如果是,则获取xxx,调用setxxx方法进行注入

这里以ApplicationContextAware为例,实现了ApplicationContextAware接口,则将User对象转成ApplicationContextAware接口调用setApplicationContext方法进行注入

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 ApplicationStartupAware) {
        ((ApplicationStartupAware)bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
    }

    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
    }

}

四、总结

  • bean在初始化的时候利用xxxProcess后置处理器判断这个bean是否是xxxAware接口
  • 如果是,则调用相应的set方法传入组件
end
  • 作者:ONESTAR(联系作者)
  • 更新时间:2021-03-15 13:35
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论

    新增邮件回复功能,回复将会通过邮件形式提醒,请填写有效的邮件!