前言

年初面试时接触到一道面试题,在聊到SpringMVC时提到了SpringMVC的开发者为何要设计父子容器呢,又或者说是父子容器的设计有什么更实际的作用呢?

         首先要理解对于一个web应用,当其部署在web容器上时,容器会为其提供一个全局上下文环境ServletContext,这个上下文环境将为后续的Spring提供宿主环境。

SpringMVC工作流程

DispatcherServlet上下文继承关系

SpringMVC设计的父子容器

父子容器配置文件

--在web.xml中配置,两个重要的xml:applicationContext.xml和SpringMVC-conf.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applictionContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<servlet-name>dispatcher-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springMVC-conf.xml</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>dispatcher-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

父子容器的设计目的

根据SpringMVC的官方解释,父(根)容器主要包括一些基础脚手架的bean,比如Pool、DataSource、Dao、Service。目的是在不同的Servlet实例之间共享。这些不同的bean可以在子容器中重写。

     而子容器主要包括一些Controller、View等一些web相关的bean。

DispatcherServlet源码分析

既然SpringMVC中同时包含Spring容器和SpringMVC容器,那么这两个容器都是在什么时候初始化呢?

根容器初始化

首先,根容器是通过ServletContext监听器进行创建,默认的监听器为ContextLoaderListener,当web应用启动时,会调用监听器的contextInitialized方法。

      那么根容器的初始化就从ContextLoaderListener类说起吧,,Spring官方对该类的描述是启动监听器去启动和关闭Spring的root WebApplicationContext(翻译的实在有点蹩脚)。

     

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
} public ContextLoaderListener(WebApplicationContext context) {
super(context);
} //===初始化root WebApplicationContext===
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
} @Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
//ContextLoader.java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器时如果发现servlet 容器中已存在根Spring容根器则抛出异常,证明rootWebApplicationContext只能有一个。
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
} try {
//创建webApplicationContext实例
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//配置WebApplicationContext
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
} /**
把生成的webApplicationContext设置成root WebApplicationContext。保存在ServletContext上下文中。
下一步初始化MVC ApplicationContext时需要从ServletContext取出根上下文作为其父上下文。
**/
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
} return this.context;
}
catch (RuntimeException | Error ex) {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}

以上代码主要完成两个功能:创建实例WebApplicationContext实例、把所创建的WebApplicationContext设置为根上下文,也就是设置成为ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

MVC容器初始化

大家知道Servlet生命周期都是从init方法开始,desctory方法结束,由jvm负责垃圾回收。而DispatcherServlet也是一个普通的Servlet,先看一下DispatcherServlet的继承关系图,对整个继承关系有个了解。



   既然说起Servlet,那就从Servlet的初始化(init)方法入手

//HttpServletBean.java
@Override
public final void init() throws ServletException { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
throw ex;
}
} //交给子类重写
initServletBean();
} //FrameworkServlet.java
@Override
protected final void initServletBean() throws ServletException {
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
throw ex;
}
} //FrameworkServlet.java
//初始化MVC容器
protected WebApplicationContext initWebApplicationContext() {
//从ServletContext取出根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null; if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
} //如果还没有webApplicatioinContext,创建webApplicationContext
if (wac == null) {
wac = createWebApplicationContext(rootContext);
} //子类自定义对servlet子上下文后续操作,在DispatcherServlet中实现
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
//执行子类扩展方法onRefresh,在DispatcherServlet内初始化所有web相关组件
onRefresh(wac);
}
} //发布servlet子上下文到ServletContext
if (this.publishContext) {
String attrName = getServletContextAttributeName();
//将servlet子上下文以org.springframework.web.servlet.FrameworkServlet.CONTEXT. + servletName的属性名称注册到ServletContext中
getServletContext().setAttribute(attrName, wac);
} return wac;
} protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
} protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
//获取WebApplicationContext实现类,此处其实就是XmlWebApplicationContext
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");
} //生成XmlWebApplicationContext实例
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment());
//设置根容器为父容器
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
//设置配置文件
wac.setConfigLocation(configLocation);
} //配置webApplicationContext
configureAndRefreshWebApplicationContext(wac); return wac;
} protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
} wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
} postProcessWebApplicationContext(wac);
applyInitializers(wac); //开始处理bean
wac.refresh();
}

上面的关键代码都在FrameworkServlet类中,有几个关键点:取除根上下文,创建子上下文并设置父上下文,完成刷新,把子上下文发布到ServletContext中。 到这里可以说子容器(子上下文)已经创建完成。 并把其他初始化web组件的相关工作交给onRefresh方法完成,由DispatcherServlet来重写onRefresh方法,这就又回到了我们熟悉的initStrategies方法。

web组件初始化

@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
} protected void initStrategies(ApplicationContext context) {
//文件上传解析器
initMultipartResolver(context); //本地化解析器
initLocaleResolver(context); //主题解析器
initThemeResolver(context); //处理器映射器(url和Controller方法的映射)
initHandlerMappings(context); //处理器适配器(实际执行Controller方法)
initHandlerAdapters(context); //处理器异常解析器
initHandlerExceptionResolvers(context); //RequestToViewName解析器
initRequestToViewNameTranslator(context); //视图解析器(视图的匹配和渲染)
initViewResolvers(context); //FlashMap管理者
initFlashMapManager(context);
}

这里我们主要关注一下三个重要组件:HandlerMapping、HandlerAdapter、ViewResolver。分析这3个组件之前,我们先看一下我们的springMVC-conf.xml配置文件,mvc的配置文件中,我们配置了两行代码:

<context:component-scan base-package="com.zhangfei"/>
<mvc:annotation-driven>

第二行代码主要是添加了默认的HandleMapping,ViewResolver,HandleAdapter。我们看看annotation-driven的源码定义,根据spring自定义schema定义,我们找到如下代码,如图所示:



该文件就一行代码:

http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
//MVC所有的标签解析器都定义在此
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}

那么通过分析AnnotationDrivenBeanDefinitionParser类,主要完成以下三大组件的装配工作:

初始化处理器映射器

private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null; //这里detectAllHandlerMappings默认值为true,可以通过配置文件设置为false
if (this.detectAllHandlerMappings) {
//从上下文(包含父上下文)中查找所有HandlerMapping实现类
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//这里只取固定的bean
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) { }
} /***
确保至少有一个HandlerMapping,如果没能找到,注册一个默认的
默认规则在DispatcherServlet.properties中,这里也就是取BeanNameUrlHandlerMapping、RequestMappingHandlerMapping
***/
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}

初始化处理器适配器

private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null; if (this.detectAllHandlerAdapters) {
//从上下文(包括父上下文)中查找所有HandlerAdapter实现类
Map<String, HandlerAdapter> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
//这里取bean名字为handlerAdapter,类型为HandlerAdapter的处理器适配器
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) { }
} /**
如果没找到,则从默认规则里取出指定的三个实现类:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
**/
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}

初始化试图解析器

private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null; if (this.detectAllViewResolvers) {
//从上下文(包括父上下文)中查找所有ViewResolver实现类
Map<String, ViewResolver> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) { }
} /**
如果没找到,则从默认规则里取出指定的实现类:InternalResourceViewResolver
**/
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
}
}

三大组件的初始化最后判断为NULL时都会调用getDefaultStrategies方法,也就是从DispatcherServlet.properties中取出指定默认值。

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className +"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" +className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}

DispatcherServlet请求处理过程

提到请求处理过程,我们再来回顾一下Servlet生命周期,处理请求都放在service方法中处理,那么也从DispatcherServlet的service方法入手。DispatcherServlet继承FrameworkServlet,在FrameworkServlet中重写了service、doGet、doPost、doPut、doDelete方法。

//FrameworkServlet.java
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
} @Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
} @Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
} @Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
} @Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
} protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { long startTime = System.currentTimeMillis();
Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //把新构造的LocaleContext对象和ServletRequestAttributes对象和当前请求线程绑定(后面要解除绑定)
initContextHolders(request, localeContext, requestAttributes); try {
//抽象方法,交给DispatcherServlet方法实现
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
} finally {
//重置LocaleContext和RequestAttributes对象,也就是解除LocaleContext对象和ServletRequestAttributes对象和当前请求线程的绑定
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
//发布ServletRequestHandledEvent事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
//DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
} //在当前request对象中填充4个属性
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
} try {
//主要处理分发请求
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
} protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); //调用handlerMapping获取handlerChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
} //获取支持该handler解析的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
} if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} //使用HandlerAdapter完成handler处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} //视图处理(页面渲染)
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

DispatcherServlet的doDispatch方法概括起来大致就是以下几点:首先根据当前请求路径找到对应的HandlerMethod,一个HandlerMethod和若干个拦截器构造一个HandlerExecutionChain.通过HandlerExecutionChain得到HandlerAdapter对象通过执行HandlerAdapter的handle方法得到ModelAndView对象,调用ModelAndView解析视图,渲染视图,Response结束。

参考

https://juejin.im/post/5cb89dae6fb9a0686b47306d

https://juejin.im/post/5cbc10b46fb9a0689f4c2c22

https://www.cnblogs.com/fangjian0423/p/springMVC-dispatcherServlet.html

SpringMVC 源码解析的更多相关文章

  1. SpringMVC源码解析- HandlerAdapter - ModelFactory(转)

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  2. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  3. SpringMVC源码解析- HandlerAdapter - ModelFactory

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  4. springMVC源码解析--ViewResolverComposite视图解析器集合(二)

    上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...

  5. springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)

    上一篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)中我们已经介绍了参数解析相关的东西,并且也提到了HandlerMethodArgume ...

  6. springmvc源码解析MvcNamespaceHandler之<mvc:view-resolvers>

    说在前面 本次主要介绍springmvc配置解析. springmvc配置解析 本次介绍MvcNamespaceHandler. 进入到这个方法org.springframework.web.serv ...

  7. SpringMVC源码解析

    一:springmvc运行过程: 1. dispatcherServlet 通过 HandlerMapping 找到controller2. controller经过后台逻辑处理得到结果集modela ...

  8. 深入了解SpringMVC源码解析

    Spring MVC源码解析 Spring MVC的使用原理其实是通过配置一个Servlet来接管所有的请求,所有的请求由这个Servlet来进行分发处理. 我们可以从web.xml里面看出这一点 & ...

  9. SpringMVC源码解析- HandlerAdapter初始化

    HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttr ...

  10. springmvc源码解析-初始化

    1.      概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根 ...

随机推荐

  1. 最全面阐述WebDataBinder理解Spring的数据绑定

    每篇一句 不要总问低级的问题,这样的人要么懒,不愿意上网搜索,要么笨,一点独立思考的能力都没有 相关阅读 [小家Spring]聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析) ...

  2. ctrl shift o失效

    是这样的.preference -> general -> keys ,找到Organize Imports ,然后 在 “WHEN”里面 要选择 Editing JAVA SOURCE. ...

  3. RabbitMQ实战(三)-高级特性

    0 相关源码 1 你将学到 如何保证消息百分百投递成功 幂等性 如何避免海量订单生成时消息的重复消费 Confirm确认消息.Return返回消息 自定义消费者 消息的ACK与重回队列 限流 TTL ...

  4. vue2.0 富文本组件(基于wangeditor)

    1. 本组件基于 wangeditor http://www.wangeditor.com/ 如有侵权 请告知, 2. 效果图 3. 依赖安装 package.json 中 devDependenci ...

  5. [译]为任意网格计算tangent空间的基向量

    +BIT祝威+悄悄在此留下版了个权的信息说: [译]为任意网格计算tangent空间的基向量 Computing Tangent Space Basis Vectors for an Arbitrar ...

  6. windows和linux下如何对拍

    对拍是各种计算机考试检查时必备工具,实际上十分强大,只要你的暴力没有写错就没有问题. 对拍的意思:(怎么有点语文课的意思雾) 对:看见'对'就可以知道有两个. 拍:就是把两个程序结果拍在一起,对照(有 ...

  7. 入门训练-1.A+B问题

    问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第一行包括两个整数,由空格分隔,分别表示A.B. 说明:“输入格式”是描述在测试你 ...

  8. JavaScript-基本语法和数据类型

           前奏:在HTML中使用JavaScript 1_推荐src引用外部JavaScript文件,方便管理与维护,标签位置在页面最下面,使浏览器更优先加载页面内容. 2_HTML页面需要有标准 ...

  9. 关于写自定义的SQL接口出现的问题

    1.<if test="   as != ' ' "></if> 与    <if test='   as != " "    ' ...

  10. JS面向对象编程(一):封装

    js是一门基于面向对象编程的语言.      如果我们要把(属性)和(方法)封装成一个对象,甚至要从原型对象生成一个实例,我们应该怎么做呢?  一.生成对象的原始模式            假定把猫看 ...