SpringMvc中容器加載的過程源碼詳解
了解springmvc
springmvc是基于spring的一個web層框架,同樣也是web層框架的有struts,struts2等等,但是struts因為漏洞等問題,被慢慢淘汰了,現(xiàn)在基本都在用springmvc; 相信以前面試的時候總是背了springmvc的執(zhí)行流程:
SpringMVC流程
1、 用戶發(fā)送請求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到請求調(diào)用HandlerMapping處理器映射器。
3、 處理器映射器找到具體的處理器(可以根據(jù)xml配置、注解進(jìn)行查找),生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet。
4、 DispatcherServlet調(diào)用HandlerAdapter處理器適配器。
5、 HandlerAdapter經(jīng)過適配調(diào)用具體的處理器(Controller,后端控制器)。
6、 Controller執(zhí)行完成返回ModelAndView。
7、 HandlerAdapter將controller執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet。
8、 DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。
9、 ViewReslover解析后返回具體View。
10、DispatcherServlet根據(jù)View進(jìn)行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)。
11、 DispatcherServlet響應(yīng)用戶。
雖然背的挺熟,但是沒有看過源碼,印象總不是很深;因為springmvc基本都有用過,所以有些地方就簡述了;
啟動流程
首先,我們看一下在web.xml配置的一些東西:
<!-- Spring監(jiān)聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/config/beans.xml</param-value> </context-param> <!-- Spring MVC servlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
前面一個是配置spring容器的,后面就是配置springmvc的;DispatcherServlet顧名思義,是一個servlet,先看看DispatcherServlet的類圖:
httpServietBean的init方法
關(guān)于servlet就不做過多描述了,servlet入口就是init方法,我們從init方法看,先執(zhí)行g(shù)enericServlet的init然后genericServlet又是執(zhí)行子類httpServietBean的init方法:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. // 對Servlet初始化參數(shù)封裝成PropertyValues對象 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { //將serlvet封裝為BeanWrapper對象 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); // 資源加載器 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); // 設(shè)置一個屬性編輯器 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 執(zhí)行init,(留給子類覆蓋的,dispatcherServlet都沒有覆蓋方法,所以是空的) initBeanWrapper(bw); //設(shè)置配置 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. // 初始化serlvetBean了 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
initServletBean方法
比較簡單:
initWebApplicationContext方法是主要初始化子容器,跟dispatcherservlet的方法:
protected WebApplicationContext initWebApplicationContext() { //獲取web.xml配置的spring容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // web容器,一般開始為空;springboot項目則不為空; 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); } } } // 為空就查找,查找已經(jīng)綁定的上下文,一般也是空 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(); } // 根據(jù)父容器,創(chuàng)建web容器,就是子容器,這里面子容器進(jìn)行了刷新 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. // 刷新上下文,這里主要加載dispatcherservlet的組件 onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. // 將web容器作為servlet上下文屬性進(jìn)行發(fā)布 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
createWebApplicationContext就是創(chuàng)建子容器的主要方法:
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { // 獲取web容器的類型 Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } 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"); } // 反射創(chuàng)建web容器 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); // 設(shè)置環(huán)境 wac.setEnvironment(getEnvironment()); // 設(shè)置父容器 wac.setParent(parent); //設(shè)置配置文件地址 wac.setConfigLocation(getContextConfigLocation()); // 加載配置,刷新容器 configureAndRefreshWebApplicationContext(wac); return wac; }
看一下加載配置和刷新容器的地方:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 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()); } } // 設(shè)置web容器所屬的servletcontext wac.setServletContext(getServletContext()); // 設(shè)置servletconfig wac.setServletConfig(getServletConfig()); // 設(shè)置命名空間 wac.setNamespace(getNamespace()); // 設(shè)置監(jiān)聽器 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()); } // web容器的鉤子方法,跟spring容器的BeanPostProcess差不多 postProcessWebApplicationContext(wac); // 添加一些初始化器,如果沒有設(shè)置就沒有 applyInitializers(wac); // 刷新容器就是走的spring容器刷新 wac.refresh(); }
onRefresh刷新dispatcherservlet九大組件的主要地方
onRefresh在frameworkservlet方法里是空的,在dispatcherservlet里重寫了onRefresh方法,我們看dispatcherservlet里面的:
@Override protected void onRefresh(ApplicationContext context) { //初始化九大組件 initStrategies(context); } protected void initStrategies(ApplicationContext context) { // 初始化文件上傳解析器 initMultipartResolver(context); // 本地化解析 initLocaleResolver(context); // 主題解析器 initThemeResolver(context); // 處理器映射器 保存Url映射關(guān)系 initHandlerMappings(context); // 處理器適配器 initHandlerAdapters(context); // 異常解析器 initHandlerExceptionResolvers(context); // 視圖提取器,從request中獲取viemName initRequestToViewNameTranslator(context); // 視圖解析器 initViewResolvers(context); // 參數(shù)解析器 initFlashMapManager(context); }
到此這篇關(guān)于SpringMvc中容器加載的過程源碼詳解的文章就介紹到這了,更多相關(guān)SpringMvc中容器加載過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解JavaEE使用過濾器實現(xiàn)登錄(用戶自動登錄 安全登錄 取消自動登錄黑用戶禁止登錄)
主要介紹用戶的自動登錄和取消自動登錄,以及實現(xiàn)一天自動登錄或者n天實現(xiàn)自動登錄,當(dāng)用戶ip被加入到黑名單之后,直接利用過濾器返回一個警告頁面。接下來通過本文給大家介紹JavaEE使用過濾器實現(xiàn)登錄的相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧2016-05-05Java后端限制頻繁請求和重復(fù)提交的實現(xiàn)
很多用戶會請求過于頻繁或者是多次重復(fù)提交數(shù)據(jù),本文主要介紹了Java后端限制頻繁請求和重復(fù)提交的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04mybatis plus in使用時傳數(shù)組、集合的注意點說明
這篇文章主要介紹了mybatis plus in使用時傳數(shù)組、集合的注意點說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11詳解MyBatis的getMapper()接口、resultMap標(biāo)簽、Alias別名、 盡量提取sql列、動態(tài)操作
這篇文章主要介紹了詳解MyBatis的getMapper()接口、resultMap標(biāo)簽、Alias別名、 盡量提取sql列、動態(tài)操作的相關(guān)資料,需要的朋友可以參考下2016-08-08