Spring钩子之Aware接口

Spring的钩子

使用过Spring的人应该都知道他是一套很强大的应用开发框架,他的IOC、AOP等特性也是我们开发中最常用的一些特性,而Spring强大的不仅仅与在他的应用上,而且也在于他能与其他的框架集成,也可以通过二次开发形成更适合企业业务的框架。对于Spring的二次开发其实就不得不说到Spring的钩子方法了,这里我们把Spring提供的回调方法称为钩子方法,也就是我们常用的扩展点。

Aware接口

对于这个接口官方给出的解释是

A marker superinterface indicating that a bean is eligible to be notified by the Spring container of a particular framework object through a callback-style method. The actual method signature is determined by individual subinterfaces but should typically consist of just one void-returning method that accepts a single argument.

这里的意思其实就是Aware接口就是一个标记,继承Aware的接口就表示Spring容器可以通过回调方法将bean通知给特定的框架对象,并且他的返回值一般为void。

其实更通俗的解释可以是:我只要实现了Aware接口,Spring就需要为我把我需要的bean赋值进来给我

这也是为什么一些Aware的实现类可以用作工具类,可以使得非bean对象也能够使用spring中的bean。

而Aware接口的常见实现有BeanFactoryAware, BeanNameAware, ApplicationContextAware, EnvironmentAware,BeanClassLoaderAware等

ApplicationContextAware

这里的BeanFactoryAware和ApplicationContextAware实际上都是设置上下文的接口,我们下面以ApplicationContextAware来演示一下

@Component
public class HunterBeanFactoryAware implements ApplicationContextAware {

	private static ApplicationContext applicationContext;

	@SuppressWarnings("static-access")
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public static  <T> T getBean(Class<T> clazz){
		if (clazz != null){
			return applicationContext.getBean(clazz);
		}
		return null;
	}

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

		HunterBeanFactoryAware bean = getBean(HunterBeanFactoryAware.class);
		System.out.println(bean);
	}
}

在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210522213514555.png
以上这个实现其实很常见,在一些工具类等经常可以看到,它使得在非Spring管理的对象能够用上bean,我们可以看到这接口可以手动setApplicationContext设置上Spring的下文,同时,当没有手动设置上下文时,是默认使用当前容器加载的上下文的。

BeanNameAware

顾名思义,这个回调的方法是和Bean的名字相关的,具体作用是能够获取到Bean的名字。
使用如下:
Spring钩子之Aware接口_第1张图片
运行后可从容器获取名字:
在这里插入图片描述

EnvironmentAware

顾名思义,这个EnvironmentAware很可能是与环境变量相关的一个接口,我们可以看一下他的接口以及需要实现的方法

public interface EnvironmentAware extends Aware {

	/**
	 * Set the {@code Environment} that this component runs in.
	 */
	void setEnvironment(Environment environment);

}

这个其实很明显,和其他的方法都差不多是setxxx方法,实际上继承该方法后能得到Spring的注入。

@Component
public class HunterBeanFactoryAware implements EnvironmentAware {

	private static Environment environment;

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		Environment environment = getEnvironment();
		System.out.println(environment);
	}
	
	@SuppressWarnings("static-access")
	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	public static Environment getEnvironment(){
		return environment;
	}
}

输出显示:
在这里插入图片描述
因此,很明显,我们可以通过Aware的相关接口来获取到我们需要的Bean。

这个是不是很神奇?我们可以思考一下,为什么我们继承了这个方法以后,Spring就会自动帮我们注入进来我们需要的Bean呢,它是什么时候注入进来的?
下面我们可以分析一下源码:

首先我们从入口开始

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

在这里声明了一个上下文并且规定了我们的包扫描范围

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		//调用默认无参构造器,主要初始化AnnotatedBeanDefinitionReader
		//以及路径扫描器ClassPathBeanDefinitionScanner
		//这里由于他有父类,故而回显调用父类的构造方法,然后才会调用自己的构造方法
		//在这里构造方法中初始一个读取器和扫描器
		this();
		/**
		 * 把传入的Class进行注册,Class既可以有@注解,也可以没有@Configuration注解
		 * 如何把注册委托给org.springframework.context.annotation.AnnotationedBeanDefinitionReader.register方法进行注册
		 * 包装传入Class生成BeanDefinition,注入到BeanDefinitionRegistry
		 */
		register(componentClasses);
		refresh();
	}

继续往下走,我们本章先忽略spring初始化的其他内容,进入到refresh()方法

@Override
	public void refresh() throws BeansException, IllegalStateException {
		//给容器refresh加锁,避免容器处在refresh阶段时,容器进行了初始化或销毁的操作
		synchronized (this.startupShutdownMonitor) {
			// 调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识,具体方法.
			prepareRefresh();

			// 告诉子类启动refreshBeanFactory方法,Bean定义资源文件的载入从子类的
			//refreshBeanFactory()方法启动,里面有抽象方法
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 对BeanFactory进行各种功能填充.
			prepareBeanFactory(beanFactory);

			try {
				//允许容器的子类去注册PostProcessor,TODO 钩子方法
				postProcessBeanFactory(beanFactory);

				//激活各种BeanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				//注册拦截Bean创建的Bean处理器,这里只是注册,真正的注册是在getBean时候.
				registerBeanPostProcessors(beanFactory);

				// 为上下文初始化Message源,即不同语言的消息体,国际化处理.
				//找到"messageSource"的Bean提供给ApplicationContext使用,
				//使得ApplicationContext具有国际化能力
				initMessageSource();

				// 初始化应用消息广播器,并放入"applicationEventMulticaster" bean中.
				//初始化APplicationEventMulticaster该类为事件发布者
				//可以存储所有事件监听者信息,并根据不同的事件,通知不同的时间监听者
				initApplicationEventMulticaster();

				//预留给AbstractApplicationContext的子类用于初始化其它特殊的bean,
				//该方法需要在所有单例bean初始化之前调用
				//比如Web容器就会去初始化一些和主题展示相关的Bean(ThemeSource)
				onRefresh();

				// 在所有注册的bean中查找Listener bean,注册到消息广播器中.
				registerListeners();

				// 设置自定义的类型转化器ConversionService
				//设置自定义AOP相关的类LoadTimeWeaverAware
				//清除临时的ClassLoader
				//实例化所有的类(懒加载的类除外)
				finishBeanFactoryInitialization(beanFactory);

				// 完成刷新过程,通知声明周期处理器lifecycleProcessor刷新过程,
				// 同时发出ContextRefreshEvent通知别人
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

refresh()方法其实Spring中最核心的方法之一,也是我们重点分析的一个方法,Spring容器创建之后,会调用它的refresh方法刷新Spring应用的上下文,并设置一些生命周期回调的管理类,也就是在Bean初始化能够“接入的方法”,这里需要注意的是prepareBeanFactory()方法,其主要做的是对Bean工厂初始化的准备。
Spring钩子之Aware接口_第2张图片
此处添加的BeanPostProcessor相关的接口其实是Spring最重要的生命周期回调接口,我们可以来看一下他的接口方法

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

是两个默认实现的空方法,他们分别在Bean初始化之前和初始化之后进行调用,入参分别是bean和beanName,回调方法可以对相信的信息进行判断和处理。
我们进一步看一下他的实现类
Spring钩子之Aware接口_第3张图片
我们这里需要注意的是ApplicationContextAwareProcessor这个类,因为他正是我们前面在prepareBeanFactory方法中new出来的对象(前面第二张图中的代码)。

我们来看一下这个ApplicationContextAwareProcessor的在对bean的初始化前所回调的方法做了写什么

	@Override
	@Nullable
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

很明显可以看出来,这个方法主要是用来对特定的几个Aware实现类进行处理的,我们进入到invokeAwareInterfaces方法继续看一下

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 ApplicationContextAware) {
 			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}

走到这里其实我们已经大概明白了,因为我们对所有被spring管理的bean都会循环处理,所以这里可以对所有被spring管理的bean一个个的做判断,只要有一个属于规定的其中的Aware实现类,我们就对他进行初始化,并且把我们现在容器所存在的bean用set的形式放进去。这就是为什么只要我们实现了这个接口,并且该接口的实现类是被Spring所管理的,就会为我们自动赋上相应的bean。

你可能感兴趣的:(spring,java)