【Spring/SpringBoot源码】“感应器”-Aware分析

【Spring/SpringBoot源码】“感应器”-Aware分析

Scroll Down

文章目录

前言

在通常情况下,Spring容器的中的Bean对象是无法直接获取容器的相关属性的,我们都知道这么做是为了解耦。但在某些特殊情况下,我们为了某些特殊功能,可能要求bean持有一些容器属性,于是Spring就给我们提供了Aware接口,它允许实现它的bean获取容器的相关属性。

使用Aware的优缺点:

  • 优点:使Bean能感知到Spring容器,可以调用Spring容器的资源。
  • 缺点:增加了Bean和Spring容器之间的耦合性。

SpringBoot Version:2.1.7

Aware接口介绍

先来看看Spring自带的几种Aware接口:

image.png

再解释下这些Aware接口的含义:

Aware接口作用
BeanNameAware获取bean在容器中的beanName
BeanClassLoaderAware获取类加载器
BeanFactory获取创建bean的工厂
EnvironmentAware获取环境变量
EmbeddedValueResolverAware获取Spring容器加载的properties文件属性值
ResourceLoaderAware获取资源加载器
ApplicationEventPublisherAware获取事件发布(广播)器
MessageSourceAware获取消息处理器
ApplicationContextAware获取上下文

实验代码

两个Bean:

TestBean.java


import lombok.Data;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component("testBean")
@Data
public class TestBean implements BeanNameAware, EnvironmentAware, MyAware {

    private String beanName;
    private Environment environment;
    private TestBean2 testBean2;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setTestBean2(TestBean2 testBean2) {
        this.testBean2 = testBean2;
    }
}

TestBean2.java


import lombok.Data;
import org.springframework.stereotype.Component;

@Component("testBean2")
@Data
public class TestBean2 {
    // 自定义划水bean
}

再写一个启动加载器,方便启动后控制台能看到效果:

ResultCommandLineRunner.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class ResultCommandLineRunner implements CommandLineRunner {

    @Autowired
    private TestBean testBean;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(testBean.toString());
    }
}

自定义Aware接口:

MyAware.java


import org.springframework.beans.factory.Aware;

public interface MyAware extends Aware {
    void setTestBean2(TestBean2 testBean2);
}

最后的最后还需要一个后置处理器,用于处理我们自定义的Aware:

MyAwareProcessor.java


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyAwareProcessor implements BeanPostProcessor {

    private final ConfigurableApplicationContext configurableApplicationContext;

    public MyAwareProcessor(ConfigurableApplicationContext configurableApplicationContext) {
        this.configurableApplicationContext = configurableApplicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Aware) {
            if (bean instanceof MyAware) {
                ((MyAware) bean).setTestBean2((TestBean2) configurableApplicationContext.getBean("testBean2"));
            }
        }
        return bean;
    }
}
  • 你可能疑惑为什么自定义的Aware需要BeanPostProcessor实现

看看控制台输出,Aware确实都生效了:


TestBean(beanName=testBean, environment=StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='server.ports'}, ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}]}, testBean2=TestBean2())

跟进源码

这次跟进源码的顺序跟之前可能有些不一样,因为几个关键的回调函数都藏得太深了,所以这次反过来,从回调函数往回跟。

当然,如果你真的非常想知道整个流程(SpringApplication#run -> Aware回调),那么你可跟着栈帧信息追,比如下面这个样子,本篇博客只记录一些关键流程:

方法栈帧链.png

在启动应用前,我打了几个断点,方便调试:

断点.png

之后启动程序,就能进入setBeanName函数处的断点了:
image.png

返回上一层,来到invokeAwareMethods:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#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) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}
  • 就是判断实现关系,然后回调对应覆写的实现方法,因为我们只实现了BeanNameAware,所以这段代码中只回调了setBeanName方法。

继续返回上一层:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)


	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 刚刚从这里出来
			// BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 后置处理器回调
			// 其中ApplicationContextAwareProcessor就回调了如下Aware的实现:
			// EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware
			// ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

跟进applyBeanPostProcessorsBeforeInitialization方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization


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

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
  • 看到这里遍历BeanPostProcessor了吗,这就是为什么我们实验代码需要后置处理器。

看看我们当前有哪些后置处理器(getBeanPostProcessors返回):
image.png

  • 在这里,我们只需关注ApplicationContextAwareProcessor、MyAwareProcessor。

我们先跟进ApplicationContextAwareProcessor的postProcessBeforeInitialization实现:
org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization


	@Override
	@Nullable
	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((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			// 来到这里
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

跟进invokeAwareInterfaces方法,发现就是回调剩余SpringBoot自带Aware:

	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);
			}
		}
	}

当上面applyBeanPostProcessorsBeforeInitialization方法的for循环轮到MyAwareProcessor且bean为testBean时,就会进入:
image.png

可以发现相关源码都是回调,没什么好说的,看了这个源码,也只是有助于我们了解它的回调机制、顺序罢了,日常开发看文档应该也知道各个Aware是个什么用法。

那我为什么还要来特地看看呢?因为后面看SpringBoot源码的时候遇到了,想了解下它的回调机制、如何自定义Aware,于是就写下了这篇博客。

一个可能会踩的坑

虽说Aware可以获取Spring容器中的其它属性/组件,但并不是所有属性/组件都需要通过实现Aware获取,就比如实验代码TestBean中的·Environment·,要让TestBean持有Environmentbean,我们可以改成如下代码,控制台输出不变:

@Component("testBean")
@Data
public class TestBean implements BeanNameAware, EnvironmentAware, MyAware {

    private String beanName;
    @Autowired
    private Environment environment;
    private TestBean2 testBean2;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

//    @Override
//    public void setEnvironment(Environment environment) {
//        this.environment = environment;
//    }

    @Override
    public void setTestBean2(TestBean2 testBean2) {
        this.testBean2 = testBean2;
    }
}