您好,登錄后才能下訂單哦!
本篇內容介紹了“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<>(); } }
一上來就修改兩個成員變量,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()
是不正常的吧。
這個方法主要是留給子類用來將 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")); } }
這里主要是驗證 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"); } }; } ); } }
什么叫做早期(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<>(); } }
“Spring容器刷新prepareRefresh第一步是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。