文档章节

springmvc的大体流程理解

j
 java小学生儿
发布于 2016/12/08 12:54
字数 2729
阅读 185
收藏 2

故事是这样开始的。。。。。

        sprng和springmvc是属于两个不同的容器

   Spring的容器属于父容器,springmvc的容器是子容器

父容器不能访问子容器的bean,但是子容器可以访问父容器的bean

因此,不能再spring的配置中扫面所有的包,这样的话,springmvc就扫不到controller了

这样的话,当请求到达时,springmvc没有handler,就无法做出响应。  

      首先来说web.xml中的一个配置

  <listener>        <!--用来设定Listener接口-->  

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>    

        这个配置定义了一个上下文加载监听器,当监听到web程序启动时就会执行该类的方法:

 /**

     * Initialize the root web application context.

     */

    public void contextInitialized(ServletContextEvent event) {

        this.contextLoader.initWebApplicationContext(event.getServletContext());

    }

    可以看到执行了contextLoader的initWebApplicationContext(servletContext)该方法会初始化web的上下文

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

                   //省掉了部分代码

//这个方法的用途主要是用来解决Spring共享环境的,即,如果我们有多个WAR包部署在同一个服务器上,而且这些WAR都共享某一套业务逻辑层。如何共享//一套业务逻辑包配置而不要每个WAR都单独配置,这时我们就可能需要Spring的共享环境了。

            // Determine parent for root web application context, if any.

            ApplicationContext parent = loadParentContext(servletContext);

            // Store context in local instance variable, to guarantee that

            // it is available on ServletContext shutdown.

            this.context = createWebApplicationContext(servletContext, parent);

//将得到的web上下文设为全web域的属性

            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        我们来看下是怎么创建web上下文的

protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {

//通过servlet的初始化参数得到定义的contextclass

Class<?> contextClass = determineContextClass(sc);

//首先判断自定义的或默认的webcontext类是不是ConfigurableWebApplicationContext的子类

if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

}

ConfigurableWebApplicationContext wac =

(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

try {

String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

ObjectUtils.getDisplayString(contextPath));

}

catch (Exception ex) {

throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);

}

//为wac设置相应的属性

wac.setParent(parent);

wac.setServletContext(sc);

//设置contextConfigLocation,这个属性在web.xml中配置

wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));

customizeContext(sc, wac);

//最核心的方法,完成bean的初始化和实例化

wac.refresh();

return wac;

}

 来看下refresh()这个核心的方法

//待更新。。。。。。

到此为止在web.xml中配置的contextConfigLocation的spring.xml已经加载完毕,已经加载完了spring.xml中配置的所有的bean对象。

接下来就是要加载web.xml中的springmvc的内容了

先看一下web.xml的相应配置

<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>

</servlet>

<servlet-mapping>

<servlet-name>springmvc</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

从以上配置可以看到springmvc其实就是一个servlet而已,因为它的配置和普通的servlet一样,唯一不同的,就是servlet-class也就是DispatcherServlet会拦截所有的符合的请求,并执行处理操作,最后返回试图。

首先加载httpServletBean的init()方法
 

@Override

public final void init() throws ServletException {

// Set bean properties from init parameters.

try {

PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));

initBeanWrapper(bw);

bw.setPropertyValues(pvs, true);

}

// Let subclasses do whatever initialization they like.

initServletBean();

}

加载HttpServletBean的initServletBean()方法,它没有写这个方法,于是调用它的子类FrameworkServlet的该方法:

protected final void initServletBean() throws ServletException {

try {

this.webApplicationContext = initWebApplicationContext();

//* This method will be invoked after any bean properties have been set and

      * the WebApplicationContext has been loaded. The default implementation is empt;

      * subclasses may override this method to perform any initialization they require.

initFrameworkServlet();

}

}

主要就是这个两个方法,先看下initWebApplicationContext()

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);

}

}

}

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.

onRefresh(wac);

}

 

if (this.publishContext) {

// Publish the context as a servlet context attribute.

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;

}

这里最核心的还是configureAndRefreshWebApplicationContext(cwac)

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {

// Generate default id...

ServletContext sc = getServletContext();

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +

"." + getServletName());

else {

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());

}

wac.setServletContext(getServletContext());

//设置该servlet的servletConfig对象

wac.setServletConfig(getServletConfig());

wac.setNamespace(getNamespace());

wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

postProcessWebApplicationContext(wac);

applyInitializers(wac);

wac.refresh();

}

就这样springmvc的上下文也解析完了。

DispatcherServlet  负责拦截所有request,并返回试图

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

int interceptorIndex = -1;

try {

ModelAndView mv;

boolean errorView = false;

try {

processedRequest = checkMultipart(request);

// Determine handler for the current request.

//得到这个request,具体来说是url对应的处理类,并将该类以及相应的inteceptors封装在HandlerExecuteChain类中,这个地方使用了策略模式,优点是十分方便的扩展

mappedHandler = getHandler(processedRequest, false)

// Determine handler adapter for the current request.

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.

String method = request.getMethod();

boolean isGet = "GET".equals(method);

// Apply preHandle methods of registered interceptors.

HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();

if (interceptors != null) {

for (int i = 0; i < interceptors.length; i++) {

HandlerInterceptor interceptor = interceptors[i];

if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {

triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);

return;

}

interceptorIndex = i;

}

}

 

// Actually invoke the handler.

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 

// Do we need view name translation?

if (mv != null && !mv.hasView()) {

mv.setViewName(getDefaultViewName(request));

}

 

// Apply postHandle methods of registered interceptors.

if (interceptors != null) {

for (int i = interceptors.length - 1; i >= 0; i--) {

HandlerInterceptor interceptor = interceptors[i];

interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);

}

}

}

finally {

// Clean up any resources used by a multipart request.

if (processedRequest != request) {

cleanupMultipart(processedRequest);

}

}

}

  首先来说下HandlerExecuteChain

mappedHandler = getHandler(processedRequest, false);

//首先这里的实现原理是循环调用springmvc默认的或者是自定义的(优先级高)HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

for (HandlerMapping hm : this.handlerMappings) {

HandlerExecutionChain handler = hm.getHandler(request);

if (handler != null) {

return handler;

}

}

return null;

}

 

最常用的就是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping分别通过配置的
bean的id以及添加的注解获取对应的处理器类,然后将相应的inteceptor添加到HandlerExecuteChain
对象中去,这样的做法可以符合框架设计的开闭原则,保证不会修改代码,只需要添加然后进行相应的配置就好了。
这里使用了策略模式。

接下来是handlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

在这里使用了适配器原则,首先说一下HandlerAdapter,大家想一下,如果没有这个类的话,那我们在执行处理器方法时,是不是需要

使用大量的if...else...而且还不能扩展,所以这里使用了适配器模式,每一个处理器类都会有一个对应的HandlerAdapter,每一个HandlerAdapter类都会有一个Handle方法和supports方法

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

for (HandlerAdapter ha : this.handlerAdapters) {

if (ha.supports(handler)) {

return ha;

}

}

}

可以看到这里会遍历springmvc定义的HandlerAdapter,如果是这个处理器对应的适配器,就返回这个适配器。这样的做法就十分有利于扩展

开始执行处理器方法返回一个ModelAndView对象

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这是的AnnotationMethodHandlerAdapter的handle方法,最后返回一个ModelAndView

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

        //先判断是不是被@SesionAttribute注解了,分别处理

Class<?> clazz = ClassUtils.getUserClass(handler);

Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);

if (annotatedWithSessionAttributes == null) {

annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);

this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);

}

if (annotatedWithSessionAttributes) {

// Always prevent caching in case of session attribute management.

checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);

// Prepare cached set of session attributes names.

}

else {

// Uses configured default cacheSeconds setting.

checkAndPrepare(request, response, true);

}

// Execute invokeHandlerMethod in synchronized block if required.

if (this.synchronizeOnSession) {

HttpSession session = request.getSession(false);

if (session != null) {

Object mutex = WebUtils.getSessionMutex(session);

synchronized (mutex) {

return invokeHandlerMethod(request, response, handler);

}

}

}

return invokeHandlerMethod(request, response, handler);

}

接下来是执行invokeHandlerMethod方法

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

//得到处理器的处理方法

ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);

Method handlerMethod = methodResolver.resolveHandlerMethod(request);

ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);

//得到请求对象

ServletWebRequest webRequest = new ServletWebRequest(request, response);

ExtendedModelMap implicitModel = new BindingAwareModelMap()

//得到处理器方法返回的模型数据

Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel)

//将模型数据和视图信息封装进ModelAndView对象中

ModelAndView mav =

methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

//做一些更新操作

methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);

return mav;

}

首先看下invokeHandlerMethod方法

public final Object invokeHandlerMethod(Method handlerMethod, Object handler,

NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

 

Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);

try {

boolean debug = logger.isDebugEnabled();

//查找@sessionAttribute绑定的参数,如果存在就加入model中,不存在则不处理

for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {

Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);

if (attrValue != null) {

implicitModel.addAttribute(attrName, attrValue);

}

}

//循环处理@modelAttribute绑定的方法

for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {

Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);

//得到这个方法的所有的参数

Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);

//得到@ModelAttribute绑定的数据的名字

String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value(); //如果之前已经得到了这个数据,就执行下一个@ModelAttribute方法,这意味着如果sessionAttribute中已经有了这个数据,就不会再接受这个方法返回的数据

if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {

continue;

}

ReflectionUtils.makeAccessible(attributeMethodToInvoke);

Object attrValue = attributeMethodToInvoke.invoke(handler, args);

if ("".equals(attrName)) {

Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());

attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);

}

//如果sessionAttribute中没有这个数据,则将它放进model中

if (!implicitModel.containsAttribute(attrName)) {

implicitModel.addAttribute(attrName, attrValue);

}

}

//得到处理器方法的所有的参数

Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);

ReflectionUtils.makeAccessible(handlerMethodToInvoke);

return handlerMethodToInvoke.invoke(handler, args);

}

}

接下来看看怎么得到的modelAndView

ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

不论处理器方法返回什么值,最终都会组装成modelAndView对象。

另外在handle执行的前后都会先进行拦截器的处理

if (interceptors != null) {

for (int i = 0; i < interceptors.length; i++) {

HandlerInterceptor interceptor = interceptors[i];

if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {

triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);

return;

}

interceptorIndex = i;

}

}

// Actually invoke the handler.

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// Do we need view name translation?

if (mv != null && !mv.hasView()) {

mv.setViewName(getDefaultViewName(request));

}

// Apply postHandle methods of registered interceptors.

if (interceptors != null) {

for (int i = interceptors.length - 1; i >= 0; i--) {

HandlerInterceptor interceptor = interceptors[i];

interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);

}

}

}

在handle之前会执行inteceptor的preHandle方法,当执行完handle后会执行inteceptor的postHandle方法,最后在执行inteceptor的

afterCompletion方法

springmvc的配置:

<mvc:interceptors>

<!-- XXX会拦截所有的请求 -->

<bean class="cn.zcm.interceptor.XXX"></bean>

<mvc:interceptor>

<!-- AAA会拦截所有的"/user"的请求 -->

<mvc:mapping path="/user"/>

<bean class="cn.zcm.interceptor.AAA"></bean>

</mvc:interceptor>

</mvc:interceptors>

解析并渲染视图

render(mv, processedRequest, response);

首先解析modelAndView得到一个视图

view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);

这里与HandlerAdapter类似,也使用了策略模式,当我们要添加其他视图技术时,只需要在springmvc配置文件中配置就好了,然后就会出现在viewResolvers里面,然后视图解析器就能通过逻辑视图名解析出具体的视图。

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,

HttpServletRequest request) throws Exception {

for (ViewResolver viewResolver : this.viewResolvers) {

View view = viewResolver.resolveViewName(viewName, locale);

if (view != null) {

return view;

}

}

return null;

}

 

最后就只差一步渲染视图了,将模型数据添加到视图中

view.render(mv.getModelInternal(), request, response);

主要是:有许多的具体的视图类

renderMergedOutputModel(mergedModel, request, response);

这些视图类会根据视图的不同利用不同的技术将模型数据进行相应的处理加工,形成自己需要的数据,然后将该视图以重定向或者转发的方式返回给用户,其实也是用的servlet的response.sendDirect和DispatcherServlet.forword();

到此大体流程就结束了。

© 著作权归作者所有

共有 人打赏支持
j
粉丝 0
博文 1
码字总数 2729
作品 0
私信 提问
轻松理解AOP(面向切面编程)

本文主要介绍AOP思想,而不是Spring,Spring在本文只做为理解AOP的工具和例子,所以也不打算介绍Spring的Aspect、Join point、Advice、AOP proxy等概念,那样初学者会很难理解,如果你懂了A...

爱捣鼓
2014/02/26
0
0
spring-boot项目的docker集成化部署

spring-boot项目的docker集成化部署 [toc] 前言 据说流行的微服务和docker一起,更配哦!接下来,使用简单spring-boot项目演示docker的集成化部署的案例,在看过微笑的博客觉得不过瘾,自己动...

weir_will
10/06
0
0
Java程序员从笨鸟到菜鸟之(七十九)细谈Spring(八)spring+hibernate整合基本详解

由于Spring和Hibernate处于不同的层次,Spring关心的是业务逻辑之间的组合关系,Spring提供了对他们的强大的管理能力, 而Hibernate完成了OR的映射,使开发人员不用再去关心SQL语句,直接与对...

长平狐
2012/11/12
67
0
FeignClient源码深度解析

微信公众号:吉姆餐厅ak 学习更多源码知识,欢迎关注。 全文共16984字左右。 概述

方志朋
11/09
0
0
SpringBoot集成Spring Security(7)——认证流程

版权声明:本文版权归Jitwxs所有,欢迎转载,但未经作者同意必须保留原文链接。 https://blog.csdn.net/yuanlaijike/article/details/84703690 文章目录 在前面的六章中,介绍了 Spring Sec...

Jitwxs
12/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据学习有哪几个步骤

目前大数据行业异常火爆,不少人都对大数据充满了兴趣,其中有大部分人都从没接触过大数据,对于应该如何学习大数据一头雾水。大数据学习并不是高深莫测的,虽然它并没有多简单,但是通过努力...

董黎明
20分钟前
4
0
shell习题_3

1:监控httpd的进程;每隔10s检测一次服务器的httpd的进程数,如果大于500则自动重启httpd服务;并检测是否启动成功; 如果没有正常启动还需要再启动一次,最大不成功数超过五次立即发邮件给管理...

芬野de博客
20分钟前
1
0
Android 9.0 优势探讨

我们来谈论一下 Android。尽管 Android 只是一款内核经过修改的 Linux,但经过多年的发展,Android 开发者们(或许包括正在阅读这篇文章的你)已经为这个平台的演变做出了很多值得称道的贡献...

问题终结者
37分钟前
4
0
vue 组件使用中的一些细节点

细节一 基础例子 运行结果: 以上大家都懂,这边就不多说,回到代码里,有时候我们需要 tbody 里面每一行是一个子组件,那我们代码可以怎么写呢?我们可以这样写,定义一个全局组件,如下: ...

peakedness丶
44分钟前
2
0
vue 之 css module的使用方法

动手之前先配置项目,网上很多文章说需要下载css-loader插件,Vue中的vue-loader已经集成了 CSS Modules,因此删掉也能正常运行 在vue.config.js中添加如下配置 `css: {``loaderOptions: ...

前端小攻略
48分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部