Spring扩展点探索之prepareRefresh

只需低头努力,剩下的交给时光,时间会公平地帮你处理这一切~

前言

Spring作为优秀的框架,被很多开发者使用,只要是做Java开发的,就一定会知道Spring框架。

Spring作为Java开发框架的标配,它所表现出来的优秀特性大大降低了企业开发过程中的复杂性

在前面我通过几篇源码的文章,分析了Spring是如何把Bean注册到容器中的、Spring是如何获取容器中的Bean的、Spring是如何实现AOP的

有兴趣可以去了解一下

那Spring这么优秀的框架,在扩展性方面自然也是极好的,我们知道Spring整个框架是个很庞大的集体,里面包括很多东西

Spring容器在启动过程中,有很多步骤,如初始化、加载Bean、国际化、事件广播等,在很多步骤中Spring都预留了接口给你来进行扩展,让你在使用Spring框架的过程中,可以做很多个性化的操作

从这个层面来讲,我们在使用Spring框架的时候,不光是把Bean对象交给Spring来管理,你也可以介入进去,可以干涉注册到Spring容器中的Bean

下面我们就一起来探索一下,Spring有哪些扩展点?

refresh()方法

了解过Spring源码的朋友肯定知道refresh()方法,如果连这个方法都不知道,那你百分之百没有看过Spring源码

refresh()方法可以说是Spring框架的核心,它串起了整个Spring容器的启动过程,你把这个方法里面的逻辑搞清楚了,那你对Spring的整体脉络就会有一个清晰的认识

下面我们就来看一下refresh()方法

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			//容器启动前的准备工作
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			//告诉子类刷新内部Bean工厂,解析Bean并注册到容器中(此时还没有初始化)
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			//准备BeanFactory,设置beanFactory类加载器,添加多个beanPostProcesser
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				//允许子类上下文中对beanFactory做后期处理
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				//调用BeanFactoryPostProcessor各个实现类的方法
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册 BeanPostProcessor 的实现类
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 初始化ApplicationContext的MessageSource
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化事件广播
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//初始化子类特殊bean
				onRefresh();

				// Check for listener beans and register them.
				//注册事件监听器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化所有singleton bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 广播事件,ApplicationContext初始化完成
				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();
			}
		}
	}

方法主体总共12个方法

  • prepareRefresh() 容器启动前的准备工作
  • obtainFreshBeanFactory() 告诉子类刷新内部Bean工厂,解析Bean并注册到容器中(此时还没有初始化)
  • prepareBeanFactory(beanFactory) 准备BeanFactory,设置beanFactory类加载器,添加多个beanPostProcesser
  • postProcessBeanFactory(beanFactory) 允许子类上下文中对beanFactory做后期处理
  • invokeBeanFactoryPostProcessors(beanFactory) 调用BeanFactoryPostProcessor各个实现类的方法
  • registerBeanPostProcessors(beanFactory) 注册 BeanPostProcessor 的实现类
  • initMessageSource() 初始化ApplicationContext的MessageSource
  • initApplicationEventMulticaster() 初始化事件广播
  • onRefresh() 初始化子类特殊bean
  • registerListeners() 注册事件监听器
  • finishBeanFactoryInitialization(beanFactory) 初始化所有singleton bean
  • finishRefresh() 广播事件,ApplicationContext初始化完成

大部分方法都是为了完成注册和初始化,整个refresh()方法执行完成之后,基本上容器就启动完成了

prepareRefresh()方法

我们今天先来看一下prepareRefresh()方法,它是容器启动之前准备工作

protected void prepareRefresh() {
		// 记录开始启动的时间
		this.startupDate = System.currentTimeMillis();
		//设置关闭状态为false
		this.closed.set(false);
		//设置激活状态为true
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// Initialize any placeholder property sources in the context environment.
		//初始化上下文环境中的占位符
		initPropertySources();

		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		//校验所有必须的属性
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh ApplicationListeners...
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

这里就出现了第一个扩展点,我们可以看到initPropertySources()是空的

protected void initPropertySources() {
		// For subclasses: do nothing by default.
	}

在Spring里面有很多这种空的方法,很多人在看源码的时候可能不太理解,为什么会调用一个空的方法,其实这是Spring为你预留的口子,就是让你自由发挥的地方

既然initPropertySources()是空的,那我们就来自己实现一下

public class MyAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext {

    public MyAnnotationConfigApplicationContext(String... basePackages){
        super(basePackages);
    }
    @Override
    protected void initPropertySources() {
        super.initPropertySources();
        getEnvironment().getSystemProperties().put("action","run");
        getEnvironment().setRequiredProperties("KEY");
    }
}

这里我继承了AnnotationConfigApplicationContext,实现了initPropertySources()方法,在里面添加了一个系统属性,并且标记“KEY”为必须的参数

下面我们来测试一下

public class AnnotionBeanTest {
    public static void main(String[] args){
        MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext("org.kxg.springDemo");
    }
}

启动后发现报如下的错误:

Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [KEY]
	at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:146)
	at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:523)
	at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:603)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:518)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:101)
	at org.kxg.springDemo.refresh.prepare.MyAnnotationConfigApplicationContext.<init>(MyAnnotationConfigApplicationContext.java:8)
	at org.kxg.springDemo.AnnotionBeanTest.main(AnnotionBeanTest.java:8)

从异常信息可以看到,这里是缺少必须参数的Exception,这里可以用来验证系统启动的一些必须参数

我们把“KEY”加到系统参数中,再来看一下

public class AnnotionBeanTest {
    public static void main(String[] args){
        System.setProperty("KEY","VAL");
        MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext("org.kxg.springDemo");
    }
}

启动容器,一切正常,至此我们就完成了第一个Spring的扩展点,当然例子中的扩展很简单,实际项目中大家可以根据情况来做自己的扩展,后面会陆续介绍其他的扩展点,请大家持续关注!

如果感觉对你有些帮忙,请收藏好,你的关注和点赞是对我最大的鼓励!
如果想跟我一起学习,坚信技术改变世界,请关注【Java天堂】公众号,我会定期分享自己的学习成果,第一时间推送给您

在这里插入图片描述

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值