上一篇筆記(Spring MVC源碼——Root WebApplicationContext)中記錄了下 Root WebApplicationContext 的初始化代碼.這一篇來看 Servlet WebApplicationContext 的初始化代碼
DispatcherServlet
?是另一個需要在?web.xml
?中配置的類, Servlet WebApplicationContext 就由它來創建和初始化.
HttpServletBean
HttpServletBean
?簡單繼承了?HttpServlet
, 負責將 init-param 中的參數注入到當前?Servlet
?實例的屬性中, 并且為子類提供了增加 requiredProperties 的能力.?HttpServletBean
?并不依賴于 Spring 容器.
來看一下它的?init()
?方法:
public final void init() throws ServletException {// Set bean properties from init parameters.// 從 ServletConfig 中取出初始化參數到 PropertyValues。ServletConfigPropertyValues 的構造器中將會檢查是否缺失了必要屬性PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {// 將 servlet 對象包裝成 BeanWrapper ,從而能夠以 Spring 的方式(反射)來注入參數BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());// 注冊 PropertyEditor,遇到 Resource 類型的屬性時,用 ResourceEditor 解析bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));// 初始化 BeanWrapper,空方法 initBeanWrapper(bw);// 注入屬性,忽略沒有 setter 的屬性bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}// Let subclasses do whatever initialization they like.// 由子類實現初始化邏輯 initServletBean(); }
private static class ServletConfigPropertyValues extends MutablePropertyValues {public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)throws ServletException {// 將 requiredProperties 拷貝到新的 Set missingPropsSet<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?new HashSet<>(requiredProperties) : null);// 將 ServletConfig 中的初始化參數取出,添加到 MutablePropertyValues 中Enumeration<String> paramNames = config.getInitParameterNames();while (paramNames.hasMoreElements()) {String property = paramNames.nextElement();Object value = config.getInitParameter(property);addPropertyValue(new PropertyValue(property, value));if (missingProps != null) {missingProps.remove(property);}}// Fail if we are still missing properties.if (!CollectionUtils.isEmpty(missingProps)) {// 存在必須出現的條件沒出現throw new ServletException("Initialization from ServletConfig for servlet '" + config.getServletName() +"' failed; the following required properties were missing: " +StringUtils.collectionToDelimitedString(missingProps, ", "));}} }
FrameworkServlet
FrameworkServlet
?是一個更具體的 Servlet 基類. 它有以下兩個功能:
- 每個 servket 管理一個?
WebApplicationContext
?實例. - 無論請求是否成功, 根據請求處理發布事件.
FrameworkServlet
?重寫了?HttpServletBean
?的?initServletBean()
?方法, 這個方法會在 所有 servlet 的屬性被注入之后執行, 來看一下代碼:
protected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");if (logger.isInfoEnabled()) {logger.info("Initializing Servlet '" + getServletName() + "'");}long startTime = System.currentTimeMillis();try {// 初始化 webApplicationContextthis.webApplicationContext = initWebApplicationContext();// 在容器被加載后執行,由子類來實現一些必要的初始化 initFrameworkServlet();}catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);throw ex;}// 略去打印日志的部分...
}
initWebApplicationContext()
?方法會初始化并返回一個容器:
protected WebApplicationContext initWebApplicationContext() {// 獲取 Root WebApplicationContextWebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use it// 一個上下文已經被注入進來wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {// 如果是 ConfigurableWebApplicationContext,ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// 沒有激活,設置父容器,配置并且刷新容器if (cwac.getParent() == null) {cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// 嘗試從 ServletContext 中獲取一個容器wac = findWebApplicationContext();}if (wac == null) {// 創建一個新的容器并初始化wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// 沒有觸發過刷新時間synchronized (this.onRefreshMonitor) {// 手動觸發刷新事件 onRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.// 將容器發布到 ServletContext 的屬性上String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac; }
onRefresh()
?方法供子類來重寫,?DispatcherServlet
?重寫了這個方法來初始化 MVC 中的一些組件:
@Override protected void onRefresh(ApplicationContext context) {initStrategies(context); }protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context); }
initWebApplicationContext()
?方法調用的其他方法其實和?ContextLoader
?中的方法比較類似, 這里就不再放上來了, 有興趣的可以訪問我的源碼注釋.
?總結
通過本篇博客以及上一篇博客,相信大家對springmvc的上下文有了明確的認識。總的來說,默認springmvc項目會有兩個上下文(root webapplicationcontext 和 servlet webapplicationcontext)。接下來的博文會帶大家認識一下springboot項目的上下文以及springmvc項目上下文和springboot項目上下文的異同點,敬請期待。
?
轉自:https://www.cnblogs.com/FJH1994/p/10813687.html