一、介绍

  使用spring开发的同学,或多或少都使用过形如XxxAware这样的接口。spring文档中是这样解释Aware接口的:

Spring提供了广泛的Aware回调接口,让bean向容器表明它们需要某种基础设施依赖。

通常Aware有这样一个规则:Aware接口的名称,表示依赖对象的类名称。
例如,一个bean需要使用ApplicationContext,实现ApplicationContextAware接口即可。

二、使用demo

功能:我的一个bean想要使用ApplicationContext对象,来查看容器中有哪些bean

  1. 定义一个bean的类
    如上规则,想要使用ApplicationContext对象,就要实现ApplicationContextAware接口
public class MyApplicationContextAware implements ApplicationContextAware {
    // 用一个全局变量,保存接口回调时传入的applicationContext对象
    private ApplicationContext applicationContext;

	// 使用获取的applicationContext对象
    public void printAllBean(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("当前容器中的bean如下:");
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
  1. 把MyApplicationContextAware这个bean注册到容器中
<bean id="myApplicationContextAware" class="com.kaka.spring.context.aware.MyApplicationContextAware"/>
  1. 执行代码
    @Test
    public void awareTest(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        MyApplicationContextAware myApplicationContextAware = classPathXmlApplicationContext.getBean("myApplicationContextAware", MyApplicationContextAware.class);
        myApplicationContextAware.printAllBean();
    }
  1. 执行结果
    在这里插入图片描述
    这个例子就是让bean实现一个ApplicationContextAware接口,通过这个接口的回调方法setApplicationContext(),来获取到一个ApplicationContext对象,进而使用该对象的方法。此处是使用ApplicationContext对象的getBeanDefinitionNames()方法。

三、spring有哪些Aware

名称 用途 所属容器 回调点
BeanNameAware 获取bean名称 BeanFactory Bean后处理器的BeforeInitialization方法之前
BeanClassLoaderAware 获取bean的类加载器 BeanFactory Bean后处理器的BeforeInitialization方法之前
BeanFactoryAware 获取bean工厂(建议用下面的ApplicationContextAware) BeanFactory Bean后处理器的BeforeInitialization方法之前
EnvironmentAware 获取环境相关信息,如属性、配置信息等 ApplicationContext Bean后处理器的BeforeInitialization方法中
EmbeddedValueResolverAware 获取值解析器 ApplicationContext Bean后处理器的BeforeInitialization方法中
ResourceLoaderAware 获取资源加载器 ApplicationContext Bean后处理器的BeforeInitialization方法中
ApplicationEventPublisherAware 获取事件广播器,发布事件使用 ApplicationContext Bean后处理器的BeforeInitialization方法中
MessageSourceAware 获取消息资源 ApplicationContext Bean后处理器的BeforeInitialization方法中
ApplicationContextAware 获取ApplicationContext ApplicationContext Bean后处理器的BeforeInitialization方法中

前三个Aware接口:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware是在使用BeanFactory方式初始化容器时调用的,所有bean都可以使用。如下:

Resource classPathResource = new ClassPathResource("applicationContext.xml");
BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);

后六个Aware接口是使用ApplicationContext的方式初始化容器时,才会起作用。如下:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

不过我们现在使用的容器一般都是ApplicationContext,在此了解一下即可,无需太关注。

四、看下原理

通过上面的讲解,我们大概了解到Aware的功能:bean实现个某某Aware接口,然后这个bean就可以通过实现接口的方法,来接收接口方法传递过来的资源。那么接口方法是何时被调用的呢?我们来看下源码~

1. bean的初始化过程

	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		// 1. 调用Aware方法(重点关注)
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		// 2. 调用bean后处理器的BeforeInitialization方法(重点关注)
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 3. 调用bean的初始化方法
			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()) {
			// 4. 调用bean后处理器的AfterInitialization方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

该代码片段来自于AbstractAutowireCapableBeanFactory类的initializeBean方法。其中Aware接口方法的回调过程,分别存在于1和2两个步骤中。我们分别看下

2. BeanFactory的3个Aware接口
以下是bean初始化过程的第一个步骤,也就是beanFactory加载bean时硬编码的3个Aware接口的回调,代码如下:

	private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				// 1. BeanNameAware接口方法的回调
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					// 2. BeanClassLoaderAware接口方法的回调
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				// 3. BeanFactoryAware接口方法的回调
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

3. ApplicationContext的6个Aware接口

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

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

从这段代码中,似乎看不出来怎么就调用Aware接口的回调方法了,仅看到了调用容器中所有beanPostProcessor的postProcessBeforeInitialization()方法。这里就用到上面表格中标识的所属容器,为什么后面6个Aware接口属于ApplicationContext这个容器了?我们来看下初始化ApplicationContext时的一段代码:

	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// 主要看这一行!
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// 省略其他无关代码...
	}

该代码来自于AbstractApplicationContext类的prepareBeanFactory方法,使用ApplicationContext初始化容器时会执行此步骤,代码如下:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

该方法中创建了一个ApplicationContextAwareProcessor实例,并注册到beanFactory的BeanPostProcessors中。那么后面调用容器中所有beanPostProcessor的postProcessBeforeInitialization()方法时,也会调用ApplicationContextAwareProcessor的postProcessBeforeInitialization()方法,我们看下这个方法

	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>) () -> {
				// 调用Aware接口方法
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			// 调用Aware接口方法
			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(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);
			}
		}
	}

五、小结

两种容器中的Aware接口,调用逻辑如下图所示:
在这里插入图片描述
其中粉色代表ApplicationFactory方式初始化的容器,回调的Aware接口。
蓝色表示BeanFactory方式初始化容器时,回调的Aware接口。
ApplicationContext方式初始化的容器,包括所有BeanFactory方式初始化容器的所有功能。

  • BeanFactory初始化容器方式
  1. 在加载bean的步骤中,创建bean之后,调用bean后处理器之前,回调表格中的3个Aware接口
  • ApplicationContext初始化容器方式
  1. 调用BeanFactory方式的3个Aware接口
  2. 在加载bean之前创建一个ApplicationContextAwareProcessor,注册在AbstractBeanFactory的beanPostProcessors属性中。然后在加载bean的步骤中,调用bean后处理器的postProcessBeforeInitialization方法过程中,回调表格中的6个Aware接口。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐