Spring框架系列(13) - SpringMVC實現原理之DispatcherServlet的初始化過程

語言: CN / TW / HK

前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第一篇:DispatcherServlet的初始化過程的原始碼解析。@pdai

DispatcherServlet和ApplicationContext有何關係?

DispatcherServlet 作為一個 Servlet,需要根據 Servlet 規範使用 Java 配置或 web.xml 宣告和對映。反過來,DispatcherServlet 使用 Spring 配置來發現請求對映、檢視解析、異常處理等等所需的委託元件。那它和ApplicationContext有和關係呢?如下內容可以參考官網-SpringMVC文件

DispatcherServlet 需要 WebApplicationContext(繼承自 ApplicationContext) 來配置。WebApplicationContext 可以連結到ServletContext 和 Servlet。因為綁定了 ServletContext,這樣應用程式就可以在需要的時候使用 RequestContextUtils 的靜態方法訪問 WebApplicationContext。

大多數應用程式只有一個WebApplicationContext,除此之外也可以一個Root WebApplicationContext 被多個 Servlet例項,然後各自擁有自己的Servlet WebApplicationContext 配置。

Root WebApplicationContext 包含需要共享給多個 Servlet 例項的資料來源和業務服務基礎 Bean。這些 Bean 可以在 Servlet 特定的範圍被繼承或覆蓋。

(PS:官網上的這張圖可以可以幫助你構建DispatcherServlet和ApplicationContext在設計上的認知,這一點對於理解DispatcherServlet的設計和初始化過程非常重要)

DispatcherServlet是如何初始化的?

DispatcherServlet首先是Sevlet,Servlet有自己的生命週期的方法(init,destory等),那麼我們在看DispatcherServlet初始化時首先需要看原始碼中DispatcherServlet的類結構設計。

首先我們看DispatcherServlet的類結構關係,在這個類依賴結構中找到init的方法

很容易找到init()的方法位於HttpServletBean中,然後跑Spring基礎 - SpringMVC請求流程和案例中的程式碼,在init方法中打斷點。

init

init()方法如下, 主要讀取web.xml中servlet引數配置,並將交給子類方法initServletBean()繼續初始化

```java /* * Map config parameters onto bean properties of this servlet, and * invoke subclass initialization. * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. / @Override public final void init() throws ServletException {

// 讀取web.xml中的servlet配置 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 轉換成BeanWrapper,為了方便使用Spring的屬性注入功能 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); // 注入Resource型別需要依賴於ResourceEditor解析,所以註冊Resource類關聯到ResourceEditor解析器 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 更多的初始化可以讓子類去拓展 initBeanWrapper(bw); // 讓spring注入namespace,contextConfigLocation等屬性 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } }

// 讓子類去拓展 initServletBean(); } ```

讀取配置可以從下圖看出,正是初始化了我們web.xml中配置

再看下initServletBean()方法,位於FrameworkServlet類中

```java /* * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. / @Override 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 { // 最重要的是這個方法 this.webApplicationContext = initWebApplicationContext();

// 可以讓子類進一步拓展
initFrameworkServlet();

} catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; }

if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); }

if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } ```

initWebApplicationContext

initWebApplicationContext用來初始化和重新整理WebApplicationContext。

initWebApplicationContext() 方法如下

```java /* * Initialize and publish the WebApplicationContext for this servlet. *

Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * @return the WebApplicationContext instance * @see #FrameworkServlet(WebApplicationContext) * @see #setContextClass * @see #setContextConfigLocation / protected WebApplicationContext initWebApplicationContext() { WebApplicationContext 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 cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } // 沒有在建構函式中初始化,則嘗試通過contextAttribute初始化 if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); }

// 還沒有的話,只能重新建立了 if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); }

if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } }

if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); }

return wac; } ``` webApplicationContext只會初始化一次,依次嘗試建構函式初始化,沒有則通過contextAttribute初始化,仍沒有則建立新的

建立的createWebApplicationContext方法如下

```java /* * Instantiate the WebApplicationContext for this servlet, either a default * {@link org.springframework.web.context.support.XmlWebApplicationContext} * or a {@link #setContextClass custom context class}, if set. *

This implementation expects custom contexts to implement the * {@link org.springframework.web.context.ConfigurableWebApplicationContext} * interface. Can be overridden in subclasses. *

Do not forget to register this servlet instance as application listener on the * created context (for triggering its {@link #onRefresh callback}, and to call * {@link org.springframework.context.ConfigurableApplicationContext#refresh()} * before returning the context instance. * @param parent the parent ApplicationContext to use, or {@code null} if none * @return the WebApplicationContext for this servlet * @see org.springframework.web.context.support.XmlWebApplicationContext / protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); }

// 通過反射方式初始化 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

wac.setEnvironment(getEnvironment()); wac.setParent(parent); String configLocation = getContextConfigLocation(); // 就是前面Demo中的springmvc.xml if (configLocation != null) { wac.setConfigLocation(configLocation); }

// 初始化Spring環境 configureAndRefreshWebApplicationContext(wac);

return wac; } ```

configureAndRefreshWebApplicationContext方法初始化設定Spring環境

```java protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { // 設定context ID if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } }

// 設定servletContext, servletConfig, namespace, listener... wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

// The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); }

// 讓子類去拓展 postProcessWebApplicationContext(wac); applyInitializers(wac);

// Spring環境初始化完了,就可以初始化DispatcherServlet處理流程中需要的元件了。 wac.refresh(); } ```

refresh

有了webApplicationContext後,就開始重新整理了(onRefresh()方法),這個方法是FrameworkServlet提供的模板方法,由子類DispatcherServlet來實現的。

java /** * This implementation calls {@link #initStrategies}. */ @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }

重新整理主要是呼叫initStrategies(context)方法對DispatcherServlet中的元件進行初始化,這些元件就是在SpringMVC請求流程中包的主要元件。

```java /* * Initialize the strategy objects that this servlet uses. *

May be overridden in subclasses in order to initialize further strategy objects. / protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context);

// 主要看如下三個方法 initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context);

initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } ```

initHanlderxxx

我們主要看initHandlerXXX相關的方法,它們之間的關係可以看SpringMVC的請求流程:

  1. HandlerMapping是對映處理器
  2. HandlerAdpter是處理介面卡,它用來找到你的Controller中的處理方法
  3. HandlerExceptionResolver是當遇到處理異常時的異常解析器

initHandlerMapping方法如下,無非就是獲取按照優先順序排序後的HanlderMappings, 將來匹配時按照優先順序最高的HanderMapping進行處理。

initHandlerAdapters方法和initHandlerExceptionResolvers方法也是類似的,如果沒有找到,那就構建預設的。

```java /* * Initialize the HandlerAdapters used by this class. *

If no HandlerAdapter beans are defined in the BeanFactory for this namespace, * we default to SimpleControllerHandlerAdapter. / private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null;

if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerAdapter later. } }

// Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. if (this.handlerAdapters == null) { this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerAdapters declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }

/* * Initialize the HandlerExceptionResolver used by this class. *

If no bean is defined with the given name in the BeanFactory for this namespace, * we default to no exception resolver. / private void initHandlerExceptionResolvers(ApplicationContext context) { this.handlerExceptionResolvers = null;

if (this.detectAllHandlerExceptionResolvers) { // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. Map matchingBeans = BeanFactoryUtils .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values()); // We keep HandlerExceptionResolvers in sorted order. AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers); } } else { try { HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); this.handlerExceptionResolvers = Collections.singletonList(her); } catch (NoSuchBeanDefinitionException ex) { // Ignore, no HandlerExceptionResolver is fine too. } }

// Ensure we have at least some HandlerExceptionResolvers, by registering // default HandlerExceptionResolvers if no other resolvers are found. if (this.handlerExceptionResolvers == null) { this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } } ```

最後我們看下初始化的日誌:

bash 21:30:33.163 [RMI TCP Connection(2)-127.0.0.1] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'springmvc-demo' 21:30:38.242 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.context.support.XmlWebApplicationContext - Refreshing WebApplicationContext for namespace 'springmvc-demo-servlet' 21:30:39.256 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/pdai/pdai/www/tech-pdai-spring-demos/011-spring-framework-demo-springmvc/target/011-spring-framework-demo-springmvc-1.0-SNAPSHOT/WEB-INF/classes/tech/pdai/springframework/springmvc/controller/UserController.class] 21:30:39.261 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/pdai/pdai/www/tech-pdai-spring-demos/011-spring-framework-demo-springmvc/target/011-spring-framework-demo-springmvc-1.0-SNAPSHOT/WEB-INF/classes/tech/pdai/springframework/springmvc/dao/UserDaoImpl.class] 21:30:39.274 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/pdai/pdai/www/tech-pdai-spring-demos/011-spring-framework-demo-springmvc/target/011-spring-framework-demo-springmvc-1.0-SNAPSHOT/WEB-INF/classes/tech/pdai/springframework/springmvc/service/UserServiceImpl.class] 21:30:39.546 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 29 bean definitions from class path resource [springmvc.xml] 21:30:39.711 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' 21:30:39.973 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor' 21:30:39.984 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory' 21:30:39.995 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' 21:30:40.003 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' 21:30:40.042 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.ui.context.support.UiApplicationContextUtils - Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.ResourceBundleThemeSource@791af912] 21:30:40.052 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userController' 21:30:40.136 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userServiceImpl' 21:30:40.140 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDaoImpl' 21:30:40.147 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0' 21:30:40.153 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0' 21:30:40.350 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.handler.MappedInterceptor#0' 21:30:40.356 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.format.support.FormattingConversionServiceFactoryBean#0' 21:30:40.741 [RMI TCP Connection(2)-127.0.0.1] DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings - 'org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0' {/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@216c0f1f} 21:30:40.742 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mvcCorsConfigurations' 21:30:40.742 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping' 21:30:40.792 [RMI TCP Connection(2)-127.0.0.1] DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings - 'org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping' {} 21:30:40.792 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter' 21:30:40.793 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter' 21:30:40.794 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'localeResolver' 21:30:40.796 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'themeResolver' 21:30:40.798 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'viewNameTranslator' 21:30:40.799 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'flashMapManager' 21:30:40.805 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mvcContentNegotiationManager' 21:30:40.887 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' 21:30:41.150 [RMI TCP Connection(2)-127.0.0.1] DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings - t.p.s.s.c.UserController: { [/user]}: list(HttpServletRequest,HttpServletResponse) 21:30:41.202 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - 1 mappings in 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' 21:30:41.202 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter' 21:30:41.626 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - ControllerAdvice beans: none 21:30:41.738 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mvcUriComponentsContributor' 21:30:41.786 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - ControllerAdvice beans: none 21:30:41.806 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0' 21:30:41.919 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - ControllerAdvice beans: none 21:30:41.920 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0' 21:30:41.949 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0' 21:30:41.967 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'jspViewResolver' 21:30:44.214 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.DispatcherServlet - Detected AcceptHeaderLocaleResolver 21:30:44.214 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.DispatcherServlet - Detected FixedThemeResolver 21:31:02.141 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@d57bc91 21:31:03.483 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.support.SessionFlashMapManager@2b4e795e 21:44:08.180 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.jndi.JndiTemplate - Looking up JNDI object with name [java:comp/env/spring.liveBeansView.mbeanDomain] 21:44:08.185 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.jndi.JndiLocatorDelegate - Converted JNDI name [java:comp/env/spring.liveBeansView.mbeanDomain] not found - trying original name [spring.liveBeansView.mbeanDomain]. javax.naming.NameNotFoundException: 名稱[spring.liveBeansView.mbeanDomain]未在此上下文中繫結。找不到[spring.liveBeansView.mbeanDomain]。 21:44:08.185 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.jndi.JndiTemplate - Looking up JNDI object with name [spring.liveBeansView.mbeanDomain] 21:44:08.185 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.jndi.JndiPropertySource - JNDI lookup for name [spring.liveBeansView.mbeanDomain] threw NamingException with message: 名稱[spring.liveBeansView.mbeanDomain]未在此上下文中繫結。找不到[spring.liveBeansView.mbeanDomain]。. Returning null. 21:44:08.195 [RMI TCP Connection(2)-127.0.0.1] DEBUG org.springframework.web.servlet.DispatcherServlet - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 21:44:08.195 [RMI TCP Connection(2)-127.0.0.1] INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in 815032 ms

更多文章

首先, 從Spring框架的整體架構和組成對整體框架有個認知。

  • Spring基礎 - Spring和Spring框架組成
  • Spring是什麼?它是怎麼誕生的?有哪些主要的元件和核心功能呢? 本文通過這幾個問題幫助你構築Spring和Spring Framework的整體認知。

其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。

基於Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。

  • Spring基礎 - SpringMVC請求流程和案例
  • 前文我們介紹了Spring框架和Spring框架中最為重要的兩個技術點(IOC和AOP),那我們如何更好的構建上層的應用呢(比如web 應用),這便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技術基礎上,遵循上述Web MVC的規範推出的web開發框架,目的是為了簡化Java棧的web開發。 本文主要介紹SpringMVC的請求流程和基礎案例的編寫和執行。

Spring進階 - IoC,AOP以及SpringMVC的原始碼分析

  • Spring進階 - Spring IOC實現原理詳解之IOC體系結構設計
  • 在對IoC有了初步的認知後,我們開始對IOC的實現原理進行深入理解。本文將幫助你站在設計者的角度去看IOC最頂層的結構設計
  • Spring進階 - Spring IOC實現原理詳解之IOC初始化流程
  • 上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的
  • Spring進階 - Spring IOC實現原理詳解之Bean例項化(生命週期,迴圈依賴等)
  • 上文,我們看了IOC設計要點和設計結構;以及Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的;容器中存放的是Bean的定義即BeanDefinition放到beanDefinitionMap中,本質上是一個ConcurrentHashMap<String, Object>;並且BeanDefinition介面中包含了這個類的Class資訊以及是否是單例等。那麼如何從BeanDefinition中例項化Bean物件呢,這是本文主要研究的內容?
  • Spring進階 - Spring AOP實現原理詳解之切面實現
  • 前文,我們分析了Spring IOC的初始化過程和Bean的生命週期等,而Spring AOP也是基於IOC的Bean載入來實現的。本文主要介紹Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor,為後續交給代理增強實現做準備的過程)。
  • Spring進階 - Spring AOP實現原理詳解之AOP代理
  • 上文我們介紹了Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor)。本文在此基礎上繼續介紹,代理(cglib代理和JDK代理)的實現過程。
  • Spring進階 - Spring AOP實現原理詳解之Cglib代理實現
  • 我們在前文中已經介紹了SpringAOP的切面實現和建立動態代理的過程,那麼動態代理是如何工作的呢?本文主要介紹Cglib動態代理的案例和SpringAOP實現的原理。
  • Spring進階 - Spring AOP實現原理詳解之JDK代理實現
  • 上文我們學習了SpringAOP Cglib動態代理的實現,本文主要是SpringAOP JDK動態代理的案例和實現部分。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet初始化的過程
  • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第一篇:DispatcherServlet的初始化過程的原始碼解析。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet處理請求的過程
  • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第二篇:DispatcherServlet處理請求的過程的原始碼解析。