本篇内容介绍了“Spring容器刷新prepareRefresh第一步是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
下面是这部分的涉及到的源码中的关键部分:
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { private long startupDate; /** Flag that indicates whether this context is currently active. */ private final AtomicBoolean active = new AtomicBoolean(); /** Flag that indicates whether this context has been closed already. */ private final AtomicBoolean closed = new AtomicBoolean(); /** Environment used by this context. */ @Nullable private ConfigurableEnvironment environment; protected void prepareRefresh() { // Switch to active. this.startupDate = System.currentTimeMillis(); // 1. 初始化状态位 this.closed.set(false); this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } // 2. 留给子类的扩展方法 // Initialize any placeholder property sources in the context environment. initPropertySources(); // 3. 验证必须的配置项是否存在 // Validate that all properties marked as required are resolvable: // see ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); // 4. 处理早期事件 // 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<>(); } }
1.初始化状态位
一上来就修改两个成员变量,active 改为 true, closed 改为 false:
成员变量 active 为 true 表示当前 context 处于激活状态
成员变量 closed 为 true 表示当前 context 已经被关闭
这里修改了状态,后续有两个地方使用。
第一个地方是容器关闭的时候(避免重复关闭)
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { protected void doClose() { // 当前是激活状态 && 还没有被关闭 // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { // 这里省略 N 行代码 // 这里省略 N 行代码 // Switch to inactive. this.active.set(false); } } }
第二个地方是和
BeanFactory交互的时候作断言用的
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { protected void assertBeanFactoryActive() { if (!this.active.get()) { if (this.closed.get()) { throw new IllegalStateException(getDisplayName() + " has been closed already"); } else { throw new IllegalStateException(getDisplayName() + " has not been refreshed yet"); } } } }
几乎所有和
BeanFactory交互的方法都需要调用
assertBeanFactoryActive方法来检测容器的状态。
AbstractApplicationContext中有二三十个地方使用了该方法。
比如最常见的各种重载的
AbstractApplicationContext.getBean(java.lang.String)方法都会在将方法调用委托给
getBeanFactory().getBean(name, args);之前调用
assertBeanFactoryActive()来检测容器状态;毕竟在一个已经关闭了的容器上
getBean()是不正常的吧。
2.initPropertySources
这个方法主要是留给子类用来将
StubPropertySource(占位符) 替换为真实的
PropertySource。
比如在 servlet 环境下,会将
ServletContextPropertySource和
ServletConfigPropertySource加入(替换 Stub)到
Environment中。
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext implements ConfigurableWebApplicationContext, ThemeSource { @Override protected void initPropertySources() { ConfigurableEnvironment env = getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { // 这里实际上是调用了 WebApplicationContextUtils#initServletPropertySources ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig); } } } public abstract class WebApplicationContextUtils { public static void initServletPropertySources(MutablePropertySources sources, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) { Assert.notNull(sources, "'propertySources' must not be null"); String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME; // servletContextInitParams if (servletContext != null && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletContextPropertySource(name, servletContext)); } name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME; // servletConfigInitParams if (servletConfig != null && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletConfigPropertySource(name, servletConfig)); } } }
当然,你可以在这里直接 修改/替换
Environment中的任何
PropertySource。
也就是说,可以在这里做类似于 spring-boot 中提供的
EnvironmentPostProcessor能做的事情。
如果是 spring-boot 项目的话,还是推荐直接使用
EnvironmentPostProcessor。 而不是像下面这样再搞一个
ApplicationContext的实现类。
public class PrepareRefreshTest { /** * 重写 initPropertySources(),给 Environment 中新增两个自定义的配置项 "osName" 和 "a.b.c.d" */ @Test void initPropertySourcesTest() { final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PrepareRefreshTest.class) { @Override protected void initPropertySources() { super.initPropertySources(); final ConfigurableEnvironment environment = getEnvironment(); final Map<String, Object> config = new HashMap<>(); config.put("osName", System.getProperty("os.name", "UNKNOWN")); config.put("a.b.c.d", "haha"); environment.getPropertySources().addFirst(new MapPropertySource("demo-property-source", config)); } }; final Environment environment = applicationContext.getEnvironment(); Assertions.assertEquals(System.getProperty("os.name"), environment.getProperty("osName")); Assertions.assertEquals("haha", environment.getProperty("a.b.c.d")); } }
3.validateRequiredProperties
这里主要是验证
ConfigurablePropertyResolver.setRequiredProperties(String... requiredProperties)方法中指定的那些 必须出现的配置项 是不是都已经在 Environment 中了。
所谓的验证,逻辑也很简单:所有指定的配置项名称都遍历一遍,如果发现 Environment 中获取不到对应的配置项就直接抛出
MissingRequiredPropertiesException
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver { @Override public void validateRequiredProperties() { MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException(); for (String key : this.requiredProperties) { if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); } } if (!ex.getMissingRequiredProperties().isEmpty()) { throw ex; } } }
下面这段代码是验证
validateRequiredProperties()方法的(同样的功能,可以使用 spring-boot 提供的
EnvironmentPostProcessor来完成)。
public class PrepareRefreshTest { /** * 验证 getEnvironment().validateRequiredProperties(); 的功能 * <p> * 抛出 MissingRequiredPropertiesException 异常(Environment 中缺少必须出现的配置项"jdbc.url") */ @Test void validateRequiredPropertiesTest() { Assertions.assertThrows(MissingRequiredPropertiesException.class, () -> { final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PrepareRefreshTest.class) { @Override protected void initPropertySources() { super.initPropertySources(); // 这里指定 Environment 中必须要有一个名为 "jdbc.url" 的配置项 // 如果 Environment 中没有名为 "jdbc.url" 的配置项, 就会在 validateRequiredProperties() 方法中抛出 MissingRequiredPropertiesException getEnvironment().setRequiredProperties("jdbc.url"); } }; } ); } }
4.处理早期事件
什么叫做早期(early)事件?
spring 中的事件最终是委托给
ApplicationEventMulticaster(多播器) 发布的。 但现在是在 prepareRefresh 阶段,多播器 实例还没有初始化呢。 这时候要是有事件的话,就只能先将这种 "早期"事件保存下来,等到多播器初始化好之后再回过头来发布这种"早期"事件。
处理早期事件 这一步所作的事情就是 初始化 用来 临时 保存 "早期" 事件的两个集合:
earlyApplicationEvents: 早期事件
earlyApplicationListeners: 早期事件监听器
等到后续的
initApplicationEventMulticaster()之后会回过头来遍历
earlyApplicationEvents发布事件。
详细内容会在 步骤8-initApplicationEventMulticaster() 和 步骤10-registerListeners() 相关的文章中介绍。这里只介绍和
prepareRefresh相关的内容。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>(); /** Local listeners registered before refresh. */ @Nullable private Set<ApplicationListener<?>> earlyApplicationListeners; /** ApplicationEvents published before the multicaster setup. */ @Nullable private Set<ApplicationEvent> earlyApplicationEvents; protected void prepareRefresh() { // Switch to active. // 1. 初始化状态位 // ... // 2. 留给子类的扩展方法 // ... // 3. 验证必须的配置项是否存在 // ... // 4. 处理早期事件 // 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<>(); } }