ApplicationContext
ApplicationContext是Spring的高級容器。
與BeanFactory類似,它可以加載bean定義並根據請求分發bean;此外,它還添加了很多特定的功能,比如:從屬性文件解析文本消息、將應用程序事件發布到感興趣的事件偵聽器。
類圖
可以使用如下代碼創建ApplicationContext:
ApplicationContext context = new FileSystemXmlApplicationContext("Beans.xml");
Spring提供了適合不同需求的ApplicationContext,每個應用程序上下文可以擁有多個配置文件、配置類或兩者的混合,相關類圖見下圖:
解讀:
- ConfigurableApplicationContext
ConfigurableApplicationContext 擴展自 ApplicationContext,新增了兩個主要方法 reflesh() 和close(),使得其具有啟動、刷新和關閉應用上下文的能力。
在應用上下文關閉的情況下調用 reflesh() 即可啟動應用上下文;在應用上下文啟動的情況下調用 reflesh() 即可清除緩存並且重新裝載配置信息;調用 close() 方法則可以關閉應用上下文。
ApplicationContext實現類
普通應用
FileSystemXMLApplicationContext
通過FileSystemXMLApplicationContext[地址]可以從文件系統或url加載基於xml的Spring配置文件。
應用代碼如下:
String path = "D:/source/Test/src/main/resources/applicationcontext/bean-config.xml";
ApplicationContext context = new FileSystemXmlApplicationContext(path); AccountService accountService = context.getBean("accountService", AccountService.class);
ClassPathXmlApplicationContext
通過ClassPathXmlApplicationContext[地址]可以從類路徑加載XML配置文件。
應用代碼如下:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/bean-config.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext是在Spring 3.0中引入的(與@Configuration、@Component和JSR-330元數據注釋等一起工作)。
應用代碼如下:
ApplicationContext context = new AnnotationConfigApplicationContext(AccountConfig.class); AccountService accountService = context.getBean(AccountService.class);
Web應用
進一步發掘,可以得到如下類圖:
解讀:
觀察其與本文前面展示的類圖之間的區別,可以發現Web應用以WebApplicationContext為主線。
AnnotationConfigWebApplicationContext
AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的一個基於web的變體。
Note:
從Spring 3.0開始,可以通過編程方式配置ApplicationContext,開發人員需要做的是實現WebApplicationInitializer接口[地址],代碼如下:
public class MyWebApplicationInitializer implements WebApplicationInitializer { public void onStartup(ServletContext container) throws ServletException { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AccountConfig.class); context.setServletContext(container); // servlet configuration } }
XmlWebApplicationContext
如果在web應用程序中使用基於XML的配置可以使用XmlWebApplicationContext;類似AnnotationConfigWebApplicationContext,可以通過實現WebApplicationInitializer接口來配置應用程序。
應用代碼如下:
public class MyXmlWebApplicationInitializer implements WebApplicationInitializer { public void onStartup(ServletContext container) throws ServletException { XmlWebApplicationContext context = new XmlWebApplicationContext(); context.setConfigLocation("/WEB-INF/spring/applicationContext.xml"); context.setServletContext(container); // Servlet configuration } }
深度解析
Message Resolution
ApplicationContext接口通過擴展MessageSource接口來支持消息解析和國際化。
Spring提供了兩個MessageSource實現:ResourceBundleMessageSource、StaticMessageSource。
- StaticMessageSource
可以使用StaticMessageSource以編程方式向源添加消息,它支持基本的國際化,更適合測試而不是生產環境。
- ResourceBundleMessageSource
ResourceBundleMessageSource是MessageSource最常見的實現,它依賴於底層JDK的ResouceBundle[地址]實現;它還使用了 JDK 提供的標准消息解析 —— MessageFormat。
案例:
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("config/messages"); return messageSource; }
@Autowired private MessageSource messageSource;
messageSource.getMessage("account.name", null, Locale.ENGLISH);
Note:
Spring提供了ReloadableResourceBundleMessageSource類[地址],它允許從任何Spring資源位置讀取文件,並支持熱加載bundle屬性文件。
Event Handling
ApplicationContext 通過擴展ApplicationEventPublisher讓容器擁有發布應用上下文事件的能力;它支持內置事件,如:ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent;此外,它還支持自定義事件。
ApplicationContext 在 ApplicationEvent 類和 ApplicationListener 接口的幫助下支持事件處理:
- 實現了 ApplicationListener 接口的 Bean 可以接收到容器事件,進而對事件進行處理
- 在 ApplicationContext 的子類 AbstractApplicationContext 中存在一個 ApplicationEventMulticaster 變量,它保存所有監聽器,以便在容器產生上下文事件時通知這些事件監聽器
ApplicationContext層次結構
ApplicationContext的層次結構提供了一種重用bean的方法,可以在child ApplicationContext中訪問在parent ApplicationContext中定義的bean。
Spring提供了指定parent ApplicationContext的方法,相關構造函數如下:
Note:
child ApplicationContext與parent ApplicationContext通過父/子關系相關聯;不是組合關系。
對這一概念的理解,請琢磨下面幾個case:
場景1:
Each Spring MVC webapp has one root application context and one servlet application context for each DispatcherServlet.
The root application context is the parent of each servlet application context.
Beans defined in "contextConfigLocation" (context-param in web.xml) are loaded into root application context.
Beans in <servlet-name>-servlet.xml are loaded into servlet application context.
If an EAR has multiple web apps, an EAR level application context can parent the root context of each webapp in the EAR.
資料
https://docs.spring.io/spring-framework/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-servlet
場景2:
For example, Spring Security is independent of Spring MVC and requires its configuration beans to go in the webapp context. If you want to use Spring MVC with it, then the config for that has to go into the servlet context, which has the root webapp context as its parent.
場景3:
It's possible to create separate contexts and organize them in a hierarchy in Spring Boot.
Each child context can override configuration inherited from the parent context.
The SpringApplicationBuilder class provides a fluent API to create a parent-child relationship between contexts using parent(), child() and sibling() methods.
資料:
https://www.baeldung.com/spring-boot-context-hierarchy
Note:
BeanFactory 在初始化容器時並未實例化 Bean,直到第一次訪問某個Bean 時才實例化目標 Bean;ApplicationContext 在初始化容器時就實例化所有的單實例 Bean。