Spring容器刷新prepareRefresh第一步是什么

其他教程   发布日期:2023年09月16日   浏览次数:455

本篇内容介绍了“Spring容器刷新prepareRefresh第一步是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

下面是这部分的涉及到的源码中的关键部分:

  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext {
  3. private long startupDate;
  4. /** Flag that indicates whether this context is currently active. */
  5. private final AtomicBoolean active = new AtomicBoolean();
  6. /** Flag that indicates whether this context has been closed already. */
  7. private final AtomicBoolean closed = new AtomicBoolean();
  8. /** Environment used by this context. */
  9. @Nullable
  10. private ConfigurableEnvironment environment;
  11. protected void prepareRefresh() {
  12. // Switch to active.
  13. this.startupDate = System.currentTimeMillis();
  14. // 1. 初始化状态位
  15. this.closed.set(false);
  16. this.active.set(true);
  17. if (logger.isDebugEnabled()) {
  18. if (logger.isTraceEnabled()) {
  19. logger.trace("Refreshing " + this);
  20. } else {
  21. logger.debug("Refreshing " + getDisplayName());
  22. }
  23. }
  24. // 2. 留给子类的扩展方法
  25. // Initialize any placeholder property sources in the context environment.
  26. initPropertySources();
  27. // 3. 验证必须的配置项是否存在
  28. // Validate that all properties marked as required are resolvable:
  29. // see ConfigurablePropertyResolver#setRequiredProperties
  30. getEnvironment().validateRequiredProperties();
  31. // 4. 处理早期事件
  32. // Store pre-refresh ApplicationListeners...
  33. if (this.earlyApplicationListeners == null) {
  34. this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
  35. } else {
  36. // Reset local application listeners to pre-refresh state.
  37. this.applicationListeners.clear();
  38. this.applicationListeners.addAll(this.earlyApplicationListeners);
  39. }
  40. // Allow for the collection of early ApplicationEvents,
  41. // to be published once the multicaster is available...
  42. this.earlyApplicationEvents = new LinkedHashSet<>();
  43. }
  44. }

1.初始化状态位

一上来就修改两个成员变量,active 改为 true, closed 改为 false

  • 成员变量 activetrue 表示当前 context 处于激活状态

  • 成员变量 closedtrue 表示当前 context 已经被关闭

这里修改了状态,后续有两个地方使用。

第一个地方是容器关闭的时候(避免重复关闭)

  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext {
  3. protected void doClose() {
  4. // 当前是激活状态 && 还没有被关闭
  5. // Check whether an actual close attempt is necessary...
  6. if (this.active.get() && this.closed.compareAndSet(false, true)) {
  7. // 这里省略 N 行代码
  8. // 这里省略 N 行代码
  9. // Switch to inactive.
  10. this.active.set(false);
  11. }
  12. }
  13. }

第二个地方是和

  1. BeanFactory
交互的时候作断言用的
  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext {
  3. protected void assertBeanFactoryActive() {
  4. if (!this.active.get()) {
  5. if (this.closed.get()) {
  6. throw new IllegalStateException(getDisplayName() + " has been closed already");
  7. } else {
  8. throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");
  9. }
  10. }
  11. }
  12. }

几乎所有和

  1. BeanFactory
交互的方法都需要调用
  1. assertBeanFactoryActive
方法来检测容器的状态。
  1. AbstractApplicationContext
中有二三十个地方使用了该方法。

比如最常见的各种重载的

  1. AbstractApplicationContext.getBean(java.lang.String)
方法都会在将方法调用委托给
  1. getBeanFactory().getBean(name, args);
之前调用
  1. assertBeanFactoryActive()
来检测容器状态;毕竟在一个已经关闭了的容器上
  1. getBean()
是不正常的吧。

2.initPropertySources

这个方法主要是留给子类用来将

  1. StubPropertySource
(占位符) 替换为真实的
  1. PropertySource

比如在 servlet 环境下,会将

  1. ServletContextPropertySource
  1. ServletConfigPropertySource
加入(替换 Stub)到
  1. Environment
中。
  1. public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
  2. implements ConfigurableWebApplicationContext, ThemeSource {
  3. @Override
  4. protected void initPropertySources() {
  5. ConfigurableEnvironment env = getEnvironment();
  6. if (env instanceof ConfigurableWebEnvironment) {
  7. // 这里实际上是调用了 WebApplicationContextUtils#initServletPropertySources
  8. ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
  9. }
  10. }
  11. }
  12. public abstract class WebApplicationContextUtils {
  13. public static void initServletPropertySources(MutablePropertySources sources,
  14. @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
  15. Assert.notNull(sources, "'propertySources' must not be null");
  16. String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
  17. // servletContextInitParams
  18. if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
  19. sources.replace(name, new ServletContextPropertySource(name, servletContext));
  20. }
  21. name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
  22. // servletConfigInitParams
  23. if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
  24. sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
  25. }
  26. }
  27. }

当然,你可以在这里直接 修改/替换

  1. Environment
中的任何
  1. PropertySource

也就是说,可以在这里做类似于 spring-boot 中提供的

  1. EnvironmentPostProcessor
能做的事情。

如果是 spring-boot 项目的话,还是推荐直接使用

  1. EnvironmentPostProcessor
。 而不是像下面这样再搞一个
  1. ApplicationContext
的实现类。
  1. public class PrepareRefreshTest {
  2. /**
  3. * 重写 initPropertySources(),给 Environment 中新增两个自定义的配置项 "osName" 和 "a.b.c.d"
  4. */
  5. @Test
  6. void initPropertySourcesTest() {
  7. final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PrepareRefreshTest.class) {
  8. @Override
  9. protected void initPropertySources() {
  10. super.initPropertySources();
  11. final ConfigurableEnvironment environment = getEnvironment();
  12. final Map<String, Object> config = new HashMap<>();
  13. config.put("osName", System.getProperty("os.name", "UNKNOWN"));
  14. config.put("a.b.c.d", "haha");
  15. environment.getPropertySources().addFirst(new MapPropertySource("demo-property-source", config));
  16. }
  17. };
  18. final Environment environment = applicationContext.getEnvironment();
  19. Assertions.assertEquals(System.getProperty("os.name"), environment.getProperty("osName"));
  20. Assertions.assertEquals("haha", environment.getProperty("a.b.c.d"));
  21. }
  22. }

3.validateRequiredProperties

这里主要是验证

  1. ConfigurablePropertyResolver.setRequiredProperties(String... requiredProperties)
方法中指定的那些 必须出现的配置项 是不是都已经在 Environment 中了。

所谓的验证,逻辑也很简单:所有指定的配置项名称都遍历一遍,如果发现 Environment 中获取不到对应的配置项就直接抛出

  1. MissingRequiredPropertiesException
  1. public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
  2. @Override
  3. public void validateRequiredProperties() {
  4. MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
  5. for (String key : this.requiredProperties) {
  6. if (this.getProperty(key) == null) {
  7. ex.addMissingRequiredProperty(key);
  8. }
  9. }
  10. if (!ex.getMissingRequiredProperties().isEmpty()) {
  11. throw ex;
  12. }
  13. }
  14. }

下面这段代码是验证

  1. validateRequiredProperties()
方法的(同样的功能,可以使用 spring-boot 提供的
  1. EnvironmentPostProcessor
来完成)。
  1. public class PrepareRefreshTest {
  2. /**
  3. * 验证 getEnvironment().validateRequiredProperties(); 的功能
  4. * <p>
  5. * 抛出 MissingRequiredPropertiesException 异常(Environment 中缺少必须出现的配置项"jdbc.url")
  6. */
  7. @Test
  8. void validateRequiredPropertiesTest() {
  9. Assertions.assertThrows(MissingRequiredPropertiesException.class, () -> {
  10. final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PrepareRefreshTest.class) {
  11. @Override
  12. protected void initPropertySources() {
  13. super.initPropertySources();
  14. // 这里指定 Environment 中必须要有一个名为 "jdbc.url" 的配置项
  15. // 如果 Environment 中没有名为 "jdbc.url" 的配置项, 就会在 validateRequiredProperties() 方法中抛出 MissingRequiredPropertiesException
  16. getEnvironment().setRequiredProperties("jdbc.url");
  17. }
  18. };
  19. }
  20. );
  21. }
  22. }

4.处理早期事件

什么叫做早期(early)事件?

spring 中的事件最终是委托给

  1. ApplicationEventMulticaster
(多播器) 发布的。 但现在是在 prepareRefresh 阶段,多播器 实例还没有初始化呢。 这时候要是有事件的话,就只能先将这种 "早期"事件保存下来,等到多播器初始化好之后再回过头来发布这种"早期"事件。

处理早期事件 这一步所作的事情就是 初始化 用来 临时 保存 "早期" 事件的两个集合:

    1. earlyApplicationEvents
    : 早期事件
    1. earlyApplicationListeners
    : 早期事件监听器

等到后续的

  1. initApplicationEventMulticaster()
之后会回过头来遍历
  1. earlyApplicationEvents
发布事件。

详细内容会在 步骤8-initApplicationEventMulticaster()步骤10-registerListeners() 相关的文章中介绍。这里只介绍和

  1. prepareRefresh
相关的内容。
  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext {
  3. private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
  4. /** Local listeners registered before refresh. */
  5. @Nullable
  6. private Set<ApplicationListener<?>> earlyApplicationListeners;
  7. /** ApplicationEvents published before the multicaster setup. */
  8. @Nullable
  9. private Set<ApplicationEvent> earlyApplicationEvents;
  10. protected void prepareRefresh() {
  11. // Switch to active.
  12. // 1. 初始化状态位
  13. // ...
  14. // 2. 留给子类的扩展方法
  15. // ...
  16. // 3. 验证必须的配置项是否存在
  17. // ...
  18. // 4. 处理早期事件
  19. // Store pre-refresh ApplicationListeners...
  20. if (this.earlyApplicationListeners == null) {
  21. this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
  22. } else {
  23. // Reset local application listeners to pre-refresh state.
  24. this.applicationListeners.clear();
  25. this.applicationListeners.addAll(this.earlyApplicationListeners);
  26. }
  27. // Allow for the collection of early ApplicationEvents,
  28. // to be published once the multicaster is available...
  29. this.earlyApplicationEvents = new LinkedHashSet<>();
  30. }
  31. }

以上就是Spring容器刷新prepareRefresh第一步是什么的详细内容,更多关于Spring容器刷新prepareRefresh第一步是什么的资料请关注九品源码其它相关文章!