spring中的Aware接口原来是这么回事

一、介绍

  使用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名称BeanFactoryBean后处理器的BeforeInitialization方法之前
BeanClassLoaderAware获取bean的类加载器BeanFactoryBean后处理器的BeforeInitialization方法之前
BeanFactoryAware获取bean工厂(建议用下面的ApplicationContextAware)BeanFactoryBean后处理器的BeforeInitialization方法之前
EnvironmentAware获取环境相关信息,如属性、配置信息等ApplicationContextBean后处理器的BeforeInitialization方法中
EmbeddedValueResolverAware获取值解析器ApplicationContextBean后处理器的BeforeInitialization方法中
ResourceLoaderAware获取资源加载器ApplicationContextBean后处理器的BeforeInitialization方法中
ApplicationEventPublisherAware获取事件广播器,发布事件使用ApplicationContextBean后处理器的BeforeInitialization方法中
MessageSourceAware获取消息资源ApplicationContextBean后处理器的BeforeInitialization方法中
ApplicationContextAware获取ApplicationContextApplicationContextBean后处理器的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接口。
  • 24
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值